问题
(define (cart . lists)
(cond ((null? lists) '())
((= (length lists) 1) (map list (first lists)))
(else
(append-map (lambda(x)
(map (lambda(y) (cons y x))
(first lists)))
(apply cart (rest lists))))))
(define (numbers n)
(define (reversed-numbers n)
(if (= n 0)
'()
`(,n . ,(reversed-numbers (- n 1)))))
(reverse (reversed-numbers n)))
(define (gen-truth n vals)
(apply cart (map (lambda(x) list vals) (numbers n))))
(gen-truth 2 '(#t #f)) returns: '((#t #t) (#f #t) (#t #f) (#f #f))
(define and-l (lambda x
(if (null? x)
#t
(if (car x) (apply and-l (cdr x)) #f))))
why does: (apply map and-l (gen-truth 2 '(#t #f)))
return '(#f #f)
? i would expect it return a Boolean for each of the sub expressions containing pairs of Boolean values.
回答1:
A Better and-l
Procedure
The problem is that the and-l
procedure is operating in a way that probably was not anticipated. Here, x
is put into a list before being passed along to the body of the procedure. Consider:
scratch.rkt> (define bad-proc (lambda x x))
scratch.rkt> (bad-proc '(#t #f))
'((#t #f))
One would probably expect the and-l
procedure to test whether a list contains all true values, and the one passed to the body of the procedure does:
scratch.rkt> (and-l-bad '(#t #f))
#t
scratch.rkt> (and-l-bad '(#f #f))
#t
scratch.rkt> (and-l-bad '(#t #t))
#t
Here is a correct version of and-l
; note that there is no need for apply
:
(define and-l
(lambda (x)
(if (null? x)
#t
(and (car x) (and-l (cdr x))))))
Testing the new procedure:
scratch.rkt> (and-l '(#t #f))
#f
scratch.rkt> (and-l '(#f #f))
#f
scratch.rkt> (and-l '(#t #t))
#t
Now that and-l
is working as it should, we turn our attention to:
(apply map and-l (gen-truth 2 '(#t #f)))
Again, there is no need for apply
here. It does not even make sense here to do (apply map and-l ;....)
. The apply
procedure takes a procedure and a list as arguments, and uses the elements of the list as arguments to the procedure. So, (apply + '(1 2 3 4))
is equivalent to (+ 1 2 3 4)
. This capability is not needed in the current context; all that is needed is map
to apply and-l
to each list of booleans in the list returned by gen-truth
:
scratch.rkt> (gen-truth 2 '(#t #f))
'((#t #t) (#f #t) (#t #f) (#f #f))
scratch.rkt> (map and-l (gen-truth 2 '(#t #f)))
'(#t #f #f #f)
Salvaging the Original and-l
Procedure
Note that and-l-bad
works not on a list of arguments, but on an unspecified number of arguments which are wrapped in a list and passed to the procedure body:
scratch.rkt> (and-l-bad #t #f #f)
#f
scratch.rkt> (and-l-bad #t #t #t)
#t
scratch.rkt> (and-l-bad #f #f #t)
#f
With this in mind, OP goal can be achieved without rewriting and-l-bad
to and-l
as above. Instead, rethink (apply map and-l (gen-truth 2 '(#t #f)))
:
scratch.rkt> (map (lambda (x) (apply and-l-bad x)) (gen-truth 2 '(#t #f)))
'(#t #f #f #f)
Here, map
applies a function to each boolean list in the list returned by gen-truth
. That function takes a boolean list and applies apply
with and-l-bad
to it, so that,e.g. (apply and-l-bad '(#t #f)
--> (and-l-bad #t #f)
, which is exactly what and-l-bad
is expecting.
and-proc
The procedure and-l-bad
is really like and
, in that it takes an unspecified number of arguments and returns the result of combining the arguments with and
:
scratch.rkt> (and-l-bad #t #t #f #t)
#f
scratch.rkt> (and #t #t #f #t)
#f
But there is an important difference: and-l-bad
is a procedure, while and
is a special form. Procedures always evaluate all of their arguments, but special forms have special evaluation rules. The special form and
evaluates its arguments sequentially until the result is known, either returning the first #f
encountered in that sequence, or the last value of the sequence. This is short-circuit evaluation.
A better name for and-l-bad
would perhaps be and-proc
. The original name, and-l
suggested that and
was applied to a list (which was not true), while and-proc
emphasizes that this is a procedure which behaves similarly to and
.
Why would you need something like and-proc
in the first place? Well, apply
takes a procedure for its first argument, so (map (lambda (x) (apply and x)) (gen-truth 2 '(#t #f)))
will not work. But this will work:
scratch.rkt> (map (lambda (x) (apply and-proc x)) (gen-truth 2 '(#t #f)))
'(#t #f #f #f)
Here we have used an and
-like procedure where and
itself would not work. The downside of and-proc
is that it always evaluates all of its arguments; this is not always desirable.
How to Avoid Writing a New and
-like Procedure
Writing a new procedure to handle using and
here could have been avoided altogether:
scratch.rkt> (map (lambda (x) (andmap identity x)) (gen-truth 2 '(#t #f)))
'(#t #f #f #f)
Here (lambda (x) (andmap identity x))
is a procedure that takes its arguments and combines them with and
. That is, ((lambda (x) (andmap identity x)) '(#t #f))
is equivalent to (and (identity #t) (identity #f))
. This procedure is mapped over the list of boolean lists as before.
This is equivalent to the and-l
defined at the top of this answer, and and-l
could in fact be better-defined as:
(define (and-l xs)
(andmap identity xs))
来源:https://stackoverflow.com/questions/61334629/why-does-apply-map-and-to-list-of-expressions-returns-only-one-boolean