How does one reduce a list of boolean values in Common Lisp?

后端 未结 3 586
迷失自我
迷失自我 2020-12-16 16:15

Given a list of values, I want to reduce the list to T if all the elements are not NIL, NIL if not. This gives me an error:

(apply #\'and (get-some-list))


        
相关标签:
3条回答
  • 2020-12-16 16:43

    #'and is invalid because and is a macro, not a function.

    You can get around having to define a named function, by using a lambda:

    (reduce (lambda (x y) (and x y)) (get-some-list) :initial-value t)
    

    There's no shortcut like #' though.

    Alternatively you can also use every with the identify function as the predicate.

    0 讨论(0)
  • 2020-12-16 16:51

    You can use the 'sharp-quote' symbol only with ordinary functions.

    Note that only ordinary functions can be quoted with #’. It is an error to
    quote a macro function or special function this way, or to quote a symbol with
    #’ if that symbol does not name a function.
    
    > #’if
    Error: IF is not an ordinary function.
    

    COMMON LISP: A Gentle Introduction to Symbolic Computation, page 202

    0 讨论(0)
  • 2020-12-16 16:54

    You can use the EVERY function:

    (every #'identity '(T T T T T))  ->  T
    

    and

    (every #'identity '(T T T T NIL))  ->  NIL
    

    Probably the most efficient way is using LOOP:

    (loop for element in '(T T T T nil) always element)  ->  NIL
    

    The advantage is that no function calls over the list elements are needed.

    #' is a read macro that expands into FUNCTION during read the expression. So #'and is (FUNCTION AND).

    FUNCTION is described here: http://www.lispworks.com/documentation/HyperSpec/Body/s_fn.htm

    FUNCTION takes a function name or a lambda expression and returns the corresponding function object.

    AND is defined here: http://www.lispworks.com/documentation/HyperSpec/Body/m_and.htm

    It says that AND is a macro, not a function. The consequence is that (FUNCTION AND) does not work, since FUNCTION needs a function and not a macro to return the corresponding function object. As sepp2k describes in his answer, you can create a function using LAMBDA and use the macro AND inside that function. Macros cannot be passed as values and later be called via FUNCALL or APPLY. This works only with functions.

    This solution is written as

    (reduce (lambda (x y) (and x y)) (get-some-list))
    

    LAMBDA is a macro that expands (lambda (...) ...) into (function (lambda (...) ...)).

    So above is really:

    (reduce (function (lambda (x y) (and x y))) (get-some-list))
    

    which can be written as

    (reduce #'(lambda (x y) (and x y)) (get-some-list))
    

    FUNCTION is needed because Common Lisp makes a difference between the namespace for values and functions. REDUCE needs to get the function passed as an argument by value. So we need to retrieve the function from the function namespace -- which is the purpose of FUNCTION. Whenever we want to pass a function object, we need to get it from the function namespace.

    For example in the case of a local function:

    (flet ((my-and (x y) (and x y)))
      #'my-and)
    

    LAMBDA as a convenience macro that expands into (FUNCTION (LAMBDA ...)) has been added during the design of Common Lisp.

    0 讨论(0)
提交回复
热议问题