Помогите мне написать мои среды LISP:) LISP, Ruby Hashes

Я внедряю рудиментарную версию LISP в Ruby только для того, чтобы ознакомиться с некоторыми понятиями. Я основываю свое исполнение на Питере Норвиге Lispи (http://norvig.com/lispy.html).

Там что-то мне здесь не хватает, и я был бы рад помочь...

Он подклассифицирует Python dict следующим образом:

class Env(dict):
 "An environment: a dict of {'var':val} pairs, with an outer Env."
 def __init__(self, parms=(), args=(), outer=None):
 self.update(zip(parms,args))
 self.outer = outer
 def find(self, var):
 "Find the innermost Env where var appears."
 return self if var in self else self.outer.find(var)

Затем он продолжает объяснять, почему он это делает, а не просто использует dict. Однако по какой-то причине его объяснение продолжает проходить сквозь мои глаза и выходить через затылок.

Почему бы не использовать dict, а затем внутри eval-функции, когда нужно создать новую "под-среду", просто возьмите существующий dict и обновите пары ключ/значение, которые необходимо обновить, и передайте это новый dict в следующий eval?

Не будет ли интерпретатор Python отслеживать предыдущие "внешние" envs? И не будет ли характер рекурсии гарантировать, что значения выведены из "внутреннего" в "внешний"?

Я использую Ruby, и я пытался реализовать вещи таким образом. Что-то не работает, хотя, возможно, из-за этого, а может и нет. Здесь моя функция eval, являющаяся регулярным хэшем:

def eval(x, env = $global_env)
 ........ 
 elsif x[0] == "lambda" then
 ->(*args) { eval(x[2], env.merge(Hash[*x[1].zip(args).flatten(1)])) }
 ........ 
end

Линия, которая, конечно же, имеет значение "лямбда".

Если есть функциональная разница, что существенно отличается от того, что я здесь делаю и что сделал Норвиг со своим классом Env? Может ли кто-нибудь описать мне случай, когда два будут отклоняться?

Если нет никакой разницы, тогда, возможно, кто-то может рассказать мне, почему Норвиг использует класс Env. Спасибо:)

2 ответа

Если привязки переменных в вашем Lisp являются неизменяемыми, скопируйте окружения эквивалентно их связыванию. Но рассмотрим следующий случай:

(define main
 (lambda ()
 (define i 0)
 (define increment-i
 (lambda ()
 (set! i (+ i 1))))
 (increment-i)
 i))
(main)

Если среда increment-i полностью не зависит от main (так как это копия), мутация i не будет видна в main, и приведенный выше код вернет 0. Если, с другой стороны, среды связаны, возвращаемое значение будет 1, как ожидалось.


Мой Python не так хорош, и мой Lisp довольно ржавый, но я буду думать о том, что происходит: вы не можете уйти от указателей даже на языках, которые утверждают, что не имеют их.

eval, о котором вы говорите, не сделает копию dict, он просто сделает копию ссылки, и вы получите две ссылки (переменные AKA), которые ссылаются на (т.е. указать) тот же базовый объект. В основном это вариант:

a = [ 'a', 'b', 'c' ]
b = a
a[1] = 'x'
puts a
# => ["a", "x", "c"]
puts b 
# => ["a", "x", "c"]

Его класс Env позволяет изменять среду внутри eval без изменения внешней среды, в то время как метод find позволяет вам обращаться к внешней среде без необходимости знать; Кроме того, изменения будут внутри внутренней среды, и эти изменения будут маскировать соответствующие значения во внешней среде; get операции будут обращаться к локальной среде и внешней среде, операции с настройками изменят только внутреннюю среду.

Я думаю, вы могли бы назвать Env фасад уровня объекта (а не уровня класса).

Я не уверен, что случилось с вашей реализацией ruby, похоже, что вы делаете модифицированную копию хэша среды. Можете ли вы пояснить "Что-то не работает"?

Уточнение: Env - таблица символов. Материал обертывания/перенаправления в find позволяет вам получить доступ к внешней таблице символов, защищая ее от добавления новых символов, новые символы будут добавляться только во внутреннюю таблицу символов. Env по существу управляет замыканием.

licensed under cc by-sa 3.0 with attribution.