对于一项任务,我们必须实现类似于一个非常基本的性别解析器,例如输入:
"((a b) ((c d) e) f)"
它将返回:
[["a", "b"], [["c", "d"], "e"], "f"]
由于这是较大任务的一部分,因此解析器仅获得有效输入(匹配的parens& c)。我在Ruby中提出了以下解决方案:
def parse s, start, stop
tokens = s.scan(/#{Regexp.escape(start)}|#{Regexp.escape(stop)}|\w+/)
stack = [[]]
tokens.each do |tok|
case tok
when start
stack << []
when stop
stack[-2] << stack.pop
else
stack[-1] << tok
end
end
return stack[-1][-1]
end
这可能不是最佳解决方案,但它可以胜任。
现在,我对一个惯用的Haskell解决方案感兴趣的核心功能(即我不关心lexing或选择分隔符,考虑已经lexed输入会很好),如果可能只使用“核心”haskell ,没有像parsec这样的扩展或库。
请注意,这不是作业的一部分,我只是对Haskell的做事方式感兴趣。
答案 0 :(得分:6)
[["a", "b"], [["c", "d"], "e"], "f"]
haskell中没有有效类型(因为列表中的所有元素都必须在haskell中具有相同的类型),因此您需要为嵌套列表定义自己的数据结构,如下所示:
data NestedList = Value String | Nesting [NestedList]
现在,如果您有令牌列表,其中令牌被定义为data Token = LPar | RPar | Symbol String
,您可以将其解析为NestedList,如下所示:
parse = fst . parse'
parse' (LPar : tokens) =
let (inner, rest) = parse' tokens
(next, outer) = parse' rest
in
(Nesting inner : next, outer)
parse' (RPar : tokens) = ([], tokens)
parse' ((Symbol str) : tokens) =
let (next, outer) = parse' tokens in
(Value str : next, outer)
parse' [] = ([],[])
答案 1 :(得分:4)
答案 2 :(得分:2)
虽然像Parsec这样的鸽友解析器很好,但你并不需要那么强大的力量
对于这个简单的案例。解析的经典方法是使用ReadS
来自前奏曲。这也是你给Sexp类型a的方式
Read
实例。
至少对这种风格有点熟悉是件好事 解析,因为它有很多例子 标准库。
这是一个简单的解决方案,采用经典风格:
import Data.Char (isSpace)
data Sexp = Atom String | List [Sexp]
deriving (Eq, Ord)
instance Show Sexp where
show (Atom a ) = a
show (List es) = '(' : unwords (map show es) ++ ")"
instance Read Sexp where
readsPrec n (c:cs) | isSpace c = readsPrec n cs
readsPrec n ('(':cs) = [(List es, cs') |
(es, cs') <- readMany n cs]
readsPrec _ (')':_) = error "Sexp: unmatched parens"
readsPrec _ cs = let (a, cs') = span isAtomChar cs
in [(Atom a, cs')]
readMany :: Int -> ReadS [Sexp]
readMany _ (')':cs) = [([], cs)]
readMany n cs = [(e : es, cs'') | (e, cs') <- readsPrec n cs,
(es, cs'') <- readMany n cs']
isAtomChar :: Char -> Bool
isAtomChar '(' = False
isAtomChar ')' = False
isAtomChar c = not $ isSpace c
请注意Int
参数readsPrec
,
通常表示运算符优先级的,不是
在这里使用。