Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/flutter/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Parsing Haskell:左偏/短路功能_Parsing_Haskell - Fatal编程技术网

Parsing Haskell:左偏/短路功能

Parsing Haskell:左偏/短路功能,parsing,haskell,Parsing,Haskell,两节课前,我们的教授向我们展示了一个解析器模块 代码如下: module Parser (Parser,parser,runParser,satisfy,char,string,many,many1,(+++)) where import Data.Char import Control.Monad import Control.Monad.State type Parser = StateT String [] runParser :: Parser a -> String -&g

两节课前,我们的教授向我们展示了一个解析器模块

代码如下:

module Parser (Parser,parser,runParser,satisfy,char,string,many,many1,(+++)) where

import Data.Char
import Control.Monad
import Control.Monad.State

type Parser = StateT String []

runParser :: Parser a -> String -> [(a,String)]
runParser = runStateT

parser :: (String -> [(a,String)]) -> Parser a
parser = StateT

satisfy :: (Char -> Bool) -> Parser Char
satisfy f = parser $ \s -> case s of
    [] -> []
    a:as -> [(a,as) | f a]

char :: Char -> Parser Char
char = satisfy . (==)

alpha,digit :: Parser Char
alpha = satisfy isAlpha
digit = satisfy isDigit

string :: String -> Parser String
string = mapM char

infixr 5 +++
(+++) :: Parser a -> Parser a -> Parser a
(+++) = mplus

many, many1 :: Parser a -> Parser [a]
many p = return [] +++ many1 p
many1 p = liftM2 (:) p (many p)
今天,他给我们布置了一个任务,介绍“一个左倾的,或者说是(+++)的短路版本”,叫做(runParser p s++runParser q s)

2) 使用最终的实现,如(+++)=mplus

以下是我的问题:

1) 如果我使用原始实现,模块将不会编译。错误:不在范围内:数据构造函数“解析器”。它使用(+++)+=mplus进行良好编译。使用通过使用最终实现避免的原始实现有什么错

2) 如何检查第一个解析器是否返回任何内容?类似(not)(isNothing(Parser$\s->runParser ps)的东西是否在正确的轨道上?看起来应该很简单,但我不知道

3) 一旦我弄清楚如何检查第一个解析器是否返回任何东西,如果我要将代码建立在最终实现的基础上,它会像这样简单吗

-- if p returns something then
p <++ q = mplus (Parser $ \s -> runParser p s) mzero
-- else
(<++) = mplus
——如果p返回某个值,那么
p runParser p s)mzero
--否则
(
  • 1) 正如@andras指出的,将
    Parser
    更改为
    Parser
  • 2+3)查看
    ++

p+++q=parser$\s->runParser p s++runParser q s

我们可以把它扩大一点,让事情更清楚

p +++ q = parser $ \s -> resP ++ resQ
  where resP = runParser p s
        resQ = runParser q s
这只需要一个小改动(在
resP+++resQ
中)即可使
  • 1) 正如@andras指出的,将
    Parser
    更改为
    Parser
  • 2+3)查看
    ++

p+++q=parser$\s->runParser p s++runParser q s

我们可以把它扩大一点,让事情更清楚

p +++ q = parser $ \s -> resP ++ resQ
  where resP = runParser p s
        resQ = runParser q s

这只需要一个小改动(在
resP+++resQ
中)就可以使
这里发生了很多事情!您所看到的是一个(非确定性)“解析器组合器库”,您可以在
parsec
attoparsec
uu parsinglib
中找到其他示例。。。这在Haskell中是一个相当普遍的想法,但肯定有点复杂。我将在这里稍微解释一下核心思想


第一个要考虑的概念是增量解析“步骤”的概念。这就是上面代码中由

Parser a
表示的内容,您可以将其视为“运行解析步骤,尝试解析
a
类型的内容”

“解析步骤”包括查看某种类型的输入字符流,提取表示某种
A
类型所需的字符数,然后返回新的
A
和未使用的剩余字符。在这种级别的描述中,用Haskell很容易写出这一点

String {- input stream -} -> (a {- fresh -}, String {- leftovers -})
这是解析器步骤的基础,值得注意的是,这是一种非常常见的习惯用法,在解析库之外,我们称之为
状态字符串a

newtype State s a = State { runState :: s -> (a, s) }

