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

16
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 []

Upload: -mydls

Post on 12-Aug-2015

122 views

Category:

Education


2 download

TRANSCRIPT

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

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. Интерпретация и компиляция функциональных программ

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. Интерпретация и компиляция функциональных программ

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. Интерпретация и компиляция функциональных программ

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. Интерпретация и компиляция функциональных программ

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. Интерпретация и компиляция функциональных программ

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. Интерпретация и компиляция функциональных программ

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. Интерпретация и компиляция функциональных программ

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. Интерпретация и компиляция функциональных программ

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. Интерпретация и компиляция функциональных программ

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. Интерпретация и компиляция функциональных программ

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. Интерпретация и компиляция функциональных программ

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. Интерпретация и компиляция функциональных программ

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. Интерпретация и компиляция функциональных программ

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. Интерпретация и компиляция функциональных программ

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. Интерпретация и компиляция функциональных программ

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. Системы исполнения функциональных программ.