球拍:为什么不能使用“位图”功能?

时间:2018-07-14 01:51:27

标签: scheme lisp racket eval apply

已被警告不要在代码中使用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]

3 个答案:

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

applybitmap不能一起使用,因为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))