1 / 16

Кубенский А.А. Функциональное программирование.

5.3. Eval / Apply- интерпретатор. Интерпретация, основанная на контексте. Контекст – иерархический ассоциативный список связей имен переменных со значениями. type Context = [(String, Expr)]. assoc :: String -> Context -> Expr.

hayes-bauer
Download Presentation

Кубенский А.А. Функциональное программирование.

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

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

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

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

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

  5. Реализация встроенных функций apply (Oper nArgs f argsList) arg | nArgs == 1 = intrinsic f newList| otherwise = Oper (nArgs-1) f newListwhere 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. Системы исполнения функциональных программ.

  6. Пример интерпретации простой программы На языке Haskell: sqr :: Integer -> Integersqr x = x*xinterpret: sqr 3 В расширенном лямбда-исчислении: let sqr = λx.* x x in (sqr 3) Представление в виде выражения типа Exprв языке Haskell: prog :: Exprprog = (Let "sqr" (Lambda "x" (Apply (Apply (Function "*“) (Variable "x")) (Variable "x"))) (Apply (Variable "sqr") (Integral 3))) Что получится в результате вызова interpreter prog ? Кубенский А.А. Функциональное программирование. Глава 5. Системы исполнения функциональных программ.

  7. Пример интерпретации простой программы prog = (Let "sqr" (Lambda "x" (Apply (Apply (Function "*") (Variable "x")) (Variable "x"))) (Apply (Variable "sqr") (Integral 3))) interpreter prog eval [] prog eval [] (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. Системы исполнения функциональных программ.

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

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

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

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

  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 Command 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) Кубенский А.А. Функциональное программирование. Глава 5. Системы исполнения функциональных программ.

  13. Интерпретация простого и рекурсивного блоков. Рассмотрим последовательность состояний при исполнении простого блока: 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, (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 evaluate (s, e, (LRApp n body):c, d) = evaluate ([], replaceValues n s e, [body], (drop n s, drop n e, c):d) Кубенский А.А. Функциональное программирование. Глава 5. Системы исполнения функциональных программ.

  14. Реализация псевдо-функции replaceValues. e1':e2':...en':STK, (x1, ):(x2, ):...(xn, ):ENV, (LRApp body):COM, DUMP e1' ? e2' ? en' ? Если в выражениях e1', e2', en'имеются копии контекста, то замена значений с помощью псевдо-функции replaceValuesповлияет сразу на все копии. Реализованная SECD-машина – энергичная, однако, и здесь ленивый инструментальный язык реализации привносит «ленивость» в процесс интерпретации. Например, помещаемые в стекрезультаты вычислений функции intrinsicна самом деле будут получены, только если ониреально потребуются для представления результата. Кубенский А.А. Функциональное программирование. Глава 5. Системы исполнения функциональных программ.

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

  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