SlideShare a Scribd company logo
1
Кубенский А.А. Функциональное программирование.
Глава 5. Системы исполнения функциональных программ.
5.3. Eval / Apply-интерпретатор
Интерпретация, основанная на контексте.
Контекст – иерархический ассоциативный список связей имен переменных со значениями.
type Context = [(String, Expr)]
assoc :: String -> Context -> Expr
assoc x ((y,e):ctx) | x == y = e
| otherwise = assoc x ctx
eval :: Context -> Expr -> Expr
apply :: Expr -> Expr -> Expr
-- вычисление значения выражения в контексте (приведение к СЗНФ):
-- вычисление результата применения функции к аргументу:
interpreter :: Expr -> Expr
-- интерпретация:
interpreter = eval []
2
Кубенский А.А. Функциональное программирование.
Глава 5. Системы исполнения функциональных программ.
5.3. Eval / Apply-интерпретатор (продолжение)
data Expr =
Integral Integer | Logical Bool | Function String -- константы
| Variable String -- переменная
| Lambda String Expr -- лямбда-выражение
| Apply Expr Expr -- применение функции
| Let String Expr Expr | Letrec [(String, Expr)] Expr -- блоки
| Closure String Expr Context -- замыкание
| Oper Int String [Expr] -- сечение
eval _ e@(Integral _) = e
eval _ e@(Logical _) = e
eval _ (Function f) = Oper (arity f) f []
eval ctx (Lambda x e) = Closure x e ctx
eval _ e@(Closure _ _ _) = e
eval _ e@(Oper _ _ _) = e
eval ctx (Variable x) = assoc x ctx
eval ctx (Apply f a) = apply (eval ctx f) (eval ctx a)
apply (Closure x body ctx) arg = eval nc body where nc = (x, arg) : ctx
apply (Oper n f la) a | n == 1 = intrinsic f newListArgs
| otherwise = Oper (n-1) f newListArgs
where newListArgs = la ++ [a]
3
Кубенский А.А. Функциональное программирование.
Глава 5. Системы исполнения функциональных программ.
5.3. Eval / Apply-интерпретатор (продолжение)
eval ctx (Let x arg body) = eval newCtx body
where newCtx = ((x, (eval ctx arg)):ctx)
let x=arg in body ~ (λx.body) arg
(Let x arg body) ~ (Apply (Lambda x body) arg)
eval ctx (Let x arg body) = apply (eval ctx (Lambda x body)) (eval ctx arg)
= apply (Closure x body ctx) (eval ctx arg)
eval ctx (Letrec args body) = eval newCtx body
where newCtx = (map ((x,arg) -> (x, eval newCtx arg)) args) ++ ctx
4
Кубенский А.А. Функциональное программирование.
Глава 5. Системы исполнения функциональных программ.
Энергичный vs. ленивый интерпретатор
Механизм интерпретации определяет реализованную схему!
eval ctx (Apply f a) = apply (eval ctx f) (eval ctx a)
Вычисление аргумента происходит до вызова apply при энергичной реализации, но
задерживается до первого обращения к нему при ленивой реализации инструментального языка.
Если инструментальный язык энергичный, то дополнительные проблемы – это:
 реализация стандартной функции IF;
 реализация рекурсивного блока.
