简体   繁体   English

Haskell Parser无法正常工作

[英]Haskell Parser not working

I am trying to understand Parsers. 我试图了解解析器。 Therefore I have created my own parser. 因此,我创建了自己的解析器。 Unfortunately it does not work. 不幸的是,它不起作用。 Why? 为什么?

type Parser a = String -> [(a, String)]

preturn :: a -> Parser a 
preturn t = \inp -> [(t,inp)] 

pfailure :: Parser a 
pfailure = \inp -> []

pitem :: Parser Char
pitem = \inp -> case inp of 
    [] -> []
    (x:xs) -> [(x,xs)]


parse :: Parser a -> Parser a 
--parse :: Parser a -> String -> [(a,String)]

parse p inp = p inp

{-
combine :: Parser a -> Parser b -> Parser (a,b)
combine p1 p2 = \inp -> p2 t output
    where
        p1 inp = ([


-}
-- firstlast :: Parser (Char,Char)
firstlast = do 
    x <- pitem
    z <- pitem
    y <- pitem
    preturn (x,y)

another = do
    x <- pitem
    y <- pitem

Firstlast is supposed to take a string and return the first and third character. Firstlast应该采用字符串并返回第一个和第三个字符。 Unfortunately, it returns odd values, and it does not accept its type (Parser (Char,Char)) 不幸的是,它返回奇数值,并且不接受其类型(解析器(Char,Char))

For example, 例如,

*Main> firstlast "abc"
[(([('a',"bc")],[('a',"bc")]),"abc")]

What should happen is: 应该发生的是:

*Main> firstlast "abc"
[("ac",[])]

Please use code that compiles. 请使用可编译的代码。 Your another function does not. 您的another函数没有。

What's the problem? 有什么问题?

Your code for firstlast and another makes use of do -notation. 您的代码firstlastanother利用的do -notation。 And the way you're using pitem here, it looks as if you're expecting Parser to be a monad. 在这里使用pitem的方式,似乎就像您希望Parser成为monad。 But it isn't, at least not in the way you expect it to be. 但这不是,至少不是您期望的那样。

There is a monad instance pre-defined which make GHC think that Parser is a monad, namely 有一个预定义的monad实例,它使GHC认为Parser是monad,即

instance Monad ((->) r) where
  return = const
  f >>= k = \ r -> k (f r) r

What this instance says is that, for any type r the function type r -> ... can be considered a monad, namely by distributing the parameter everywhere. 这个实例说明的是,对于任何类型的r ,函数类型r -> ...都可以视为单子,即通过将参数分布到各处。 So returning something in this monad amounts to producing a value ignoring the parameter of type r , and binding a value means that you take r and pass it on both to the left and right computation. 因此,在此monad中返回某值等于忽略了r类型的参数而生成一个值,并绑定一个值意味着您将r传递给左右计算。

This is not what you want for a parser. 这不是解析器所需要的。 The input string will be distributed to all computations. 输入的字符串将分配给所有计算。 So each pitem will operate on the original input string. 因此,每个pitem将对原始输入字符串进行操作。 Furthermore, as 此外,作为

pitem :: String -> [(Char, String)]

the result of your monadic computation will be of type [(Char, String)] , so x and y are both of this type. 单子计算的结果将为[(Char, String)] ,因此xy均为该类型。 That's why you get the result 这就是为什么你得到结果

[(([('a',"bc")],[('a',"bc")]),"abc")]

You're calling pitem three times on the same input string. 您在同一输入字符串上调用pitem 3次。 You're putting two results in a pair, and you're preturn -ing the whole thing. 您将两个结果放在一起,然后就preturn了-整个过程。

How to fix it? 如何解决?

You need to define your own monad instance for the Parser type. 您需要为Parser类型定义自己的monad实例。 You cannot do that directly, because Parser is a type synonym, and type synonyms cannot be partially applied, so you cannot write 您不能直接执行此操作,因为Parser是类型同义词,并且类型同义词不能被部分应用,因此您不能编写

instance Monad Parser where
  ...

Instead, you have to wrap Parser in a new datatype or newtype: 相反,您必须将Parser包装为新的数据类型或新类型:

newtype Parser a = Parser { parse :: String -> [(a, String)] }

This gives you a constructor Parser and a function parse to convert between the unwrapped and wrapped parser types: 这为您提供了一个构造函数Parser和一个函数parse以在未包装和包装的解析器类型之间进行转换:

Parser :: String -> [(a, String)] -> Parser a
parse  :: Parser a -> String -> [(a, String)]

This implies you'll have to adapt your other functions. 这意味着您必须调整其他功能。 For example, preturn becomes 例如, preturn变成

preturn :: a -> Parser a 
preturn t = Parser (\inp -> [(t,inp)])

Change pfailure and pitem similarly. 同样地更改pfailurepitem Then, you have to define the Monad instance: 然后,您必须定义Monad实例:

instance Monad Parser where
  return = preturn
  (>>=)  = ... -- to be completed by you

The function (>>=) is not contained in your code above. 上面的代码中不包含函数(>>=) You'll want to implement the behaviour that the input is passed to the first parser, and for every result of that, the result and the remaining input are passed to the second argument of (>>=) . 您将想要实现将输入传递到第一个解析器的行为,并且对于每个结果,结果和其余输入将传递到(>>=)的第二个参数。 Once this is done, a call to parse firstlast "abc" will have the following result: 完成此操作后, parse firstlast "abc"的调用将具有以下结果:

[(('a','c'),"")]

which isn't quite what you want in your question, but I believe it's what you're actually after. 这不是您要的问题,但我相信这实际上是您要的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM