我正在制作一个String Parser,而我这样做的方式并不是最优的,因为它多次遍历String。我在Haskell遇到了一些我认为很奇怪的东西。
type LeftoverString = String
type Match = String
class Parser pars where
findMatch::pars -> String -> (Match,LeftoverString)
tokenName::pars -> String
isRecursive::pars -> Bool
recursiveParsers:: Parser p => pars -> [[p]]
recursiveFromMatch:: pars -> String -> String
isRecursive x = False
recursiveParsers x = []
recursiveFromMatch x y = y
type TokenName = String
data ParseTree = TokenLeaf Match TokenName | TokenNode TokenName [ParseTree] deriving Show
findFirstMatch::Parser a => [a] -> String ->(a,Match,LeftoverString)
findFirstMatch [] _ = error "could not match String"
findFirstMatch (x:xs) str | match /= "" = (x,match,leftovers)
| otherwise = findFirstMatch xs str
where
(match,leftovers) = findMatch x str
generateTree::Parser a => a -> String -> ParseTree
generateTree pars str = head $ generateTree' [[pars]] str
test::Parser a => a -> [[a]]
test x = recursiveParsers x
generateTree'::Parser a => [[a]] -> String -> [ParseTree]
generateTree' [] _ = []
generateTree' (x:xs) str | isRecursive parser = recTok:parseTreeRest
| otherwise = leafTok:parseTreeRest
where
(parser,match,leftovers)=findFirstMatch x str
tok = tokenName parser
parseTreeRest = (generateTree' xs leftovers)
recTok = TokenNode tok $ generateTree' (test parser) (recursiveFromMatch parser match)
leafTok = TokenLeaf match tok
可以看出,Parser中的recursiveParsers
返回一个Parser实例列表列表
在代码中你看到了:
test::Parser a => a -> [[a]]
test x = recursiveParsers x
这用于recTok中generateTree'
的位置
可以看出,test(基本上)是recursiveParsers x
然而 - 在哪里,如果我替换:
recTok = TokenNode tok $ generateTree' (test parser) (recursiveFromMatch parser match)
带
recTok = TokenNode tok $ generateTree' (recursiveParsers parser) (recursiveFromMatch parser match)
编译器会生成不明确类型的错误。
错误是:
StringParser.hs:42:50:
Could not deduce (Parser a0) arising from a use of generateTree'
from the context (Parser a)
bound by the type signature for
generateTree' :: Parser a => [[a]] -> String -> [ParseTree]
at StringParser.hs:34:16-57
The type variable `a0' is ambiguous
In the second argument of `($)', namely
`generateTree'
(recursiveParsers parser) (recursiveFromMatch parser match)'
In the expression:
TokenNode tok
$ generateTree'
(recursiveParsers parser) (recursiveFromMatch parser match)
In an equation for `recTok':
recTok
= TokenNode tok
$ generateTree'
(recursiveParsers parser) (recursiveFromMatch parser match)
为什么编译器需要函数recursiveParsers的同义词才能工作?
第二个问题,我相信我在尝试使用Class强制解析器具有某些功能时完全做错了吗?还有另一种方法吗?
答案 0 :(得分:3)
据我所知,您正在尝试使用Haskell类型类,就好像它们是OOP类一样。他们不是。
粗略地说,类型类是类型的集合。它最接近的OOP类似于Java风格的接口(不是类)。与Java接口一样,类型类本身几乎没有用,除非在其他地方有一些实现(instance
s)(在Java中,我们需要一些类来实现接口)。
注意复数"实现"。大多数情况下,只有在我们能够想到满足其界面的几种方法时才使用类型类。类型类Num
由Int, Integer, Float, Double, ...
居住; Show
的课程String, Char, Bool, Int, ...
。
如果我们只有一个合理的实现,那么就不需要使用类型类。
在您的具体示例中:您期望您的类型类的实现(复数)是什么?
另外,我相信你误解了方法的类型
recursiveParsers:: Parser p => pars -> [[p]]
在类型类之外,可以使用签名
访问此方法recursiveParsers:: (Parser p1, Parser p2) => p1 -> [[p2]]
我认为错误:上述意味着该方法的调用者可以将任何解析器p1
更改为解析器列表[[p2]]
,其中都 {{1} }和p1
由调用者选择。特别是,不意味着它返回"某些解析器"的列表列表。
对此类方法的任何调用都需要在其上下文中指定选择p2
的内容。
除了这个技术事实,我认为应该重新设计整个方法。