data Expr = ...
| If Expr Expr Expr -- условное выражение
eval ctx (If p t e) = if (eval ctx p) == (Logical True) then eval ctx t
else eval ctx e
eval ctx (If p t e) = eval ctx (if eval ctx p then t else e)
«Зацикленный» контекст, использующийся для реализации рекурсивного блока,
вообще не реализуем в чисто энергичном функциональном языке!
В языке Haskell энергичный интерпретатор можно реализовать с помощью «строгих» применений:
eval ctx (Apply f a) = (apply $! (eval ctx f)) $! (eval ctx a)
5
Кубенский А.А. Функциональное программирование.
Глава 5. Системы исполнения функциональных программ.
Реализация встроенных функций
intrinsic "+" [Integral(a), Integral(b)] = Integral (a+b)
intrinsic "-" [Integral(a), Integral(b)] = Integral (a-b)
intrinsic "*" [Integral(a), Integral(b)] = Integral (a*b)
intrinsic "/" [Integral(a), Integral(b)] = Integral (a `div` b)
intrinsic "EQ0" [Integral(a)] = Logical (a==0)
intrinsic "SUCC" [Integral(a)] = Integral (a+1)
intrinsic "PRED" [Integral(a)] = Integral (a-1)
apply (Oper nArgs f argsList) arg | nArgs == 1 = intrinsic f newList
| otherwise = Oper (nArgs-1) f newList
where newListArgs = argsList ++ [arg]
eval _ (Function f) = Oper (arity f) f []
arity "+" = 2
arity "-" = 2
arity "*" = 2
arity "/" = 2
arity "EQ0" = 1
arity "SUCC" = 1
arity "PRED" = 1
6
Кубенский А.А. Функциональное программирование.
Глава 5. Системы исполнения функциональных программ.
Пример интерпретации простой программы
На языке Haskell: sqr :: Integer -> Integer
sqr x = x*x
interpret: sqr 3
В расширенном лямбда-исчислении: let sqr = λx.* x x in (sqr 3)
prog :: Expr
prog = (Let "sqr"
(Lambda "x" (Apply
(Apply (Function "*“) (Variable "x"))
(Variable "x")))
(Apply (Variable "sqr") (Integral 3)))
Представление в виде выражения типа Expr в языке Haskell:
interpreter progЧто получится в результате вызова ?
7
Пример интерпретации простой программы
interpreter prog
eval [] prog
eval [] (Let "sqr" (Lambda ...) (Apply ...))
apply (Closure "sqr" (Apply ...) []) (eval [] (Lambda "x" ...))
apply (Closure "sqr" (Apply ...) []) (Closure "x" (Apply ...) [])
prog = (Let "sqr"
(Lambda "x" (Apply
(Apply (Function "*") (Variable "x"))
(Variable "x")))
(Apply (Variable "sqr") (Integral 3)))
eval [("sqr",(Closure "x" (Apply ...) []))]
(Apply (Variable "sqr") (Integral 3))
apply (eval [("sqr",(Closure "x" (Apply ...) []))] (Variable "sqr")
(eval [("sqr",(Closure "x" (Apply ...) []))] (Integral 3))
apply (Closure "x" (Apply ...) []) (Integral 3))
eval [("x",(Integral 3)]
(Apply (Apply (Function "*") (Variable "x")) (Variable "x"))
apply (eval [("x",(Integral 3)] (Apply (Function "*") (Variable "x")))
(eval [("x",(Integral 3)] (Variable "x"))
Кубенский А.А. Функциональное программирование.
Глава 5. Системы исполнения функциональных программ.
8
Кубенский А.А. Функциональное программирование.
Глава 5. Системы исполнения функциональных программ.
Пример интерпретации простой программы (продолжение)
apply (apply (eval [("x",(Integral 3)] (Function "*"))
(eval [("x",(Integral 3)] (Variable "x")))
(Integral 3)
apply (apply (Oper (arity "*") "*" [])
(Integral 3))
(Integral 3)
apply (apply (Oper 2 "*" []) (Integral 3)) (Integral 3)
apply (Oper 1 "*" [(Integral 3)]) (Integral 3)
intrinsic "*" [(Integral 3),(Integral 3)]
(Integral 9)
apply (eval [("x",(Integral 3)] (Apply (Function "*") (Variable "x")))
(eval [("x",(Integral 3)] (Variable "x"))
9
Кубенский А.А. Функциональное программирование.
Глава 5. Системы исполнения функциональных программ.
Глава 5. Системы исполнения функциональных программ
5.3. SECD-машина.
Попробуем транслировать конструкции лямбда-исчисления в еще более простой язык,
интерпретатор которого допускает простое и однозначное толкование.
S – Stack – содержит промежуточные результаты вычислений в СЗНФ
E – Environment – содержит контекст вычислений
C – Control – содержит последовательность команд
D – Dump – содержит состояния машины
type Stack = [WHNF]
type Environment = [(String, WHNF)]
type Control = [Command]
type Dump = [(Stack, Environment, Control)]
data WHNF = C_Int Integer | C_Bool Bool
| Closure String Environment Command
| Oper String Int [WHNF]
data Command = Integral Integer | Boolean Bool | Function String
| Variable String | Lambda String Command
| Apply Command Command | If Command Command Command
| Let String Command Command
| Letrec [(String, Command)] Command
data SECD = (Stack, Environment, Control, Dump)
10
Кубенский А.А. Функциональное программирование.
Глава 5. Системы исполнения функциональных программ.
Работа SECD-машины.
Переход из состояния в состояние: (s, e, c, d)  (s', e', c', d')
Функциональное выражение для процесса переходов:
evaluate :: SECD -> SECD
Уравнения функции evaluate будут иметь следующий вид:
evaluate (s, e, c, d) = evaluate (s', e', c', d')
или, в случае, когда состояние (s, e, c, d) заключительное:
evaluate (s, e, c, d) = (s, e, c, d)
Интерпретатор создает SECD-машину в начальном состоянии, запускает ее, вызывая функцию
evaluate, и извлекает результат вычислений из SECD-машины в конечном состоянии:
interpret :: Command -> WHNF
interpret com = res where (res:_, _, _, _) = evaluate ([], [], [com], [])
то есть исходная программа помещается в регистр управления,
а результат извлекается с вершины стека выражений.
11
Кубенский А.А. Функциональное программирование.
Глава 5. Системы исполнения функциональных программ.
Уравнения функции evaluate, описывающей работу SECD-машины.
evaluate (s, e, (Integral n):c, d) = evaluate ((C_Int n):s, e, c, d)
evaluate (s, e, (Boolean b):c, d) = evaluate ((C_Bool b):s, e, c, d)
evaluate (s, e, (Lambda x body):c, d) =
evaluate ((Closure x body e):s, e, c, d)
evaluate (s, e, (Function f):c, d) =
evaluate ((Oper (arity f) f []):s, e, c, d)
evaluate (s, e, (Variable x):c, d) = evaluate ((assoc x e):s, e, c, d)
Команды, представляющие выражения, уже находящиеся в СЗНФ, просто перекладывают
эти выражения на вершину стека вычислений, изменяя их представление.
evaluate (s, e, (If cond the els):c, d) =
evaluate (s, e, cond:(Select the els):c, d)
evaluate ((C_Bool True):s, e, (Select the els):c, d) =
evaluate (s, e, the:c, d)
evaluate ((C_Bool False):s, e, (Select the els):c, d) =
evaluate (s, e, els:c, d)
Исполнение команды условного вычисления If:
data Command =
...
| Select Command Command
Здесь Select – это новая специальная команда условного перехода:
12
Кубенский А.А. Функциональное программирование.
Глава 5. Системы исполнения функциональных программ.
Уравнения функции evaluate для более сложных команд.
evaluate (s, e, (Apply f a):c, d) = evaluate (s, e, a:f:App:c, d)
Применение функции:
data Command = ... | App
Здесь App – это новая специальная команда применения функции:
evaluate ((Oper n f args):arg:s, e, App: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 ++ [arg]
Исполнение команды App для случая примитивной функции:
evaluate ((Closure x body ctx):a:s, e, App:c, d) =
evaluate ([], (x, a):ctx, [body], (s, e, c):d)
evaluate (x:_, _, [], (s, e, c):d) = evaluate (x:s, e, c, d)
Исполнение команды App для замыкания (вход в функцию и выход из нее):
evaluate (s, e, (Let x exp body):c, d) =
evaluate (s, e, exp:(LApp x body):c, d)
evaluate (arg:s, e, (LApp x body):c, d) =
evaluate ([], (x,arg):e, [body], (s, e, c):d)
Аналогично для простого (нерекурсивного) блока:
data Command = ... | LApp String Command
13
Кубенский А.А. Функциональное программирование.
Глава 5. Системы исполнения функциональных программ.
Интерпретация простого и рекурсивного блоков.
Рассмотрим последовательность состояний при исполнении простого блока:
evaluate (s, e, (Letrec pairs body):c, d) =
evaluate (s, newPairs ++ e, (reverse exprs) ++ ((LRApp n body):c), d)
where (newPairs, n) = (map (n -> (n, ?)) names, length names)
(names, exprs) = unzip pairs
STK, ENV, (Let x exp body):COM, DUMP
STK, ENV, exp:(LApp x body):COM, DUMP
exp':STK, ENV, (LApp x body):COM, DUMP
[], (x,exp'):ENV, [body], (STK, ENV, COM):DUMP
[body'], (x,exp'):ENV, [], (STK, ENV, COM):DUMP
body':STK, ENV, COM, DUMP
Последовательность состояний при исполнении рекурсивного блока должна отличаться
тем, что вычисление связываемых значений должно проводиться в уже пополненном контексте:
STK, ENV, (Letrec [(x1,e1),(x2,e2),...(xn,en)] body):COM, DUMP
STK, (x1,?):(x2,?):...(xn,?):ENV, en:...e2:e1:(LRApp body):COM, DUMP
e1':e2':...en':STK, (x1,?):(x2,?):...(xn,?):ENV, (LRApp body):COM, DUMP
[], (x1,e1'):(x2,e2'):...(xn,en'):ENV, [body], (STK,ENV,COM):DUMP
[body'], (x1,e1'):(x2,e2'):...(xn,en'):ENV, [], (STK,ENV,COM):DUMP
body':STK, ENV, COM, DUMP
Здесь самый тонкий момент – это замена значений в уже сформированном контексте.
evaluate (s, e, (LRApp n body):c, d) =
evaluate ([], replaceValues n s e, [body], (drop n s, drop n e, c):d)
14
Кубенский А.А. Функциональное программирование.
Глава 5. Системы исполнения функциональных программ.
Реализация псевдо-функции replaceValues.
e1':e2':...en':STK, (x1, ):(x2, ):...(xn, ):ENV, (LRApp body):COM, DUMP
?
?
?
e1'
e2'
en'
Если в выражениях e1', e2', en' имеются копии контекста, то замена значений
с помощью псевдо-функции replaceValues повлияет сразу на все копии.
Реализованная SECD-машина – энергичная, однако, и здесь ленивый инструментальный язык
реализации привносит «ленивость» в процесс интерпретации. Например, помещаемые в стек
результаты вычислений функции intrinsic на самом деле будут получены, только если они
реально потребуются для представления результата.
15
Ленивая версия SECD-машины.
Кубенский А.А. Функциональное программирование.
Глава 5. Системы исполнения функциональных программ.
Введем понятие задержанного результата для того, чтобы задержать
вычисление значения выражения до момента первого обращения к нему.
data WHNF = C_Int Integer | C_Bool Bool
| Closure String Environment Command
| Oper String Int [WHNF]
| Delay Environment Command
evaluate (s, e, (Apply f a):c, d) =
evaluate ((Delay e a):s, e, f:App:c, d)
Применение функции для случая ленивых вычислений:
Вычисление задержанного результата будет происходить при выполнении встроенных операций,
таких как арифметические операторы или индексация кортежа.
evaluate (func@(Oper n f args):(Delay env com):s, e, App:c, d) =
evaluate ([], env, [com], (func:s, e, c):d)
При возврате вычисленное значение выражения помещается на место аргумента функции,
и применение функции повторяется уже к вычисленному значению:
evaluate ([res], _, [], (func:s, e, c):d) =
evaluate (func:res:s, e, App:c, d)
Для того, чтобы избежать повторного вычисления одних и тех же значений, задержки помещаются
в отдельную область памяти, а в стек вместо нее помещается указатель. Когда значение будет
вычислено, результат должен быть помещен на место задержки с помощью присваивания.
16
Реализация рекурсивного блока в ленивой версии SECD-машины.
evaluate (s, e, (Letrec pairs body):c, d) =
evaluate ([], newEnv, [body], (s, e, c):d)
where newEnv = (map ((n, v) -> (n, (Delay newEnv v)) pairs) ++ e
Здесь в новом контексте newEnv появятся задержки, содержащие ссылку на тот же самый контекст.
Реализация простого блока Let остается без изменений.
Выводы:
 SECD-машина удобна для описания последовательности команд для вычислений.
 Энергичная SECD-машина хорошо реализуется в энергичном языке, однако реализация
рекурсивного блока требует исполнения присваиваний; для ленивого инструментального
языка требуется моделирование энергичных вычислений.
 Реализация ленивой SECD-машины требует реализации механизма задержанных
вычислений с присваиванием.
Кубенский А.А. Функциональное программирование.
Глава 5. Системы исполнения функциональных программ.

More Related Content

PDF
Swift School #2
PDF
Sergii Tsypanov "Performance 1001 Tips"
PDF
C# Desktop. Занятие 03.
PDF
Интерпретирование языков с помощью Free-монад
PDF
2.4 Использование указателей
PPT
Характерные черты функциональных языков программирования
PDF
Очень вкусный фрукт Guava
PPTX
PHP7 - что ожидать?
Swift School #2
Sergii Tsypanov "Performance 1001 Tips"
C# Desktop. Занятие 03.
Интерпретирование языков с помощью Free-монад
2.4 Использование указателей
Характерные черты функциональных языков программирования
Очень вкусный фрукт Guava
PHP7 - что ожидать?

What's hot (19)

PDF
Олег Алистратов — Сортировка списков в Perl и Python
PPTX
C++ осень 2012 лекция 11
PPTX
Kotlin
PDF
Монады для барабанщиков. Антон Холомьёв
PPTX
Мифы и легенды Java Stream API
PDF
Scala for android
PPT
2014.12.06 04 Евгений Тюменцев — Откуда появились s.o.l.i.d. принципы
PPTX
математическое обоснование Solid принципов. Конференция dotnetconf (Челябинск...
PPT
Математическое обоснование S.O.L.I.D принципов
PDF
Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30...
PDF
Курсы актерского мастерства
PDF
Сладкое будущее: Phalcon и Zephir
PPTX
Stream API: рекомендации лучших собаководов
PPTX
разработка серверов и серверных приложений лекция №3
PPT
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014
PDF
Ecma script 6 in action
PDF
Олег Алистратов — Сортировка списков в Perl и Python
C++ осень 2012 лекция 11
Kotlin
Монады для барабанщиков. Антон Холомьёв
Мифы и легенды Java Stream API
Scala for android
2014.12.06 04 Евгений Тюменцев — Откуда появились s.o.l.i.d. принципы
математическое обоснование Solid принципов. Конференция dotnetconf (Челябинск...
Математическое обоснование S.O.L.I.D принципов
Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30...
Курсы актерского мастерства
Сладкое будущее: Phalcon и Zephir
Stream API: рекомендации лучших собаководов
разработка серверов и серверных приложений лекция №3
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014
Ecma script 6 in action
Ad

Viewers also liked (9)

PDF
VVD-Banner-85x200cm-V1
PPTX
Урок 1. Что такое функциональное программирование
PDF
GHP Food Alliance SPM
PPTX
Control presupuestario...armenia alvarado
DOCX
07-20-16 KBailey CV
PDF
Presentatie full service communicatie voor een familiebedrijf
PPTX
Урок 4. "Завязывание узлов". Классы
PPTX
Tarea de yulieth alvarez medicina legal
VVD-Banner-85x200cm-V1
Урок 1. Что такое функциональное программирование
GHP Food Alliance SPM
Control presupuestario...armenia alvarado
07-20-16 KBailey CV
Presentatie full service communicatie voor een familiebedrijf
Урок 4. "Завязывание узлов". Классы
Tarea de yulieth alvarez medicina legal
Ad

Similar to Урок 7. Интерпретация и компиляция функциональных программ. (20)

PPTX
Урок 8. Введение в редукцию графов
PPTX
Урок 6. Чистое лямбда-исчисление.
PPTX
Statements in VHDL - Sequential Statements
PPTX
Урок 3. Карринг и ленивые вычисления.
PDF
Лекция о языке программирования Haskell
PPTX
основы и применение статического анализа кода при разработке лекция 2
PDF
Лекция №2. Алгоритмические проблемы. Стандартные схемы программ. Предмет "Тео...
PPT
лекция 3
PDF
апкс 2011 05_verilog
PDF
Тодуа. Методы разработки интерпретатора языка Рефал-2
PPTX
Функциональное программирование.Списки. Функции высших порядков
PPTX
Statements in VHDL - Concurrent Statements
PDF
TMPA-2015: Expanding the Meta-Generation of Correctness Conditions by Means o...
PDF
Tech Talks @NSU: Теоретические основы программирования: проекции Футамуры-Тур...
PPTX
Подпрограммы
PPT
практика 4
PDF
fp intro
PDF
TMPA-2013 Anureyev: On the Road to Technology of Developing the Means of Dedu...
PDF
Функциональное программирование - Александр Алексеев
Урок 8. Введение в редукцию графов
Урок 6. Чистое лямбда-исчисление.
Statements in VHDL - Sequential Statements
Урок 3. Карринг и ленивые вычисления.
Лекция о языке программирования Haskell
основы и применение статического анализа кода при разработке лекция 2
Лекция №2. Алгоритмические проблемы. Стандартные схемы программ. Предмет "Тео...
лекция 3
апкс 2011 05_verilog
Тодуа. Методы разработки интерпретатора языка Рефал-2
Функциональное программирование.Списки. Функции высших порядков
Statements in VHDL - Concurrent Statements
TMPA-2015: Expanding the Meta-Generation of Correctness Conditions by Means o...
Tech Talks @NSU: Теоретические основы программирования: проекции Футамуры-Тур...
Подпрограммы
практика 4
fp intro
TMPA-2013 Anureyev: On the Road to Technology of Developing the Means of Dedu...
Функциональное программирование - Александр Алексеев

Урок 7. Интерпретация и компиляция функциональных программ.

  • 1. 1 Кубенский А.А. Функциональное программирование. Глава 5. Системы исполнения функциональных программ. 5.3. Eval / Apply-интерпретатор Интерпретация, основанная на контексте. Контекст – иерархический ассоциативный список связей имен переменных со значениями. type Context = [(String, Expr)] assoc :: String -> Context -> Expr assoc x ((y,e):ctx) | x == y = e | otherwise = assoc x ctx eval :: Context -> Expr -> Expr apply :: Expr -> Expr -> Expr -- вычисление значения выражения в контексте (приведение к СЗНФ): -- вычисление результата применения функции к аргументу: interpreter :: Expr -> Expr -- интерпретация: interpreter = eval []
  • 2. 2 Кубенский А.А. Функциональное программирование. Глава 5. Системы исполнения функциональных программ. 5.3. Eval / Apply-интерпретатор (продолжение) data Expr = Integral Integer | Logical Bool | Function String -- константы | Variable String -- переменная | Lambda String Expr -- лямбда-выражение | Apply Expr Expr -- применение функции | Let String Expr Expr | Letrec [(String, Expr)] Expr -- блоки | Closure String Expr Context -- замыкание | Oper Int String [Expr] -- сечение eval _ e@(Integral _) = e eval _ e@(Logical _) = e eval _ (Function f) = Oper (arity f) f [] eval ctx (Lambda x e) = Closure x e ctx eval _ e@(Closure _ _ _) = e eval _ e@(Oper _ _ _) = e eval ctx (Variable x) = assoc x ctx eval ctx (Apply f a) = apply (eval ctx f) (eval ctx a) apply (Closure x body ctx) arg = eval nc body where nc = (x, arg) : ctx apply (Oper n f la) a | n == 1 = intrinsic f newListArgs | otherwise = Oper (n-1) f newListArgs where newListArgs = la ++ [a]
  • 3. 3 Кубенский А.А. Функциональное программирование. Глава 5. Системы исполнения функциональных программ. 5.3. Eval / Apply-интерпретатор (продолжение) eval ctx (Let x arg body) = eval newCtx body where newCtx = ((x, (eval ctx arg)):ctx) let x=arg in body ~ (λx.body) arg (Let x arg body) ~ (Apply (Lambda x body) arg) eval ctx (Let x arg body) = apply (eval ctx (Lambda x body)) (eval ctx arg) = apply (Closure x body ctx) (eval ctx arg) eval ctx (Letrec args body) = eval newCtx body where newCtx = (map ((x,arg) -> (x, eval newCtx arg)) args) ++ ctx
  • 4. 4 Кубенский А.А. Функциональное программирование. Глава 5. Системы исполнения функциональных программ. Энергичный vs. ленивый интерпретатор Механизм интерпретации определяет реализованную схему! eval ctx (Apply f a) = apply (eval ctx f) (eval ctx a) Вычисление аргумента происходит до вызова apply при энергичной реализации, но задерживается до первого обращения к нему при ленивой реализации инструментального языка. Если инструментальный язык энергичный, то дополнительные проблемы – это:  реализация стандартной функции IF;  реализация рекурсивного блока. data Expr = ... | If Expr Expr Expr -- условное выражение eval ctx (If p t e) = if (eval ctx p) == (Logical True) then eval ctx t else eval ctx e eval ctx (If p t e) = eval ctx (if eval ctx p then t else e) «Зацикленный» контекст, использующийся для реализации рекурсивного блока, вообще не реализуем в чисто энергичном функциональном языке! В языке Haskell энергичный интерпретатор можно реализовать с помощью «строгих» применений: eval ctx (Apply f a) = (apply $! (eval ctx f)) $! (eval ctx a)
  • 5. 5 Кубенский А.А. Функциональное программирование. Глава 5. Системы исполнения функциональных программ. Реализация встроенных функций intrinsic "+" [Integral(a), Integral(b)] = Integral (a+b) intrinsic "-" [Integral(a), Integral(b)] = Integral (a-b) intrinsic "*" [Integral(a), Integral(b)] = Integral (a*b) intrinsic "/" [Integral(a), Integral(b)] = Integral (a `div` b) intrinsic "EQ0" [Integral(a)] = Logical (a==0) intrinsic "SUCC" [Integral(a)] = Integral (a+1) intrinsic "PRED" [Integral(a)] = Integral (a-1) apply (Oper nArgs f argsList) arg | nArgs == 1 = intrinsic f newList | otherwise = Oper (nArgs-1) f newList where newListArgs = argsList ++ [arg] eval _ (Function f) = Oper (arity f) f [] arity "+" = 2 arity "-" = 2 arity "*" = 2 arity "/" = 2 arity "EQ0" = 1 arity "SUCC" = 1 arity "PRED" = 1
  • 6. 6 Кубенский А.А. Функциональное программирование. Глава 5. Системы исполнения функциональных программ. Пример интерпретации простой программы На языке Haskell: sqr :: Integer -> Integer sqr x = x*x interpret: sqr 3 В расширенном лямбда-исчислении: let sqr = λx.* x x in (sqr 3) prog :: Expr prog = (Let "sqr" (Lambda "x" (Apply (Apply (Function "*“) (Variable "x")) (Variable "x"))) (Apply (Variable "sqr") (Integral 3))) Представление в виде выражения типа Expr в языке Haskell: interpreter progЧто получится в результате вызова ?
  • 7. 7 Пример интерпретации простой программы interpreter prog eval [] prog eval [] (Let "sqr" (Lambda ...) (Apply ...)) apply (Closure "sqr" (Apply ...) []) (eval [] (Lambda "x" ...)) apply (Closure "sqr" (Apply ...) []) (Closure "x" (Apply ...) []) prog = (Let "sqr" (Lambda "x" (Apply (Apply (Function "*") (Variable "x")) (Variable "x"))) (Apply (Variable "sqr") (Integral 3))) eval [("sqr",(Closure "x" (Apply ...) []))] (Apply (Variable "sqr") (Integral 3)) apply (eval [("sqr",(Closure "x" (Apply ...) []))] (Variable "sqr") (eval [("sqr",(Closure "x" (Apply ...) []))] (Integral 3)) apply (Closure "x" (Apply ...) []) (Integral 3)) eval [("x",(Integral 3)] (Apply (Apply (Function "*") (Variable "x")) (Variable "x")) apply (eval [("x",(Integral 3)] (Apply (Function "*") (Variable "x"))) (eval [("x",(Integral 3)] (Variable "x")) Кубенский А.А. Функциональное программирование. Глава 5. Системы исполнения функциональных программ.
  • 8. 8 Кубенский А.А. Функциональное программирование. Глава 5. Системы исполнения функциональных программ. Пример интерпретации простой программы (продолжение) apply (apply (eval [("x",(Integral 3)] (Function "*")) (eval [("x",(Integral 3)] (Variable "x"))) (Integral 3) apply (apply (Oper (arity "*") "*" []) (Integral 3)) (Integral 3) apply (apply (Oper 2 "*" []) (Integral 3)) (Integral 3) apply (Oper 1 "*" [(Integral 3)]) (Integral 3) intrinsic "*" [(Integral 3),(Integral 3)] (Integral 9) apply (eval [("x",(Integral 3)] (Apply (Function "*") (Variable "x"))) (eval [("x",(Integral 3)] (Variable "x"))
  • 9. 9 Кубенский А.А. Функциональное программирование. Глава 5. Системы исполнения функциональных программ. Глава 5. Системы исполнения функциональных программ 5.3. SECD-машина. Попробуем транслировать конструкции лямбда-исчисления в еще более простой язык, интерпретатор которого допускает простое и однозначное толкование. S – Stack – содержит промежуточные результаты вычислений в СЗНФ E – Environment – содержит контекст вычислений C – Control – содержит последовательность команд D – Dump – содержит состояния машины type Stack = [WHNF] type Environment = [(String, WHNF)] type Control = [Command] type Dump = [(Stack, Environment, Control)] data WHNF = C_Int Integer | C_Bool Bool | Closure String Environment Command | Oper String Int [WHNF] data Command = Integral Integer | Boolean Bool | Function String | Variable String | Lambda String Command | Apply Command Command | If Command Command Command | Let String Command Command | Letrec [(String, Command)] Command data SECD = (Stack, Environment, Control, Dump)
  • 10. 10 Кубенский А.А. Функциональное программирование. Глава 5. Системы исполнения функциональных программ. Работа SECD-машины. Переход из состояния в состояние: (s, e, c, d)  (s', e', c', d') Функциональное выражение для процесса переходов: evaluate :: SECD -> SECD Уравнения функции evaluate будут иметь следующий вид: evaluate (s, e, c, d) = evaluate (s', e', c', d') или, в случае, когда состояние (s, e, c, d) заключительное: evaluate (s, e, c, d) = (s, e, c, d) Интерпретатор создает SECD-машину в начальном состоянии, запускает ее, вызывая функцию evaluate, и извлекает результат вычислений из SECD-машины в конечном состоянии: interpret :: Command -> WHNF interpret com = res where (res:_, _, _, _) = evaluate ([], [], [com], []) то есть исходная программа помещается в регистр управления, а результат извлекается с вершины стека выражений.
  • 11. 11 Кубенский А.А. Функциональное программирование. Глава 5. Системы исполнения функциональных программ. Уравнения функции evaluate, описывающей работу SECD-машины. evaluate (s, e, (Integral n):c, d) = evaluate ((C_Int n):s, e, c, d) evaluate (s, e, (Boolean b):c, d) = evaluate ((C_Bool b):s, e, c, d) evaluate (s, e, (Lambda x body):c, d) = evaluate ((Closure x body e):s, e, c, d) evaluate (s, e, (Function f):c, d) = evaluate ((Oper (arity f) f []):s, e, c, d) evaluate (s, e, (Variable x):c, d) = evaluate ((assoc x e):s, e, c, d) Команды, представляющие выражения, уже находящиеся в СЗНФ, просто перекладывают эти выражения на вершину стека вычислений, изменяя их представление. evaluate (s, e, (If cond the els):c, d) = evaluate (s, e, cond:(Select the els):c, d) evaluate ((C_Bool True):s, e, (Select the els):c, d) = evaluate (s, e, the:c, d) evaluate ((C_Bool False):s, e, (Select the els):c, d) = evaluate (s, e, els:c, d) Исполнение команды условного вычисления If: data Command = ... | Select Command Command Здесь Select – это новая специальная команда условного перехода:
  • 12. 12 Кубенский А.А. Функциональное программирование. Глава 5. Системы исполнения функциональных программ. Уравнения функции evaluate для более сложных команд. evaluate (s, e, (Apply f a):c, d) = evaluate (s, e, a:f:App:c, d) Применение функции: data Command = ... | App Здесь App – это новая специальная команда применения функции: evaluate ((Oper n f args):arg:s, e, App: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 ++ [arg] Исполнение команды App для случая примитивной функции: evaluate ((Closure x body ctx):a:s, e, App:c, d) = evaluate ([], (x, a):ctx, [body], (s, e, c):d) evaluate (x:_, _, [], (s, e, c):d) = evaluate (x:s, e, c, d) Исполнение команды App для замыкания (вход в функцию и выход из нее): evaluate (s, e, (Let x exp body):c, d) = evaluate (s, e, exp:(LApp x body):c, d) evaluate (arg:s, e, (LApp x body):c, d) = evaluate ([], (x,arg):e, [body], (s, e, c):d) Аналогично для простого (нерекурсивного) блока: data Command = ... | LApp String Command
  • 13. 13 Кубенский А.А. Функциональное программирование. Глава 5. Системы исполнения функциональных программ. Интерпретация простого и рекурсивного блоков. Рассмотрим последовательность состояний при исполнении простого блока: evaluate (s, e, (Letrec pairs body):c, d) = evaluate (s, newPairs ++ e, (reverse exprs) ++ ((LRApp n body):c), d) where (newPairs, n) = (map (n -> (n, ?)) names, length names) (names, exprs) = unzip pairs STK, ENV, (Let x exp body):COM, DUMP STK, ENV, exp:(LApp x body):COM, DUMP exp':STK, ENV, (LApp x body):COM, DUMP [], (x,exp'):ENV, [body], (STK, ENV, COM):DUMP [body'], (x,exp'):ENV, [], (STK, ENV, COM):DUMP body':STK, ENV, COM, DUMP Последовательность состояний при исполнении рекурсивного блока должна отличаться тем, что вычисление связываемых значений должно проводиться в уже пополненном контексте: STK, ENV, (Letrec [(x1,e1),(x2,e2),...(xn,en)] body):COM, DUMP STK, (x1,?):(x2,?):...(xn,?):ENV, en:...e2:e1:(LRApp body):COM, DUMP e1':e2':...en':STK, (x1,?):(x2,?):...(xn,?):ENV, (LRApp body):COM, DUMP [], (x1,e1'):(x2,e2'):...(xn,en'):ENV, [body], (STK,ENV,COM):DUMP [body'], (x1,e1'):(x2,e2'):...(xn,en'):ENV, [], (STK,ENV,COM):DUMP body':STK, ENV, COM, DUMP Здесь самый тонкий момент – это замена значений в уже сформированном контексте. evaluate (s, e, (LRApp n body):c, d) = evaluate ([], replaceValues n s e, [body], (drop n s, drop n e, c):d)
  • 14. 14 Кубенский А.А. Функциональное программирование. Глава 5. Системы исполнения функциональных программ. Реализация псевдо-функции replaceValues. e1':e2':...en':STK, (x1, ):(x2, ):...(xn, ):ENV, (LRApp body):COM, DUMP ? ? ? e1' e2' en' Если в выражениях e1', e2', en' имеются копии контекста, то замена значений с помощью псевдо-функции replaceValues повлияет сразу на все копии. Реализованная SECD-машина – энергичная, однако, и здесь ленивый инструментальный язык реализации привносит «ленивость» в процесс интерпретации. Например, помещаемые в стек результаты вычислений функции intrinsic на самом деле будут получены, только если они реально потребуются для представления результата.
  • 15. 15 Ленивая версия SECD-машины. Кубенский А.А. Функциональное программирование. Глава 5. Системы исполнения функциональных программ. Введем понятие задержанного результата для того, чтобы задержать вычисление значения выражения до момента первого обращения к нему. data WHNF = C_Int Integer | C_Bool Bool | Closure String Environment Command | Oper String Int [WHNF] | Delay Environment Command evaluate (s, e, (Apply f a):c, d) = evaluate ((Delay e a):s, e, f:App:c, d) Применение функции для случая ленивых вычислений: Вычисление задержанного результата будет происходить при выполнении встроенных операций, таких как арифметические операторы или индексация кортежа. evaluate (func@(Oper n f args):(Delay env com):s, e, App:c, d) = evaluate ([], env, [com], (func:s, e, c):d) При возврате вычисленное значение выражения помещается на место аргумента функции, и применение функции повторяется уже к вычисленному значению: evaluate ([res], _, [], (func:s, e, c):d) = evaluate (func:res:s, e, App:c, d) Для того, чтобы избежать повторного вычисления одних и тех же значений, задержки помещаются в отдельную область памяти, а в стек вместо нее помещается указатель. Когда значение будет вычислено, результат должен быть помещен на место задержки с помощью присваивания.
  • 16. 16 Реализация рекурсивного блока в ленивой версии SECD-машины. evaluate (s, e, (Letrec pairs body):c, d) = evaluate ([], newEnv, [body], (s, e, c):d) where newEnv = (map ((n, v) -> (n, (Delay newEnv v)) pairs) ++ e Здесь в новом контексте newEnv появятся задержки, содержащие ссылку на тот же самый контекст. Реализация простого блока Let остается без изменений. Выводы:  SECD-машина удобна для описания последовательности команд для вычислений.  Энергичная SECD-машина хорошо реализуется в энергичном языке, однако реализация рекурсивного блока требует исполнения присваиваний; для ленивого инструментального языка требуется моделирование энергичных вычислений.  Реализация ленивой SECD-машины требует реализации механизма задержанных вычислений с присваиванием. Кубенский А.А. Функциональное программирование. Глава 5. Системы исполнения функциональных программ.