在haskell中使用单子从头开始创建列表解析

pkbketx9  于 2022-12-04  发布在  其他
关注(0)|答案(1)|浏览(152)

我试着创建一个解释器作为一个练习,我遇到了,并且解释器也应该能够使列表理解,应该工作类似于原来的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

huwehgph

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),并添加一种更新局部环境的方法,如下所示:

withVar :: VName -> Value -> Comp a -> Comp a
withVar name value comp =
  Comp $ \ env -> runComp ((name, value) : env) comp

现在,您的目标是实现一个转换,一次简化一个子句的列表解析。例如,[e | x <- xs, y <- ys]相当于xs中每个值x[e | y <- ys]的连接。没有子句的解析就是一个简单的列表。
由于eval会产生一个Value,但这里我们总是使用列表,我将引入两个帮助函数。

needList :: Value -> Comp [Value]
needList (ListVal xs) = pure xs
needList _ = {- Raise an error. -}

另一个是eval的本地函数,它将计算一个列表解析,它可以递归地遍历子句,使用withVar绑定每个变量。
当到达结尾时,应评价理解的主要表现形式。

eval (Compr e0 clauses0) = ListVal <$> evalCompr clauses0
  where

    evalCompr :: [ExprClause] -> Comp [Value]

    evalCompr (ForCl vname e : clauses) = do

      list <- needList =<< eval e

      results <- for list $ \value -> do
        withVar vname value (evalCompr clauses)

      {- Collect results. (Hint: ‘>>=’.) -}

    evalCompr (IfCl e : clauses) =
      {- Filter results. (Hint: ‘guard’.) -}

    evalCompr [] = do
      value <- eval e0
      {- Produce one result. (Hint: ‘pure’.) -}

剩下的我就留给你自己来填了。

相关问题