// EQ vs EQL vs EQUAL vs EQUALP examples from class GCL (GNU Common Lisp) 2.6.14 Fri Jan 13 10:47:56 AM EST 2023 ANSI git: Version_2_6_14 [..skipped..] >(setq x 'foo) FOO >(setq y 'foo) FOO >(eq x y) T >(eq 3.0 3.0) ; EQ not guaranteed for floats. These 3.0 and 3.0 are distinct objects as created by READ, not compared by value NIL >(eql 3.0 3.0) ; EQL knows numbers T >(eq 1 1) ; not guaranteed but depends on implementation T ;; These BIGNUMs are different objects as created by READ, not compared by value >(eq 100000000000000000000000000000000 100000000000000000000000000000000) NIL ;; It works better when one doesn't mistype the function name :) >(read-from-sting "(eq 100000000000000000000000000000000 100000000000000000000000000000000)") Error: Fast links are on: do (si::use-fast-links nil) for debugging Signalled by EVAL. Condition in EVAL [or a callee]: INTERNAL-SIMPLE-UNDEFINED-FUNCTION: Cell error on READ-FROM-STING: Undefined function: Broken at EVAL. Type :H for Help. 1 Return to top level. >>1 Top level. ;; READ-FROM-STRING returns a list and the length of the read part of the string >(read-from-string "(eq 100000000000000000000000000000000 100000000000000000000000000000000)") (EQ 100000000000000000000000000000000 100000000000000000000000000000000) 72 ;; EQL knows numbers: >(eql 100000000000000000000000000000000 100000000000000000000000000000000) T ;; EQL will not compare recursive objects like lists or strings: >(eql "foo" "foo") NIL >(eql (list 1 2 3) (list 1 2 3)) NIL ;; An interlude: let's see if LIST in the 1st list and in 2nd one are EQ >(setq l '(eql (list 1 2 3) (list 1 2 3))) (EQL (LIST 1 2 3) (LIST 1 2 3)) >l (EQL (LIST 1 2 3) (LIST 1 2 3)) >(car (cdr l)) (LIST 1 2 3) >(caadr l) LIST >(eq (caadr l) (caadr l)) ; of course, this is the same LIST, what was I thinking? T ;; This is what we want: the LIST from the first list vs from second one: >(eq (caadr l) (caaddr l)) T >(eql (list 1 2 3) (list 1 2 3)) NIL ;; EQUAL understands recursion: >(equal (list 1 2 3) (list 1 2 3)) T ;; and strings: >(equal "foo" "foo") T ;; and cares about case: >(equal "foo" "FOO") NIL ;; ..but EQUALP doesn't: >(equalp "foo" "FOO") T ;; BTW, GCL symbols are case-less: >(eq 'foo 'FOO) T ;; ---------------------------------------------- ;; COND is better for pattern-matching on object structure than IF: >(cond ((null l) nil) ((consp l) "cons") (t "foo")) "cons" >l (EQL (LIST 1 2 3) (LIST 1 2 3)) >(setq l 100) 100 >(cond ((null l) nil) ((consp l) "cons") (t "foo")) ;; this is the catch-all ELSE, its guard test is T, ;; always succeeds "foo" >t ;; Let's write our own recursive EQUAL (and forget about strings for a moment) > ((defun myequal (x y) (cond ((and (consp x) (consp y)) (and (myequal (car x) (car y)) (myequal (cdr x) (cdr y)))) ((eql x y) t) (t f))) ;; this line has an error. "F" is not a thing, false is NIL MYEQUAL >(myequal 1 1) T >(myequal 1 2) > Error: Fast links are on: do (si::use-fast-links nil) for debugging Signalled by COND. Condition in COND [or a callee]: INTERNAL-SIMPLE-UNBOUND-VARIABLE: Cell error on F: Unbound variable: Broken at COND. Type :H for Help. 1 Return to top level. >>1 >> ;; OK, fix the bug: >(defun myequal (x y) (cond ((and (consp x) (consp y)) (and (myequal (car x) (car y)) (myequal (cdr x) (cdr y)))) ((eql x y) t) (t nil))) MYEQUAL >(myequal 1 2) NIL >(myequal 1 '(1)) NIL >(myequal (list 1 2) (list 1 2)) T >(myequal (list 1 2) (list 1 3)) > NIL >(myequal '(1 2) '((1) 2)) > NIL ;; We declare victory. In real life, we'd want an inductive proof of MYEQUAL's ;; formal specification, of course. Come to my Winter'27 class to find out ;; how to build it (maybe with some help from LLMs :)) ;; Let's practice more recursive thinking: > (defun app-elt (e l) (cond ((null l) (cons e nil)) ((consp l) (cons (car l) (app-elt e (cdr l)))) (t (error "I needs a list for my 2nd argument")))) APP-ELT >(app-elt 1 nil) (1) >(app-elt 2 '(1)) (1 2) >(app-elt 3 '(1 2)) (1 2 3) ;;; Now write MYAPPEND that takes two lists and appends the second one to the first one!