#scope #lisp #dynamic-scope #picolisp
#область действия #lisp #динамическая область видимости #picolisp
Вопрос:
Я лениво изучаю 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
) испытывает сбой segfault и сбой. Я предполагаю, что это связано 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)
Я считаю, что ошибка segfault была каким-то образом предотвращена вызовом (тогда) неопределенной функции mapeach
, я также пытался (ooga booga)
, что аналогичным образом предотвратило ошибку segfault. Если у меня нет ошибочного вызова, отделяющего определение от правильного вызова, всегда возникает ошибка segfault.
В конечном итоге это приводит к 2 вопросам:
- Как я могу предотвратить затенение имени? Очевидно, что примеры не преуспевают в этом отношении.
- Почему этот вызов map@ не приводит к segfault ?
Комментарии:
1. Возможно, с использованием анонимных (временных) символов? ( software-lab.de/doc/ref.html#symbol )
2.Это часть моего замешательства,
"Args"
это временный символ. Я попытался сгенерировать функцию, которая использовала анонимный переходный символ для своего списка аргументов, и это действительно не сработало. Однако я думаю, что лучшее понимание переходных символов может быть ключом к решению моей проблемы. Я полагаю, что ответ заключается в том, что «индекс для переходных символов очищается автоматически до и после загрузки исходного файла, или его можно сбросить явно с помощью функции ====». Похоже, что он не сбрасывается автоматически при обычном использовании REPL. software-lab.de/doc/ref.html#transient-io
Ответ №1:
В соответствии с этим «индекс для переходных символов автоматически очищается до и после загрузки исходного файла, или его можно сбросить явно с помощью функции ====». В нем не указано, каким образом он автоматически очищается при обычном использовании 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
не находится в том же файле.
Чтобы ответить на 2 части моего вопроса:
- Вы можете предотвратить затенение имени, используя переходные символы либо в разных файлах, либо с последующим вызовом
====
. - Эти вызовы, вероятно, сработали, потому что отладчик очищает индекс, содержащий переходные символы.