【发布时间】:2019-04-15 21:47:48
【问题描述】:
我在闲逛 PicoLisp,发现自己对如何编写传统上由宏(在其他 lisp 方言中)处理的元编程函数感到困惑。对我来说最大的担忧是我看不到如何防止变量名隐藏。回顾Metaprogramming 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") ) ) )
我已经通过调用(let "Args" * (mapeach N (1 2 3) ("Args" N N))) 测试了其中的每一个。正如预期的那样,PicoLisp 解释器(以命令pil + 开始)遇到段错误并崩溃。我认为这是因为mapeach 的"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)。随后的每个测试都导致了段错误。为清楚起见,提示中的 sn-p:
: (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@ 的调用不会导致段错误?
【问题讨论】:
-
也许使用匿名(瞬态)符号? (software-lab.de/doc/ref.html#symbol)
-
这是我困惑的一部分,
"Args"是一个短暂的符号。我尝试生成一个使用匿名瞬态符号作为参数列表的函数,但它并没有真正起作用。但是,我认为更好地理解瞬态符号可能是解决我的问题的关键。我相信答案是“瞬态符号的索引在加载源文件之前和之后会自动清除,或者可以使用 ==== 函数显式重置”。在普通 REPL 使用期间,它似乎不会自动重置。 software-lab.de/doc/ref.html#transient-io
标签: scope lisp dynamic-scope picolisp