我正在玩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))
但我有点担心它首先被传递无效,因为我不知道为什么会这样。这是从功能列表中预期的吗?
答案 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-functions
和drawing
函数可以替换为普通函数,几乎没有任何修改:
(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))