Lisp has extensive support for
Two expressions are 'equalp':
If two numbers
If two characters are
If two strings are
If the two cars in conses are 'equalp' and the two cdrs in conses are 'equalp'.
If two arrays have the same number of elements and dimensions, and the corresponding elements in all dimensions are 'equalp'.
(defun equalp (expr-1 expr-2)
(or (equal expr-1 expr-2)
(and (numberp expr-1) (numberp expr-2) (= expr-1 expr-2))
(let ((type (type-of expr-1)))
(when (eq type (type-of expr-2))
(case type
(character (char-equal expr-1 expr-2))
(string (string-equal expr-1 expr-2))
(cons (do ((x (first expr-1)
(if (consp expr-1) (first expr-1) expr-1))
(y (first expr-2)
(if (consp expr-2) (first expr-2) expr-2)))
((or (null expr-1)
(null expr-2)
(not (equalp x y)))
(and (null expr-1)
(null expr-2)))
(setq expr-1 (and (consp expr-1) (rest expr-1))
expr-2 (and (consp expr-2) (rest expr-2)))))
(array (let ((end (length expr-1)))
(when (eql end (length expr-2))
(dotimes (index end t)
(and (not (equalp (aref expr-1 index)
(aref expr-2 index)))
(return nil)))))))))))
cons: I used do instead of
recursion because XLISP has only two kilobytes stack size.
Examples:
(equalp 1 1.0) => T (equalp #\a #\A) => T (equalp "Abc" "aBc") => T (equalp '(1 #\a "Abc") '(1.0 #\A "aBc")) => T (equalp #(1 #\a "Abc") #(1.0 #\A "aBc")) => T
Nested expressions only match if the nesting matches:
(equalp '(1 (2 3)) '(1.0 (2.0 3.0)) => T (equalp '(1 (2 3)) '((1.0 2.0) 3.0) => NIL (equalp '((1 2) 3) '((1.0 2.0) 3.0) => T (equalp '((1 2) 3) '(1.0 (2.0 3.0)) => NIL
A character does not match a string with the same character:
(equalp #\a "a") => NIL
The 'variablep' macro tests if a Lisp expression evaluates to a symbol with a valid variable value bound to it in the current global or lexical environment:
(defmacro variablep (expr)
`(and (symbolp ,expr)
(valuep ,expr)))
Depends on valuep, see and, defmacro, symbolp.
The 'functionp' macro tests if a Lisp expression eveluates to a function or a symbol with a valid function value bound to it in the current global or lexical environment:
(defmacro functionp (expr)
`(case (type-of ,expr)
(closure (eq 'lambda (car (get-lambda-expression ,expr))))
(subr t)
(symbol (and (or (lfboundp ,expr) (fboundp ,expr))
(functionp (function ,(if (consp expr) (cadr expr) expr)))))
(t nil)))
Depends on lfboundp, see
and,
cadr,
car,
case,
closure,
defmacro,
eq,
fboundp,
function,
The awkward
(functionp #'car) => T ; subr = built-in function (functionp 'car) => T ; symbol with a function value (functionp #'and) => NIL ; fsubr = built-in special form (functionp "and") => NIL ; string (defun a () nil) => A ; closure = user-defined function (functionp #'a) => T ; closure (functionp 'a) => T ; symbol with a function value (setq b #'a) => A ; function A stored in variable B (fboundp 'b) => NIL ; no function B found (fboundp b) => T ; variable B evaluates to function A (functionp #'(lambda () nil)) => T ; closure (functionp '(lambda () nil)) => NIL ; list (functionp (lambda () nil)) => T ; closure (functionp #'functionp) => NIL ; macro (let ((x nil)) ; lexical variable (functionp x)) => NIL (flet ((y () nil)) ; lexical closure (functionp y)) => T (labels ((z () nil)) ; lexical closure (functionp z)) => T
(defmacro specialp (expr)
`(case (type-of ,expr)
(fsubr t)
(symbol (and (or (lfboundp ,expr) (fboundp ,expr))
(functionp (function ,(if (consp expr) (cadr expr) expr)))))
(t nil)))
(defmacro macrop (expr)
`(case (type-of ,expr)
(closure (eq 'macro (car (get-lambda-expression ,expr))))
(symbol (and (or (lfboundp ,expr) (fboundp ,expr))
(macrop (function ,(if (consp expr) (cadr expr) expr)))))
(t nil)))
The 'subrp' function returns T if the symbol is a build-in function.
(defun subrp (symbol) (eq 'subr (type-of symbol)))
The 'fsubrp' function returns T if the symbol is a build-in special function.
(defun fsubrp (symbol) (eq 'fsubr (type-of symbol)))
The 'closurep' function returns T if the symbol is a user-defined function.
(defun closurep (symbol) (eq 'closure (type-of symbol)))