为什么 apply map and to list of expressions returns 只有一个布尔值?
why does apply map and to list of expressions returns only one boolean?
(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))))
为什么:(apply map and-l (gen-truth 2 '(#t #f)))
return '(#f #f)
?我希望它 return 每个包含布尔值对的子表达式的布尔值。
更好的and-l
程序
问题是 and-l
过程以一种可能未预料到的方式运行。在这里,x
在传递到过程主体之前被放入列表中。考虑:
scratch.rkt> (define bad-proc (lambda x x))
scratch.rkt> (bad-proc '(#t #f))
'((#t #f))
人们可能希望 and-l
过程测试一个列表是否包含所有真值,而传递给过程主体的那个确实:
scratch.rkt> (and-l-bad '(#t #f))
#t
scratch.rkt> (and-l-bad '(#f #f))
#t
scratch.rkt> (and-l-bad '(#t #t))
#t
这是 and-l
的正确版本;请注意,不需要 apply
:
(define and-l
(lambda (x)
(if (null? x)
#t
(and (car x) (and-l (cdr x))))))
正在测试新程序:
scratch.rkt> (and-l '(#t #f))
#f
scratch.rkt> (and-l '(#f #f))
#f
scratch.rkt> (and-l '(#t #t))
#t
现在 and-l
正在正常工作,我们将注意力转向:
(apply map and-l (gen-truth 2 '(#t #f)))
同样,这里不需要 apply
。在这里做 (apply map and-l ;....)
甚至没有意义。 apply
过程将过程和列表作为参数,并使用列表的元素作为过程的参数。所以,(apply + '(1 2 3 4))
等价于 (+ 1 2 3 4)
。当前上下文中不需要此功能;所需要的只是 map
将 and-l
应用于 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)
挽救原始and-l
程序
请注意,and-l-bad
不适用于参数列表,但适用于包装在列表中并传递给过程主体的未指定数量的参数:
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
考虑到这一点,无需像上面那样将 and-l-bad
重写为 and-l
即可实现 OP 目标。相反,重新考虑 (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)
此处,map
将函数应用于 gen-truth
返回的列表中的每个布尔列表。该函数采用布尔列表并将 apply
和 and-l-bad
应用于它,例如(apply and-l-bad '(#t #f)
--> (and-l-bad #t #f)
,这正是 and-l-bad
所期望的。
and-proc
过程 and-l-bad
与 and
非常相似,因为它采用未指定数量的参数和 returns 参数与 and
组合的结果:
scratch.rkt> (and-l-bad #t #t #f #t)
#f
scratch.rkt> (and #t #t #f #t)
#f
但有一个重要的区别:and-l-bad
是一个过程,而and
是一个特殊形式.过程总是对其参数的 all 求值,但特殊形式有特殊的求值规则。特殊形式 and
按顺序计算其参数,直到结果已知,返回该序列中遇到的第一个 #f
或序列的最后一个值。这是短路评估。
and-l-bad
更好的名称可能是 and-proc
。原名 and-l
暗示 and
应用于列表(这不是真的),而 and-proc
强调这是一个 过程 其行为类似于 and
.
为什么你首先需要像 and-proc
这样的东西?好吧,apply
为其第一个参数采用一个过程,因此 (map (lambda (x) (apply and x)) (gen-truth 2 '(#t #f)))
将不起作用。但这会起作用:
scratch.rkt> (map (lambda (x) (apply and-proc x)) (gen-truth 2 '(#t #f)))
'(#t #f #f #f)
这里我们使用了类似 and
的 过程 ,其中 and
本身不起作用。 and-proc
的缺点是它总是评估其参数的 all;这并不总是可取的。
如何避免编写类似 and
的新过程
在这里完全可以避免使用 and
编写新程序来处理:
scratch.rkt> (map (lambda (x) (andmap identity x)) (gen-truth 2 '(#t #f)))
'(#t #f #f #f)
这里 (lambda (x) (andmap identity x))
是一个过程,它接受它的参数并将它们与 and
结合起来。也就是说,((lambda (x) (andmap identity x)) '(#t #f))
等同于 (and (identity #t) (identity #f))
。此过程像以前一样映射到布尔列表列表上。
这等同于此答案顶部定义的 and-l
,而 and-l
实际上可以更好地定义为:
(define (and-l xs)
(andmap identity xs))
(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))))
为什么:(apply map and-l (gen-truth 2 '(#t #f)))
return '(#f #f)
?我希望它 return 每个包含布尔值对的子表达式的布尔值。
更好的and-l
程序
问题是 and-l
过程以一种可能未预料到的方式运行。在这里,x
在传递到过程主体之前被放入列表中。考虑:
scratch.rkt> (define bad-proc (lambda x x))
scratch.rkt> (bad-proc '(#t #f))
'((#t #f))
人们可能希望 and-l
过程测试一个列表是否包含所有真值,而传递给过程主体的那个确实:
scratch.rkt> (and-l-bad '(#t #f))
#t
scratch.rkt> (and-l-bad '(#f #f))
#t
scratch.rkt> (and-l-bad '(#t #t))
#t
这是 and-l
的正确版本;请注意,不需要 apply
:
(define and-l
(lambda (x)
(if (null? x)
#t
(and (car x) (and-l (cdr x))))))
正在测试新程序:
scratch.rkt> (and-l '(#t #f))
#f
scratch.rkt> (and-l '(#f #f))
#f
scratch.rkt> (and-l '(#t #t))
#t
现在 and-l
正在正常工作,我们将注意力转向:
(apply map and-l (gen-truth 2 '(#t #f)))
同样,这里不需要 apply
。在这里做 (apply map and-l ;....)
甚至没有意义。 apply
过程将过程和列表作为参数,并使用列表的元素作为过程的参数。所以,(apply + '(1 2 3 4))
等价于 (+ 1 2 3 4)
。当前上下文中不需要此功能;所需要的只是 map
将 and-l
应用于 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)
挽救原始and-l
程序
请注意,and-l-bad
不适用于参数列表,但适用于包装在列表中并传递给过程主体的未指定数量的参数:
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
考虑到这一点,无需像上面那样将 and-l-bad
重写为 and-l
即可实现 OP 目标。相反,重新考虑 (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)
此处,map
将函数应用于 gen-truth
返回的列表中的每个布尔列表。该函数采用布尔列表并将 apply
和 and-l-bad
应用于它,例如(apply and-l-bad '(#t #f)
--> (and-l-bad #t #f)
,这正是 and-l-bad
所期望的。
and-proc
过程 and-l-bad
与 and
非常相似,因为它采用未指定数量的参数和 returns 参数与 and
组合的结果:
scratch.rkt> (and-l-bad #t #t #f #t)
#f
scratch.rkt> (and #t #t #f #t)
#f
但有一个重要的区别:and-l-bad
是一个过程,而and
是一个特殊形式.过程总是对其参数的 all 求值,但特殊形式有特殊的求值规则。特殊形式 and
按顺序计算其参数,直到结果已知,返回该序列中遇到的第一个 #f
或序列的最后一个值。这是短路评估。
and-l-bad
更好的名称可能是 and-proc
。原名 and-l
暗示 and
应用于列表(这不是真的),而 and-proc
强调这是一个 过程 其行为类似于 and
.
为什么你首先需要像 and-proc
这样的东西?好吧,apply
为其第一个参数采用一个过程,因此 (map (lambda (x) (apply and x)) (gen-truth 2 '(#t #f)))
将不起作用。但这会起作用:
scratch.rkt> (map (lambda (x) (apply and-proc x)) (gen-truth 2 '(#t #f)))
'(#t #f #f #f)
这里我们使用了类似 and
的 过程 ,其中 and
本身不起作用。 and-proc
的缺点是它总是评估其参数的 all;这并不总是可取的。
如何避免编写类似 and
的新过程
在这里完全可以避免使用 and
编写新程序来处理:
scratch.rkt> (map (lambda (x) (andmap identity x)) (gen-truth 2 '(#t #f)))
'(#t #f #f #f)
这里 (lambda (x) (andmap identity x))
是一个过程,它接受它的参数并将它们与 and
结合起来。也就是说,((lambda (x) (andmap identity x)) '(#t #f))
等同于 (and (identity #t) (identity #f))
。此过程像以前一样映射到布尔列表列表上。
这等同于此答案顶部定义的 and-l
,而 and-l
实际上可以更好地定义为:
(define (and-l xs)
(andmap identity xs))