已被警告不要在代码中使用eval
。但是,在这段球拍代码中,我可以使eval
正常工作,但不推荐使用类似apply
的工具。这是代码:
(require 2htdp/image)
(define (get_img filename)
(let ([img (eval `(bitmap ,filename))])
(image->color-list mask)
))
我尝试用apply天真地替换eval:
(require 2htdp/image)
(define (get_img filename)
(let ([img (apply `(bitmap ,filename))])
(image->color-list mask)
))
当我运行它时,我得到:
; apply: arity mismatch;
; the expected number of arguments does not match the given number
; expected: at least 2
; given: 1
; [,bt for context]
我已经尝试了此代码的一些排列,但无济于事。我很希望这个
(let ([img (apply bitmap `(filename))])
(image->color-list img)`)
可以工作,但显然还有一些我不理解的地方
我尝试的第一件事,并显示错误消息:
> (require 2htdp/image)
> (define (get_img filename)
(let ([img (bitmap filename)])
(image->color-list img)))
; readline-input:6:15: bitmap: expected a path with a / in it
; in: (bitmap filename)
; [,bt for context]
另一个失败的尝试:
> (define (get_img filename)
(let ([img (apply bitmap (list filename))])
(image->color-list mask)))
; readline-input:16:20: bitmap: bad syntax
; in: bitmap
; [,bt for context]
答案 0 :(得分:3)
您使用错了。因此,在变量+
后面有一个可以应用的过程对象。这些是相等的:
(+ (* 2 3) 5) ; ==> 11
(apply + (list (* 2 3) 5)) ; ==> 11
(apply + `(,(* 2 3) 5)) ; ==> 11
在您的示例中,您使用的是bitmap
,它根本不是一个过程,而是一个宏,并且似乎是要从球拍包中获取位图,并使用字符串,它期望至少有一个斜杠,因为图像应该不在软件包根目录中。您应该将其替换为bitmap/file
,这是一个过程,它采用绝对路径或相对于当前工作目录的文件路径。
在您的示例(apply `(bitmap/file ,filename)
中,您传递的是将列表用作第一个参数,而不是过程对象和带有参数的最后一个参数。
在您的示例(apply bitmap/file `(filename))
中,您要应用bitmap
带有带有符号filename
的文字列表,该列表与具有相同名称的变量无关。您是如此亲密,因为我认为您想要(apply bitmap/file `(,filename))
,这是做(apply bitmap/file (list filename))
的有趣方式。我不明白的是,为什么你不能这样做:
(define (get_img filename)
(let ([img (bitmap/file filename)])
(image->color-list mask)))
答案 1 :(得分:1)
apply
和bitmap
不能一起使用,因为bitmap
不是一个函数。请注意,bitmap
文档中的条目显示“语法”而不是“过程”。
如果f
是一个函数,则(apply f (list a b c))
将计算(f a b c)
。
但是,bitmap
不是函数,它是“特殊形式”。
不过,您很幸运,因为bitmap/file
是一个函数,因此您可以改用它。
答案 2 :(得分:1)
我认为值得了解apply
在球拍或其他Lisp-1中有用的地方。在几乎所有代码中,当您有一个函数和一堆参数但又不知道有多少个参数,并且想用这些参数调用函数时,它很有用。实际上,这意味着:
如果您知道有多少个参数,那么几乎没有理由使用apply
,除非这些参数已经在列表中并且您不想打扰提取它们:(apply cons l)
可能比(cons (first l) (second l))
说起来容易。
尤其是如果您尝试使用已知的单个参数调用函数,那么apply
绝对不是您想要的。同样,如果“函数”是宏,它也永远不是您想要的。
在Lisp-2s中,apply
还有其他用途:调用一个函数,该函数是变量的值。这是必需的,因为作为愚蠢的示例,(let ((x (lambda (...) ...))) ... (x ...) ...)
在Lisp-2中不起作用。 Lisp-2还有一个附加功能funcall
,当您知道自变量数量时,它会执行apply
的作用:您不需要funcall
Lisp-1。
eval
有用的情况更为罕见。有一些,但几乎始终都是充其量,最糟糕的是可怕的安全问题:
(define (terror x) (eval `(list x)))
何时调用它?答案:该语言能够做的所有事情:(terror '(launch-the-nukes))
。