我在闲逛 PicoLisp,发现自己对如何编写传统上由宏处理的元编程函数(在其他 lisp 方言中)感到困惑。对我来说最大的担忧是我看不到如何防止变量名隐藏。回顾元编程 101中的示例,如果有的话,只会让我更加困惑。
有关如何实现该功能的示例mapeach
,如链接文章中所示:
[de mapeach "Args" # expression
(let [(@Var @List . @Body) "Args"]
(macro
(mapcar
'((@Var) . @Body)
@List ]
(de mapeach "Args"
(mapcar
(cons (cons (car "Args")) (cddr "Args"))
(eval (cadr "Args")) ) )
(de mapeach "Args"
(mapcar
'(("E")
(bind (car "Args")
(set (car "Args") "E")
(run (cddr "Args")) ) )
(eval (cadr "Args")) ) )
(de mapeach "Args"
(let "Vars" (pop '"Args")
(apply mapcar
(mapcar eval (cut (length "Vars") '"Args"))
(cons "Vars" "Args") ) ) )
我已经用 call 测试了每一个(let "Args" * (mapeach N (1 2 3) ("Args" N N)))
。正如预期的那样,PicoLisp 解释器(以 command 开头pil +
)遇到段错误并崩溃。我认为这是因为mapeach
's"Args"
阴影"Args"
在调用点定义。
我还尝试了他们的两种实现map@
(“更可爱”的替代方案mapeach
)。
(de map@ "Args"
(mapcar
'(("E") (and "E" (run (cdr "Args")))) # 'and' sets '@'
(eval (car "Args")) ) )
(de map@ "Args"
(mapcar
'((@) (run (cdr "Args")))
(eval (car "Args")) ) )
我曾经(let "Args" * (map@ (1 2 3) ("Args" @ @)))
测试过这些实现中的每一个。奇怪的是,我第一次测试第一个实现时,不仅没有段错误,而且实际上产生了正确的结果(1 4 9)
。随后的每个测试都导致了段错误。为清楚起见,提示中的片段:
: (de map@ "Args"
(mapcar
'(("E") (and "E" (run (cdr "Args")))) # 'and' sets '@'
(eval (car "Args")) ) )
-> map@
: (let "Args" * (mapeach N (1 2 3) ("Args" N N)))
!? (mapeach N (1 2 3) ("Args" N N))
mapeach -- Undefined
?
: (let "Args" * (map@ (1 2 3) ("Args" @ @)))
-> (1 4 9)
我相信通过调用(当时)未定义的函数以某种方式阻止了段错误mapeach
,我也尝试过(ooga booga)
,这同样阻止了段错误。如果我没有将定义与正确调用分开的错误调用,则总是会发生段错误。
这最终导致两个问题:
- 如何防止名称隐藏?显然,这些例子在这方面并不成功。
- 为什么对 map@ 的调用不会导致段错误?