我试着创建一个解释器作为一个练习,我遇到了,并且解释器也应该能够使列表理解,应该工作类似于原来的Haskell列表理解.然而,在这个版本的练习中,列表理解应该作为一个eval
-函数的一部分来计算,其签名如下:eval :: Expr -> Comp Value
地点
data Expr =
Const Value
| Var VName
| Call FuncName [Expr]
| List [Expr]
| Compr Expr [ExprClause]
deriving (Eq, Show, Read)
data ExprClause =
ForCl VName Expr
| IfCl Expr
deriving (Eq, Show, Read)
此外,Comp
指的是一个以newtype Comp a = Comp {runComp :: Env -> (Either RunErr a, [String]) }
为构造函数的单子。
还有哪里
data Value =
| IntVal Int
| StringVal String
| ListVal [Value]
deriving (Eq, Show, Read)
data VName = String
当eval
-函数被赋予Compr eBody [ForCl VName Expr]
时,ForCl
的每次出现将对应于由VName
命名的变量的for循环,例如eval (Compr (2*x) [ForCl (Var "x") (Call "range" (Const (IntVal 10)))]
将等效于python列表压缩[2*x for x in range 10]
我可以创建如下所示的函数:
eval (Compr eBody [(ForCl vname e)]) =
let body = [(eval eBody) | eBody <- [e]]
in
do
values <- sequence $ body
return (ListVal values)
虽然它确实编译了,但它没有考虑vname
,这样VName
中的输入就指向了eBody
中包含的同一个变量(可能)。这可能需要比我的解决方案中所暗示的更多的Monadic计算和参与。然而,这是我不确定的。
你能给予我一些想法,如何使之前提到的'共同引用',这样我也可能能够处理几个变量包含在eBody
和与每个他们的ForCl
。
1条答案
按热度按时间huwehgph1#
[(eval e0) | e0 <- [e]]
等价于[eval e]
,这不是您想要的。您在这里根本没有使用原始的e0
,而是使用了另一个同名的局部变量。既然你还没有给出
Env
的定义,我就假设它是type Env = [(VName, Value)]
。你写data VName = String
的地方,我相信你指的是type VName = String
。你写CCFor
的地方,我假设你指的是ForCl
。如果您还没有这样做,为了实现局部变量绑定,您可以包含
eval (Var vname)
的大小写(例如使用lookup
),并添加一种更新局部环境的方法,如下所示:现在,您的目标是实现一个转换,一次简化一个子句的列表解析。例如,
[e | x <- xs, y <- ys]
相当于xs
中每个值x
的[e | y <- ys]
的连接。没有子句的解析就是一个简单的列表。由于
eval
会产生一个Value
,但这里我们总是使用列表,我将引入两个帮助函数。另一个是
eval
的本地函数,它将计算一个列表解析,它可以递归地遍历子句,使用withVar
绑定每个变量。当到达结尾时,应评价理解的主要表现形式。
剩下的我就留给你自己来填了。