Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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

As does this:

(reduce #'and (get-some-list))

This is the best I've come up with:

[11]> (defun my-and (x y) (and x y))
MY-AND

[12]> (reduce #'my-and '(T T T T T))
T

[13]> (reduce #'my-and '(T T T T NIL))
NIL

Why is "#'and" invalid? Is there a more idiomatic way to do this in Common Lisp?

like image 206
postfuturist Avatar asked Dec 26 '10 20:12

postfuturist


People also ask

What is reduce in Lisp?

reduce uses a binary operation, function, to combine the elements of sequence bounded by start and end. The function must accept as arguments two elements of sequence or the results from combining those elements. The function must also be able to accept no arguments.

What does list do in Lisp?

The list function is rather used for creating lists in LISP. The list function can take any number of arguments and as it is a function, it evaluates its arguments. The first and rest functions give the first element and the rest part of a list.

Is there and in Lisp?

Common Lisp provides three operators on Boolean values: and, or, and not. Of these, and and or are also control structures because their arguments are evaluated conditionally. The function not necessarily examines its single argument, and so is a simple function.

What is true in Lisp?

Lisp includes a number of functions that give a true or false answer - they are called predicates, and often (but not always) have names ending with p. In Lisp the convention is that nil, or the empty list, means false, and anything else means true. If you want to represent true you use the special Lisp operator, t.


2 Answers

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.

like image 176
Rainer Joswig Avatar answered Oct 08 '22 23:10

Rainer Joswig


#'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.

like image 29
sepp2k Avatar answered Oct 09 '22 00:10

sepp2k