私は 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") ) ) )
これらのそれぞれを call でテストしました(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" @ @)))
はこれらの実装のそれぞれをテストしていました。奇妙なことに、最初の実装を初めてテストしたとき、segfault が発生しなかっただけでなく、実際に正しい結果が生成されました(1 4 9)
。その後の各テストでは、segfault が発生しました。明確にするために、プロンプトからのスニペット:
: (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)
てみましたが、同様にセグメンテーション違反を防止しました。定義を適切な呼び出しから分離する誤った呼び出しがない場合、segfault は常に発生します。
これは最終的に 2 つの質問で最高潮に達します。
- 名前のシャドウイングを防ぐにはどうすればよいですか? 明らかに、例はその点で成功していません。
- map@ へのその呼び出しがsegfault にならないのはなぜですか?