>>> :t runState (undefined :: State String a)
String -> (a, String)
我们也可以尝试以这种分解格式构造解析器步骤。考虑一个分析器,它消耗一个字符来创建<代码> int <代码> < /p>
parseInt :: String -> (Int, String)
parseInt (x:xs) = case x of
  '0' -> (0, xs)
  '1' -> (1, xs)
  ...
  '9' -> (9, xs)
  _   -> error "What! Failure!"
parseInt []     = error "What! Another failure!"

>>> parseInt "3leftovers"
(3, "leftovers")
我们马上就会发现这个模型太简单了——我们只能通过在运行时抛出错误来提供解析器失败。这很危险,并且表明我们对解析器的建模很差。不过,我们可以很容易地给它添加失败

-- String -> Maybe (a, String)

parseInt :: String -> Maybe (Int, String)
parseInt []     = Nothing
parseInt (x:xs) = case x of
  '0' -> Just (0, xs)
  '1' -> Just (1, xs)
  ...
  '9' -> Just (9, xs)
  _   -> Nothing

>>> parseInt "foo"
Nothing
这也是一个非常常见的Haskell主题,甚至在称为状态转换器或
StateT
的解析器之外。定义如下

newtype StateT s m a = StateT { runStateT :: s -> m (a, s) }

>>> :t runStateT (undefined :: StateT String Maybe a)
String -> Maybe (a, String)
它允许我们将
可能体现的
失败的概念与原始解析器中的
状态的概念结合起来。事实上,你的教授在他自己的版本中就是这么做的,除了没有使用
可能
他使用了
[]

>>> :t runStateT (undefined :: StateT String [] a)
String -> [(a, String)]
它既允许失败(如空列表
[]
),也允许多个同时成功。这就是为什么他的解析器是非确定性的——它收集并处理每个解析步骤的多次成功。这可能对内存非常不利,但这是一种谨慎使用的强大技术


不过,到目前为止,我所写的内容还缺少一些东西——我们如何将多个解析器组合在一起?例如,运行
parseInt
三次相当痛苦

parse3Ints :: String -> Maybe ((Int, Int, Int), String)
parse3Ints input = case parseInt input of
  Nothing -> Nothing
  Just (i1, input') -> case parseInt input' of
    Nothing -> Nothing
    Just (i2, input'') -> case parseInt input'' of
      Nothing -> Nothing
      Just (i3, leftovers) -> Just ((i1, i2, i3), leftovers)
啊。我们能做得更好吗?我们需要以某种方式将失败和
输入的传递连接在一起。幸运的是,这正是
Monad
s所做的,我们看到的所有三种数据类型都已经是
Monad
s,具有这些精确的行为

instance Monad m => Monad (StateT s m) where ...
instance Monad [] where ...
instance Monad Maybe where ...
请注意,
StateT
仅当其
m
参数为时才是
Monad
——这是因为它允许我们将
Monad
s分层在一起,因此它需要调用“内部”
Monad
,以便进行自己的排序

结果是,通过将这些简单函数转换为
StateT String,可能是一个
StateT String[]a
,我们可以立即使用
do
-表示法,让内置的
Monad
实例处理复杂的排序

parse3Ints :: StateT String Maybe (Int, Int, Int)
parse3Ints = do
  i1 <- parseInt
  i2 <- parseInt
  i3 <- parseInt
  return (i1, i2, i3)

-- or even
parse3Ints = liftM3 (,,) parseInt parseInt parseInt
因此,我们可以看到此代码的真正权重在
[]
实例上,因为
StateT
实例只是将责任转移到它的内部
Monad
m

MonadPlus[]
在做什么?它表示使用“或”组合失败的概念。如果列表中的
[]
是故障
Monad
,则
mzero
是立即故障,
mplus as bs
仅当
as
bs
都是故障。我们可以这样写

mplus mzero a = a
mplus a mzero = a
这是一种代数定律,人们可能认为它是MonadPlus的定义(虽然这里有一些争议,但它对这里的代码并不重要)


那么,通过使用
mplus
实例来组合解析器,发生了什么?简而言之,它允许您将“或”解析器放在一起,这样它们只会失败
mplus mzero a = a
mplus a mzero = a
(pa +++ pb +++ pc) is mzero ONLY if pa, pb, AND pc are mzero
instance MonadPlus Maybe where
  mzero                   = Nothing
  mplus Nothing x         = x
  mplus x Nothing         = x
  mplus (Just a) (Just b) = Just a
StateT sa <++ StateT sb = StateT $ \input -> case sa input of
  []   -> sb input
  els  -> els