Урок 7. Интерпретация и компиляция функциональных...
TRANSCRIPT
![Page 1: Урок 7. Интерпретация и компиляция функциональных программ](https://reader036.vdocuments.pub/reader036/viewer/2022082706/55cb0229bb61eb807a8b4668/html5/thumbnails/1.jpg)
1Кубенский А.А. Функциональное программирование.
Глава 5. Системы исполнения функциональных программ.
5.3. Eval / Apply-интерпретатор
Интерпретация, основанная на контексте.Контекст – иерархический ассоциативный список связей имен переменных со значениями.
type Context = [(String, Expr)]
assoc :: String -> Context -> Exprassoc x ((y,e):ctx) | x == y = e | otherwise = assoc x ctx
eval :: Context -> Expr -> Expr
apply :: Expr -> Expr -> Expr
-- вычисление значения выражения в контексте (приведение к СЗНФ):
-- вычисление результата применения функции к аргументу:
interpreter :: Expr -> Expr-- интерпретация:
interpreter = eval []
![Page 2: Урок 7. Интерпретация и компиляция функциональных программ](https://reader036.vdocuments.pub/reader036/viewer/2022082706/55cb0229bb61eb807a8b4668/html5/thumbnails/2.jpg)
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 _) = eeval _ e@(Logical _) = eeval _ (Function f) = Oper (arity f) f []eval ctx (Lambda x e) = Closure x e ctxeval _ e@(Closure _ _ _) = eeval _ 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) : ctxapply (Oper n f la) a | n == 1 = intrinsic f newListArgs | otherwise = Oper (n-1) f newListArgs where newListArgs = la ++ [a]
![Page 3: Урок 7. Интерпретация и компиляция функциональных программ](https://reader036.vdocuments.pub/reader036/viewer/2022082706/55cb0229bb61eb807a8b4668/html5/thumbnails/3.jpg)
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
![Page 4: Урок 7. Интерпретация и компиляция функциональных программ](https://reader036.vdocuments.pub/reader036/viewer/2022082706/55cb0229bb61eb807a8b4668/html5/thumbnails/4.jpg)
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)
![Page 5: Урок 7. Интерпретация и компиляция функциональных программ](https://reader036.vdocuments.pub/reader036/viewer/2022082706/55cb0229bb61eb807a8b4668/html5/thumbnails/5.jpg)
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 "+" = 2arity "-" = 2arity "*" = 2arity "/" = 2arity "EQ0" = 1arity "SUCC" = 1arity "PRED" = 1
![Page 6: Урок 7. Интерпретация и компиляция функциональных программ](https://reader036.vdocuments.pub/reader036/viewer/2022082706/55cb0229bb61eb807a8b4668/html5/thumbnails/6.jpg)
6Кубенский А.А. Функциональное программирование.
Глава 5. Системы исполнения функциональных программ.
Пример интерпретации простой программы
На языке Haskell: sqr :: Integer -> Integersqr x = x*xinterpret: sqr 3
В расширенном лямбда-исчислении: let sqr = λx.* x x in (sqr 3)
prog :: Exprprog = (Let "sqr" (Lambda "x" (Apply (Apply (Function "*“) (Variable "x")) (Variable "x"))) (Apply (Variable "sqr") (Integral 3)))
Представление в виде выражения типа Expr в языке Haskell:
interpreter progЧто получится в результате вызова ?
![Page 7: Урок 7. Интерпретация и компиляция функциональных программ](https://reader036.vdocuments.pub/reader036/viewer/2022082706/55cb0229bb61eb807a8b4668/html5/thumbnails/7.jpg)
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. Системы исполнения функциональных программ.
![Page 8: Урок 7. Интерпретация и компиляция функциональных программ](https://reader036.vdocuments.pub/reader036/viewer/2022082706/55cb0229bb61eb807a8b4668/html5/thumbnails/8.jpg)
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"))
![Page 9: Урок 7. Интерпретация и компиляция функциональных программ](https://reader036.vdocuments.pub/reader036/viewer/2022082706/55cb0229bb61eb807a8b4668/html5/thumbnails/9.jpg)
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)
![Page 10: Урок 7. Интерпретация и компиляция функциональных программ](https://reader036.vdocuments.pub/reader036/viewer/2022082706/55cb0229bb61eb807a8b4668/html5/thumbnails/10.jpg)
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 -> WHNFinterpret com = res where (res:_, _, _, _) = evaluate ([], [], [com], [])
то есть исходная программа помещается в регистр управления, а результат извлекается с вершины стека выражений.
![Page 11: Урок 7. Интерпретация и компиляция функциональных программ](https://reader036.vdocuments.pub/reader036/viewer/2022082706/55cb0229bb61eb807a8b4668/html5/thumbnails/11.jpg)
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 – это новая специальная команда условного перехода:
![Page 12: Урок 7. Интерпретация и компиляция функциональных программ](https://reader036.vdocuments.pub/reader036/viewer/2022082706/55cb0229bb61eb807a8b4668/html5/thumbnails/12.jpg)
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
![Page 13: Урок 7. Интерпретация и компиляция функциональных программ](https://reader036.vdocuments.pub/reader036/viewer/2022082706/55cb0229bb61eb807a8b4668/html5/thumbnails/13.jpg)
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, DUMPSTK, ENV, exp:(LApp x body):COM, DUMPexp':STK, ENV, (LApp x body):COM, DUMP[], (x,exp'):ENV, [body], (STK, ENV, COM):DUMP[body'], (x,exp'):ENV, [], (STK, ENV, COM):DUMPbody':STK, ENV, COM, DUMP
Последовательность состояний при исполнении рекурсивного блока должна отличатьсятем, что вычисление связываемых значений должно проводиться в уже пополненном контексте:
STK, ENV, (Letrec [(x1,e1),(x2,e2),...(xn,en)] body):COM, DUMPSTK, (x1,?):(x2,?):...(xn,?):ENV, en:...e2:e1:(LRApp body):COM, DUMPe1':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):DUMPbody':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)
![Page 14: Урок 7. Интерпретация и компиляция функциональных программ](https://reader036.vdocuments.pub/reader036/viewer/2022082706/55cb0229bb61eb807a8b4668/html5/thumbnails/14.jpg)
14Кубенский А.А. Функциональное программирование.
Глава 5. Системы исполнения функциональных программ.
Реализация псевдо-функции replaceValues.
e1':e2':...en':STK, (x1, ):(x2, ):...(xn, ):ENV, (LRApp body):COM, DUMP
?
?
?
e1'
e2'
en'
Если в выражениях e1', e2', en' имеются копии контекста, то замена значений с помощью псевдо-функции replaceValues повлияет сразу на все копии.
Реализованная SECD-машина – энергичная, однако, и здесь ленивый инструментальный язык реализации привносит «ленивость» в процесс интерпретации. Например, помещаемые в стекрезультаты вычислений функции intrinsic на самом деле будут получены, только если ониреально потребуются для представления результата.
![Page 15: Урок 7. Интерпретация и компиляция функциональных программ](https://reader036.vdocuments.pub/reader036/viewer/2022082706/55cb0229bb61eb807a8b4668/html5/thumbnails/15.jpg)
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)
Для того, чтобы избежать повторного вычисления одних и тех же значений, задержки помещаютсяв отдельную область памяти, а в стек вместо нее помещается указатель. Когда значение будетвычислено, результат должен быть помещен на место задержки с помощью присваивания.
![Page 16: Урок 7. Интерпретация и компиляция функциональных программ](https://reader036.vdocuments.pub/reader036/viewer/2022082706/55cb0229bb61eb807a8b4668/html5/thumbnails/16.jpg)
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. Системы исполнения функциональных программ.