如何编写不使用其参数隐藏变量的 PicoLisp 函数

How to write a PicoLisp function that does not shadow variables with it's parameters

我正在闲逛 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)。随后的每个测试都会导致段错误。为清楚起见,提示中的片段:

:  (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),这同样阻止了段错误。如果我没有将定义与正确调用分开的错误调用,则总是会发生段错误。

这最终会导致 2 个问题:

  1. 如何防止名称隐藏?显然,这些例子在这方面没有成功。
  2. 为什么对 map@ 的调用 而不是 会导致段错误?

According to this"The index for transient symbols is cleared automatically before and after loading a source file, or it can be reset explicitly with the ==== function"。它没有指定在常规 REPL 使用期间自动清除它的任何方式,这是我测试它的上下文。

此代码运行正常:

[de mapeach "Args"    # expression
      (let [(@Var @List . @Body)  "Args"]
         (macro
            (mapcar
               '((@Var) . @Body)
               @List ]
(====)
(let "Args" * (mapeach N (1 2 3) ("Args" N N)))

即使不调用 ====,它也能按预期运行,但前提是对 mapeach 的调用不在同一个文件中。

解决我问题的两个部分:

  1. 您可以通过在不同的文件中使用瞬态符号或随后调用 ====.
  2. 来防止名称隐藏
  3. 这些调用可能有效,因为调试器清除了包含瞬态符号的索引。