Я пишу оценщик для небольшого языка выражений, но я застрял на конструкции LetRec
.
Это язык:
data Expr = Var Nm | Lam (Nm,Ty) Expr | App Expr Expr
| Val Int | Add Expr Expr | If Expr Expr Expr
| Let Nm Expr Expr
| LetRec [((Nm,Ty),Expr)] Expr
И это оценщик до сих пор:
type Env = [ (Nm, Value) ]
data Value = Clos Env Expr
| Vint Int
deriving Show
eval :: Env -> Expr -> Value
eval _ (Val n) = Vint n
eval env (Add e1 e2) = Vint (n1 + n2)
where
Vint n1 = eval env e1
Vint n2 = eval env e2
eval env (If e e1 e0) = if n==0 then eval env e0 else eval env e1
where
Vint n = eval env e
eval env (Var x) = case lookup x env of
Nothing -> error (x)
Just v -> v
eval env (Lam x e) = Clos env (Lam x e)
eval env (App e1 e2) = case v1 of
Clos env1 (Lam (x,t) e) -> eval ((x,v2):env1) e
where
v1 = eval env e1
v2 = eval env e2
eval env (Let x e1 e2) = eval env' e2
where
env' = (x,v) : env
v = eval env e1
eval env (LetRec [((x,t),e)] e1) = eval env' e1
where
env' = env ++ map (\(v,e) -> (v, eval env' e)) [(x,e)]
Это моя тестовая функция, которую я хочу оценить:
t1 = LetRec
[ (("not", INT:->INT), Lam ("i",INT) $ If (Var "i")
(Val 0)
(Val 1))
, (("even", INT:->INT), Lam ("i",INT) $ If (Var "i")
(App (Var "not")
(App (Var "odd")
(Var "i" `Add` Val (-1))))
(Val 1))
, (("odd", INT:->INT), Lam ("i",INT) $ If (Var "i")
(App (Var "not")
(App (Var "even")
(Var "i" `Add` Val (-1))))
(Val 0))
]
(App (Var "odd") (Val 7))
Я хочу преуспеть в тесте t1 выше. Однако в LetRec есть ошибка, которую я решил.
Обратите внимание, что ваша тестовая программа неверна. Вы не хотите применять «не». Число n
четное, если n-1
НЕчетное, а не если оно НЕ НЕЧЕТНОЕ. Итак, должно быть:
t1 = LetRec
[ (("even", INT:->INT), Lam ("i",INT) $ If (Var "i")
(App (Var "odd")
(Var "i" `Add` Val (-1)))
(Val 1))
, (("odd", INT:->INT), Lam ("i",INT) $ If (Var "i")
(App (Var "even")
(Var "i" `Add` Val (-1)))
(Val 0))
]
(App (Var "odd") (Val 7))
Ваш случай LetRec
почти правильный. По какой-то причине вы только что написали его для обработки только одноэлементных списков. Кроме того, вы хотите поместить привязки letrec
в начало списка привязок среды, а не в конец, иначе привязки за пределами letrec
будут иметь приоритет. Пытаться:
eval env (LetRec bnds body) = v
where v = eval env' body
env' = [(n, eval env' e) | ((n,_),e) <- bnds] ++ env
Вот полная программа. При запуске он должен печатать Vint 1
:
type Nm = String
data Ty = INT | Ty :-> Ty
deriving (Show)
data Expr = Var Nm | Lam (Nm,Ty) Expr | App Expr Expr
| Val Int | Add Expr Expr | If Expr Expr Expr
| Let Nm Expr Expr
| LetRec [((Nm,Ty),Expr)] Expr
deriving (Show)
type Env = [ (Nm, Value) ]
data Value = Clos Env Expr
| Vint Int
deriving (Show)
eval :: Env -> Expr -> Value
eval _ (Val n) = Vint n
eval env (Add e1 e2) = Vint (n1 + n2)
where
Vint n1 = eval env e1
Vint n2 = eval env e2
eval env (If e e1 e0) = if n==0 then eval env e0 else eval env e1
where
Vint n = eval env e
eval env (Var x) = case lookup x env of
Nothing -> error (x ++ " not defined")
Just v -> v
eval env e@(Lam _ _) = Clos env e
eval env (App e1 e2) = case v1 of
Clos env1 (Lam (x,t) e) -> eval ((x,v2):env1) e
where
v1 = eval env e1
v2 = eval env e2
eval env (Let x e1 e2) = eval env' e2
where
env' = (x,v) : env
v = eval env e1
eval env (LetRec bnds body) = eval env' body
where env' = [(n, eval env' e) | ((n,_),e) <- bnds] ++ env
t1 :: Expr
t1 = LetRec
[ (("even", INT:->INT), Lam ("i",INT) $ If (Var "i")
(App (Var "odd")
(Var "i" `Add` Val (-1)))
(Val 1))
, (("odd", INT:->INT), Lam ("i",INT) $ If (Var "i")
(App (Var "even")
(Var "i" `Add` Val (-1)))
(Val 0))
]
(App (Var "odd") (Val 7))
main :: IO ()
main = print (eval [] t1)
Попробуйте еще раз, как вы написали, и я получил ошибку «Несуществующие части в функции eval».
Я предполагаю, что вы имеете в виду «Неисчерпывающие шаблоны в функции eval». Если это так, вы должны запускать код, отличный от того, что вы написали выше, потому что все шаблоны присутствуют (кроме случаев, когда вы пытаетесь применить незамыкание, но сообщение об ошибке будет другим). Попробуйте скомпилировать с помощью -Wall
и прочитайте предупреждения, чтобы увидеть, какие шаблоны вам не хватает.
eval _ exp = error $ show exp
должен помочь вам на данный момент.
Я пытался. Однако я получил следующую ошибку. LetRec [(("even",INT :-> INT),Lam ("i",INT) (If (Var "i") (App (Var "odd") (Add (Var "i") (Val (-1)))) (Val 1))),(("odd",INT :-> INT),Lam ("i",INT) (If (Var "i") (App (Var "even") (Add (Var "i") (Val (-1)))) (Val 0)))] (App (Var "odd") (Val 7)) CallStack (from HasCallStack): error, called at <interactive>:34:14 in interactive:Ghci3324
Я не пользуюсь JupyterLab, поэтому не знаю, как вам с этим помочь. Я добавил копию полной программы, которую использовал для тестирования. Он компилируется и запускается без ошибок и печатает Vint 1
для меня.
Большое спасибо за подробное объяснение!! Я погуглил и решил проблему Jupyterlab, используя информацию
Каков ваш конкретный вопрос?