在Scheme中使用apply函数与AND结合使用

19
为什么以下代码无法正常工作?
(apply and (list #t #t #f))

虽然以下内容运行良好。
(apply + (list 1 3 2))

这在R5RS和R6RS中似乎都是这种情况?

9个回答

14

and 不是普通函数,因为它只会评估所需的最少参数,以确定结果是 true 还是 false。例如,如果第一个参数是 false,则无论其他参数是什么,结果都必须为 false,因此它不会评估其他参数。如果 and 是普通函数,那么它的所有参数都将首先被评估,因此 and 被作为特殊关键字制定,这就是为什么它不能作为变量传递的原因。


7
这是我对Scheme最大的不满之一——宏应该是头等公民。我知道这可能会妨碍一些编译优化,但它肯定会增加语言的表达能力。 - Kyle Cronin
3
Kyle,你可能在谈论一种叫做“内核”的语言。内核旨在解决第二类特殊形式的问题,引入其他抽象原语以提供更灵活的求值语义。(正如你所说,编译优化可能是一个问题。)这个问题中的例子,“and”,甚至是klisp实现主页上展示的“Hello World”示例。 - Rich Dougherty

6
(define and-l (lambda x 
    (if (null? x)
        #t
        (if (car x) (apply and-l (cdr x)) #f))))

请注意,这是 lambda 的可变参数形式!示例应用:(and-l #t #t #f) 或者您可以使用 apply 过程(按照要求),例如:(apply and-l (list #t #t #f)) 两个选项都可以...

5
"and"实际上是一个宏,其定义在R5RS第4章中概述。该页面上的“库语法”表示它实现为宏。 第7.3节,派生表达式类型给出了and宏的可能定义:
(define-syntax and
  (syntax-rules ()
    ((and) #t)
    ((and test) test)
    ((and test1 test2 ...)
     (if test1 (and test2 ...) #f))))

鉴于这个定义,不可能将and作为函数参数传递给apply

3
在Scheme方言MIT/GNU Scheme中,您可以使用函数boolean/and代替特殊形式and
(apply boolean/and (list #t #t #f)) ;Value: #f

顺便说一下,我在Guile Scheme过程索引中找不到任何等效的函数。

(其他答案已经解释了为什么特殊形式and不起作用,并展示了如何编写自己的替换函数,如果在你的方言中没有这样的函数。)


1
如果你真的想要有一个指向进行 and 操作的函数的函数指针,并且不介意与“真正”的 and 行为有所不同,那么这将起作用:
(define and-l (lambda (a b) (and a b)))

你可以像这样应用:

(apply and-l (list #t #f))

这两个注意事项是:

  1. 所有的参数都会被评估,违反了and的定义,它应该具有快捷行为。
  2. 只允许两个参数。

2
(define (and/f . xs) (cond ((null? xs) #t) ((null? (cdr xs)) (car xs)) (else (and (car xs) (apply and/f (cdr xs)))))) - Zorf

1
我遇到了同样的问题,并在Racket中找到了一个优雅的解决方案。 由于问题在于“and”是一个宏而不是函数,为了防止评估其所有参数,我阅读了一些关于“lazy racket”的内容,并发现“and”是该语言中的一个函数。因此,我想出了以下解决方案,只需将lazy and导入为“lazy-and”:
#lang racket
(require (only-in lazy [and lazy-and]))

(define (mm)
  (map number? '(1 2 3)))

(printf "~a -> ~a\n" (mm) (apply lazy-and (mm)))

这会产生

(#t #t #t) -> #t

0

你也可以使用

(define (andApply lBoo) (if (not (car lBoo)) #f (if (= 1(length lBoo)) (car lBoo) (andApply (cdr lBoo)))))


0

试试这个:

(define list-and (lambda (args) (and (car args) (list-and (cdr args)))))

然后你可以使用apply到列表中!


2
这不会按预期工作。请尝试使用以下代码:(define(list-and args)(if(null?args)#t(and(car args)(list-and(cdr args))))) - Stefan Kangas

-1

我在使用PLT-Scheme 372时也遇到了这个问题,我深入研究了and-syntax的行为,并找出了下面的代码,它的工作方式就像一个人直觉地期望(apply and lst)返回的那样,但我还没有进行详尽的测试。

(define (list-and lst) 
  (cond 
    ((null? lst) '())
    ((not (pair? lst)) (and lst)) 
    ((eq? (length lst) 1) (car lst))
    (else
     (and (car lst)
          (list-and (cdr lst))))
    )
  )

Welcome to DrScheme, version 372 [3m].
Language: Textual (MzScheme, includes R5RS).

> (eq? (and '()) (list-and '()))
#t
> (eq? (and '#f) (list-and (list '#f)))
#t
> (eq? (and 'a) (list-and (list 'a)))
#t
> (eq? (and 'a 'b) (list-and (list 'a 'b)))
#t
> (eq? (and 'a 'b '()) (list-and (list 'a 'b '())))
#t
> (eq? (and 'a 'b '#t) (list-and (list 'a 'b '#t)))
#t
> (eq? (and 'a 'b '#f) (list-and (list 'a 'b '#f)))
#t

我还想出了另一个“陷入思维定势”的解决方法。我称之为“陷阱”是因为一开始我不知道如何将其转化为一个函数... 这里是我的直觉想法的演示:
Welcome to DrScheme, version 372 [3m].
Language: Textual (MzScheme, includes R5RS).
> (eval (cons 'and (list ''#f ''#f ''#t)))
#f
> (eval (cons 'and (list ''a ''b ''c)))
c

但后来我提出了一个问题,并在这里得到了答案:是否可能动态生成(quote(quote var))或''var? 有了这个答案,人们可以轻松地将上述想法转化为一个函数。
(define (my-quote lst)
  (map (lambda (x) `'',x) lst))

(cons 'and (my-quote (list 'a 'b 'c)))
=> '(and ''a ''b ''c)

1
在编程中,使用 eval 这种方法过于复杂并且不被推荐。在 Racket 中,你可以使用 andmap 来实现这个功能:(andmap values '(a b c)) => c。(在 Racket 中,你也可以使用 identity 替代 values。) - C. K. Young
感谢您分享andmapidentity...您的意思是"eval..."对于像将"and"映射到"list"这样的简单任务来说太过强大,一旦出现问题,我就无法控制它了吗?(无论如何,我会记住您的建议!) - pimgeek
1
“eval” 的滥用非常普遍,如果您没有正确引用所有内容,它可能会成为安全问题。对于您在此处执行的操作来说,它也太重了。对我来说,“eval”的主要用途是运行用户提供的代码(伴随着运行用户提供的代码的所有安全问题),例如在 REPL 中。 - C. K. Young
听起来 eval 超出了我的理解范围,至少现在是这样。我会避免为了好玩而使用它...除非我对它感到自信。 :) - pimgeek

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接