Nyquist / XLISP 2.0  -  Contents | Tutorials | Examples | Reference

Predicates and Comparison


Lisp has extensive support for run-time tests.

Generalized Lisp Functions - slower than the build-ins, but no errors

  1. Built-in XLISP Functions
  2. Generalized Comparison - one or more arguments
  3. Symbol Predicates - one argument
  4. Function Predicates - one argument
  5. Character Predicates - one argument

  Back to top


Built-in XLISP Functions


  1. Boolean Predicates - one argument [all types]
  2. Generalized Comparison - two arguments [all types]
  3. Type Predicates - one argument [all types]

  Back to top


equalp


Two expressions 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. The (consp expr) tests are necessary because in a dotted list the last rest element is not a cons.

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

  Back to top


variablep


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.

  Back to top


functionp


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, get-lambda-expression, lambda, nil, or, subr, symbol,  t , type-of.

The awkward (function ,(if (consp expr) (cadr expr) expr)) construct is necessary because the function special form needs a pre-evaluated argument, what must be done at macro-expansion time, so an additional consp test is needed if the 'expr' argument is a list at all, otherwise cadr will produce an error.

Examples:
(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

  Back to top


specialp


(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)))

  Back to top


macrop


(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)))

  Back to top


subrp


The 'subrp' function returns T if the symbol is a build-in function.

(defun subrp (symbol)
  (eq 'subr (type-of symbol))) 

  Back to top


fsubrp


The 'fsubrp' function returns T if the symbol is a build-in special function.

(defun fsubrp (symbol)
  (eq 'fsubr (type-of symbol))) 

  Back to top


closurep


The 'closurep' function returns T if the symbol is a user-defined function.

(defun closurep (symbol)
  (eq 'closure (type-of symbol)))

  Back to top


Nyquist / XLISP 2.0  -  Contents | Tutorials | Examples | Reference