对于在Racket中给出`#<void>`的函数列表,`for-each`

时间:2016-05-20 22:53:16

标签: foreach scheme racket

我正在玩Racket v6.5中的一些声明性图形内容。为此,我定义了一个执行函数列表的宏。然后在绘图回调中使用它。

#lang racket/gui

(define-syntax-rule (execute-functions flist arg)
  (for-each
   (lambda (function)
     [(function arg)])
   flist))

(define-syntax-rule
  (text str (at x y))
  (lambda (dc)
    (send dc set-scale 1 1)
    (send dc set-text-foreground "blue")
    (send dc draw-text str x y)))

(define-syntax-rule
  (drawing items ...)
  (lambda (dc) (execute-functions (list items ...) dc)))

(define my-drawing
  (drawing (text "hi" (at 1 1)) (text "lo" (at 20 20))))

(define frame (new frame%
                   [label "Example"]
                   [width 300]
                   [height 300]))
(new canvas% [parent frame]
             [paint-callback (lambda (canvas dc) (my-drawing dc))])
(send frame show #t)

上述程序将导致错误:

. . application: not a procedure;
 expected a procedure that can be applied to arguments
  given: #<void>

可以追溯到drracket中的函数执行:(function arg)

如果我检查无效,它可以正常工作:

(define-syntax-rule (execute-functions flist arg)
  (for-each
   (lambda (function)
     [if (void? function) void (function arg)])
   flist))

但我有点担心它首先被传递无效,因为我不知道为什么会这样。这是从功能列表中预期的吗?

1 个答案:

答案 0 :(得分:4)

execute-functions宏的定义中,您有一组额外的括号(在这种情况下是方括号,但它们是等价的)。

(define-syntax-rule (execute-functions flist arg)
  (for-each
   (lambda (function)
     [(function arg)])
;    ^--------------^------- these brackets shouldn’t be here
   flist))

调用本身(function arg)可能返回#<void>,并且尝试将结果作为函数本身调用(不提供参数),因为它用括号/括号括起来,表示函数当用作表达式时,调用Lisp / Scheme / Racket。

作为一个单独的问题,你在任何地方使用define-syntax-rule而不是简单地使用define让我感到困惑,似乎没有多大意义。这些东西不需要是宏 - 当你不需要宏的语法转换功能时,总是更喜欢宏上的函数。

函数更灵活,可以高阶使用(也就是说,它们可以作为值传递),但宏不能。此外,肆意使用宏将生成大量代码,有效地迫使编译器内联每一个“函数调用”。在需要时使用它们,但这里不需要它们。

execute-functionsdrawing函数可以替换为普通函数,几乎没有任何修改:

(define (execute-functions flist arg)
  (for-each
   (lambda (function)
     (function arg))
   flist))

(define (drawing . items)
  (lambda (dc) (execute-functions items dc)))

text宏的自定义语法对我来说似乎不值得,但如果你真的想要它,那么你可能想要将功能拉到一个单独的,宏所遵循的普通函数:

(define ((text-proc str x y) dc)
  (send dc set-scale 1 1)
  (send dc set-text-foreground "blue")
  (send dc draw-text str x y))

(define-syntax-rule (text str (at x y))
  (text-proc str x y))