195
evaluate (s, e, a:f:Apply:c, d)
evaluate ((Closure x body env):a:s, e, Apply:c, d) =
evaluate ([], (x, a) <+> env, [body], (s, e, c):d)
evaluate ((Oper n f args):a:s, e, Apply:c, d)
| n == 1 = evaluate ((intrinsic f newArgs):s, e, c, d)
| otherwise = evaluate ((Oper (n-1) f newArgs):s, e, c, d)
where newArgs = args ++ [a]
evaluate (s, e, (Let pairs body):c, d) =
evaluate (s, e,
(reverse values) ++ ((LetApply names body):c), d)
where (names, values) = unzip pairs
evaluate (s, e, (LetApply names body):c, d) =
evaluate ([], head <++> e, [body], (tail, e, c):d)
where (head, tail) = makepairs names s
evaluate (s, e, (Letrec pairs body):c, d) =
evaluate (s, (map (\(x,y) -> (x,(Numeric 0))) pairs) <++> e,
(reverse values) ++ ((LetrecApply names body):c), d)
where (names, values) = unzip pairs
evaluate (s, e, (LetrecApply names body):c, d) =
evaluate ([], newCtx, [body],
(tail, (drop (length names) e), c):d)
where (newCtx, tail) = replaceAll names s e
-- makepairs [x1,... xn] [e1,... en, s1,...] =
-- ([(x1, e1),... (xn, en)], [s1,...])
makepairs [] s = ([], s)
makepairs (x:names) (e:exprs) = ((x, e):head, tail)
where (head, tail) = makepairs names exprs
-- replaceAll [x1, x2,... xn] [e1', e2',...en',s1,...]
-- [(x1, ?), (x2, ?),... (xn, ?), (y1, v1),...] =
-- ([(x1, e1'),... (xn, en'), (y1, v1),...], [s1,...])
replaceAll [] s e = (e, s)
replaceAll (x:nms) (e:exprs) ctx =
replaceAll nms exprs (replace x e ctx)
Мы реализовали энергичную SECD-машину. Возникает
естественный вопрос: что нужно изменить в нашей программе, чтобы
поведение SECD-машины изменилось и стало бы соответствовать ленивой
схеме вычисления выражений? Очевидно, что для этого, прежде всего,
надо изменить реализацию вызова функции, чтобы вычислять значение ее
аргумента лишь при первом обращении к нему. Конечно, такие же
изменения
следует внести и в реализацию вычисления блоков. Поскольку
все наши вычисления основаны на хранении значений в контексте, то все,
что для этого требуется – это задержать вычисление значения при
помещении его в контекст. Теперь в тот момент, когда значение,
хранящееся в контексте, понадобится в качестве аргумента строгой
функции (первое обращение!), нужно
будет произвести вычисление и
заменить в контексте значение переменной вычисленным значением. Для