Как написать функцию PicoLisp, которая не скрывает переменные своими параметрами - PullRequest
2 голосов
/ 16 апреля 2019

Я лениво изучаю 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") ) ) )

Я проверил каждый из них с помощью вызова (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).Каждый последующий тест привел к ошибке.Для ясности, фрагмент из приглашения:

:  (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), что аналогично предотвратилоСигфоЕсли у меня нет ошибочного вызова, отделяющего определение от правильного вызова, всегда происходит ошибка.

В конечном итоге это завершается двумя вопросами:

  1. Как я могу предотвратить скрытие имени?Очевидно, что примеры в этом отношении не увенчались успехом.
  2. Почему этот вызов map @ не приводит к segfault?

1 Ответ

2 голосов
/ 16 апреля 2019

В соответствии с этим"Индекс для переходных символов очищается автоматически до и после загрузки исходного файла или может быть сброшен явно с помощью функции ====".Он не указывает каким-либо образом, что он автоматически очищается во время регулярного использования 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 части моего вопроса:

  1. Вы можете предотвратить затенение имен, используя временные символы либо в разных файлах, либо после вызова ====.
  2. Эти вызовы, вероятно, сработали, поскольку отладчик очищает индекс, содержащий временные символы.
...