Your SlideShare is downloading. ×
2012 04 13_functional_programming_lecture07
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×

Introducing the official SlideShare app

Stunning, full-screen experience for iPhone and Android

Text the download link to your phone

Standard text messaging rates apply

2012 04 13_functional_programming_lecture07

464
views

Published on


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

No Downloads
Views
Total Views
464
On Slideshare
0
From Embeds
0
Number of Embeds
2
Actions
Shares
0
Downloads
7
Comments
0
Likes
1
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

  • 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. 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. 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. Энергичный 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. Реализация встроенных функций 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. Пример интерпретации простой программы На языке 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. Пример интерпретации простой программы 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. Пример интерпретации простой программы (продолжение)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. Глава 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. Работа 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. Уравнения функции 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. Уравнения функции 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. Интерпретация простого и рекурсивного блоков. Рассмотрим последовательность состояний при исполнении простого блока: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. Реализация псевдо-функции 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. Ленивая версия 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. Реализация рекурсивного блока в ленивой версии 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