2012 04 13_functional_programming_lecture07

682 views

Published on

0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
682
On SlideShare
0
From Embeds
0
Number of Embeds
277
Actions
Shares
0
Downloads
9
Comments
0
Likes
1
Embeds 0
No embeds

No notes for slide

2012 04 13_functional_programming_lecture07

  1. 1. 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 []Кубенский А.А. Функциональное программирование.Глава 5. Системы исполнения функциональных программ. 1
  2. 2. 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]Кубенский А.А. Функциональное программирование.Глава 5. Системы исполнения функциональных программ. 2
  3. 3. 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Кубенский А.А. Функциональное программирование.Глава 5. Системы исполнения функциональных программ. 3
  4. 4. Энергичный 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. Системы исполнения функциональных программ. 4
  5. 5. Реализация встроенных функций apply (Oper nArgs f argsList) arg | nArgs == 1 = intrinsic f newList | otherwise = Oper (nArgs-1) f newList where newListArgs = argsList ++ [arg] 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) eval _ (Function f) = Oper (arity f) f [] arity "+" = 2 arity "-" = 2 arity "*" = 2 arity "/" = 2 arity "EQ0" = 1 arity "SUCC" = 1 arity "PRED" = 1Кубенский А.А. Функциональное программирование.Глава 5. Системы исполнения функциональных программ. 5
  6. 6. Пример интерпретации простой программы На языке Haskell: sqr :: Integer -> Integer sqr x = x*x interpret: sqr 3 В расширенном лямбда-исчислении: let sqr = λx.* x x in (sqr 3) Представление в виде выражения типа Expr в языке Haskell: prog :: Expr prog = (Let "sqr" (Lambda "x" (Apply (Apply (Function "*“) (Variable "x")) (Variable "x"))) (Apply (Variable "sqr") (Integral 3))) Что получится в результате вызова interpreter prog ?Кубенский А.А. Функциональное программирование.Глава 5. Системы исполнения функциональных программ. 6
  7. 7. Пример интерпретации простой программы prog = (Let "sqr" (Lambda "x" (Apply (Apply (Function "*") (Variable "x")) (Variable "x"))) (Apply (Variable "sqr") (Integral 3)))interpreter progeval [] progeval [] (Let "sqr" (Lambda ...) (Apply ...))apply (Closure "sqr" (Apply ...) []) (eval [] (Lambda "x" ...))apply (Closure "sqr" (Apply ...) []) (Closure "x" (Apply ...) [])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. Системы исполнения функциональных программ. 7
  8. 8. Пример интерпретации простой программы (продолжение)apply (eval [("x",(Integral 3)] (Apply (Function "*") (Variable "x"))) (eval [("x",(Integral 3)] (Variable "x"))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)Кубенский А.А. Функциональное программирование.Глава 5. Системы исполнения функциональных программ. 8
  9. 9. Глава 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 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 WHNF = C_Int Integer | C_Bool Bool | Closure String Environment Command | Oper String Int [WHNF] data SECD = (Stack, Environment, Control, Dump)Кубенский А.А. Функциональное программирование.Глава 5. Системы исполнения функциональных программ. 9
  10. 10. Работа 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], []) то есть исходная программа помещается в регистр управления, а результат извлекается с вершины стека выражений.Кубенский А.А. Функциональное программирование.Глава 5. Системы исполнения функциональных программ. 10
  11. 11. Уравнения функции 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)Исполнение команды условного вычисления If: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)Здесь Select – это новая специальная команда условного перехода:data Command = ... | Select Command CommandКубенский А.А. Функциональное программирование.Глава 5. Системы исполнения функциональных программ. 11
  12. 12. Уравнения функции evaluate для более сложных команд.Применение функции:evaluate (s, e, (Apply f a):c, d) = evaluate (s, e, a:f:App:c, d)Здесь App – это новая специальная команда применения функции: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)Аналогично для простого (нерекурсивного) блока:data Command = ... | LApp String Commandevaluate (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)Кубенский А.А. Функциональное программирование.Глава 5. Системы исполнения функциональных программ. 12
  13. 13. Интерпретация простого и рекурсивного блоков. Рассмотрим последовательность состояний при исполнении простого блока: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, (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 pairsevaluate (s, e, (LRApp n body):c, d) = evaluate ([], replaceValues n s e, [body], (drop n s, drop n e, c):d)Кубенский А.А. Функциональное программирование.Глава 5. Системы исполнения функциональных программ. 13
  14. 14. Реализация псевдо-функции replaceValues.e1:e2:...en:STK, (x1, ):(x2, ):...(xn, ):ENV, (LRApp body):COM, DUMP e1 ? e2 ? en ? Если в выражениях e1, e2, en имеются копии контекста, то замена значений с помощью псевдо-функции replaceValues повлияет сразу на все копии. Реализованная SECD-машина – энергичная, однако, и здесь ленивый инструментальный язык реализации привносит «ленивость» в процесс интерпретации. Например, помещаемые в стек результаты вычислений функции intrinsic на самом деле будут получены, только если они реально потребуются для представления результата.Кубенский А.А. Функциональное программирование.Глава 5. Системы исполнения функциональных программ. 14
  15. 15. Ленивая версия SECD-машины.Введем понятие задержанного результата для того, чтобы задержатьвычисление значения выражения до момента первого обращения к нему.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)Для того, чтобы избежать повторного вычисления одних и тех же значений, задержки помещаютсяв отдельную область памяти, а в стек вместо нее помещается указатель. Когда значение будетвычислено, результат должен быть помещен на место задержки с помощью присваивания.Кубенский А.А. Функциональное программирование.Глава 5. Системы исполнения функциональных программ. 15
  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. Системы исполнения функциональных программ. 16

×