使用数组在Haskell中解析方案向量

5
我是一名有帮助的助手,以下是您需要翻译的内容:

我正在尝试完成48小时内编写Scheme教程,并且作为一个新手,Haskell对我来说非常困难。

我目前正在解决一个问题,即我应该添加解析Scheme向量的功能(第3.4节练习2)。

我正在使用以下数据类型:

data LispVal = Atom String                  
         | List [LispVal]                   
         | Vector (Array Int LispVal)

为了解析,我正在寻找'#(',然后尝试解析向量内容,将其放入列表中并将该列表转换为数组。
我正在尝试使用一个已经存在并且正在使用的列表解析函数,但它将scheme列表解析为上面的LispVal List,我很难将其转换回常规列表。或者至少我认为我的问题是这样的。
lispValtoList :: LispVal -> [LispVal]
lispValtoList (List [a]) = [a]

parseVector :: Parser LispVal
parseVector = do string "#("
             vecArray <- parseVectorInternals       
             char ')'
             return $ Vector vecArray

parseVectorInternals :: Parser (Array Int LispVal)
parseVectorInternals = listToArray . lispValtoList . parseList  

listToArray :: [a] -> Array Int a
listToArray xs = listArray (0,l-1) xs
    where l = length xs

这里是列表解析器:

parseList :: Parser LispVal
parseList = liftM List $ sepBy parseExpr spaces

有没有修复这个问题的想法? 谢谢, Simon
-编辑- 这是我得到的编译错误:
无法匹配预期类型a -> LispVal'与推断类型Parser LispVal'在(.)的第二个参数中,即parseList'在(.)的第二个参数中,即lispValToList . parseList'在表达式中:listToArray . lispValToList . parseList

编译代码时是否出现任何错误消息?我认为parseVectorInternals的定义存在类型错误,但我不确定它是否是代码中唯一的错误。此外,parseVector的定义缩进很奇怪。 - Tsuyoshi Ito
我编辑了原始问题以包括错误 - parseVector缩进是渲染问题,在实际代码中很好。 - SimonBolivar
2个回答

6
你没有提供 lispValtoList 函数,但我认为它应该具有以下类型。
lispValtoList :: LispVal -> [LispVal]

这将使编译器认为parseList的类型是a -> LispVal,但实际上它是Parser LispVal,因此类似于P String -> [(LispVal,String)]
在将值放入列表之前,您必须提取解析的LispVal值。因此,parseVectorInternals可能应该像这样:
parseVectorInternals = do parsedList <- parseList 
                          let listOfLispVal = lispValtoList parsedList
                          return $ listToArray listOfLispVal

你可以写得更简洁,但这段代码尝试自行说明;)

Fraikin - 哎呀,现在它出现了。我想我当时所尝试的正是您所说的方法。lispValToList 的目的是从 LispVal List 中获取 haskell 列表。您是在说我错误地使用了 (.) 运算符吗? - SimonBolivar
1
@SimonBolivar 在这种情况下,(.) 运算符是不正确的,因为 parseList 的返回值与 lispValToList 的参数不匹配。parseList 返回一个单子,类似于保存值的盒子。在 do 块中的 <- 会将值从盒子中取出,然后您可以将该值传递给 lispValToList。(http://learnyouahaskell.com/ 有更好的单子解释) - Alex
1
@SimonBolivar 没错...问题出在组合运算符上。你需要将 lispValtoList 提升到组合函数中(如果你不知道提升是什么,请查看 Control.Monad)。看起来 John F. Miller 给出了可能的修正方案。 - Benoît Fraikin
@Alex 和 @Benoit,谢谢你们。我之前不明白 <- 是什么意思,现在知道它是 lifting 了。这帮助我理解了一些事情。 我之前写了一个很长的评论,说代码还是不能工作,直到我意识到我需要使用 return 将 Array 返回到 Parser monad 中。现在看起来它可以工作了。 - SimonBolivar

2

parseList 是类型为 parser LispVal 的单子,而函数 lispValtoList 需要一个普通的 LispVal,因此:

parseVectorInternals = listToArray . lispValtoList `liftM` parseList

如果你和我8周前一样正在阅读同一本书,以下内容也会对你有所帮助:
所有这些代码都是等效的:
parseVectorInternals = (listToArray . lispValtoList) `liftM` parseList
parseVectorInternals = liftM (listToArray . lispValtoList) parseList
parseVectorInternals = parseList >>= \listLispVal -> return listToArray (lispValtoList listLispVal)
parseVectorInternals = do 
  listLispVal <- parseList 
  return listToArray (lispValtoList listLispVal)

1
谢谢。虽然这很有帮助,但我认为你的解决方案可能存在一些问题。首先,parseVectorInternals 的类型是 Parser (Array Int LispVal),所以我需要一个 return 或等效物(另一个 liftM?)将其返回到正确的单子中。其次,第一个代码解决方案会出现错误:Precedence parsing error cannot mix '.' [infixr 9] and 'liftM' [infixl 9] in the same infix expression 我不确定该怎么做。但是,如果我在最后一行前加上 return $,那么带有 do 块的版本就可以工作了。 - SimonBolivar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接