Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/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
Haskell 如何在Parsec中使用Control.Monad.State?_Haskell_Parsec - Fatal编程技术网

Haskell 如何在Parsec中使用Control.Monad.State?

Haskell 如何在Parsec中使用Control.Monad.State?,haskell,parsec,Haskell,Parsec,我很惊讶,我找不到这方面的任何信息。我一定是唯一有麻烦的人了 那么,假设我有一个仪表盘计数器。我希望它计算字符串中破折号的数量,然后返回字符串。假设我给出了一个使用parsec的状态处理不起作用的示例。因此,这应该是可行的: dashCounter = do str <- many1 dash count <- get return (count,str) dash = do char '-' modify (+1) 好吧,这是有道理的。它应该返回状态和字符

我很惊讶,我找不到这方面的任何信息。我一定是唯一有麻烦的人了

那么,假设我有一个仪表盘计数器。我希望它计算字符串中破折号的数量,然后返回字符串。假设我给出了一个使用parsec的状态处理不起作用的示例。因此,这应该是可行的:

dashCounter = do
  str <- many1 dash
  count <- get
  return (count,str)


dash = do
  char '-'
  modify (+1)
好吧,这是有道理的。它应该返回状态和字符串。酷

>parse dashCounter "" "----"

<interactive>:1:7:
    No instance for (Control.Monad.State.Class.MonadState
                       t0 Data.Functor.Identity.Identity)
      arising from a use of `dashCounter'
    Possible fix:
      add an instance declaration for
      (Control.Monad.State.Class.MonadState
         t0 Data.Functor.Identity.Identity)
    In the first argument of `parse', namely `dashCounter'
    In the expression: parse dashCounter "" "----"
    In an equation for `it': it = parse dashCounter "" "----"
但它给出了一个类似的错误

<interactive>:1:7:
    No instance for (Control.Monad.State.Class.MonadState Int m0)
      arising from a use of `dashCounter'
    Possible fix:
      add an instance declaration for
      (Control.Monad.State.Class.MonadState Int m0)
    In the first argument of `runPT', namely `dashCounter'
    In the expression: runPT dashCounter (0 :: Int) "" "----"
    In an equation for `it':
        it = runPT dashCounter (0 :: Int) "" "----"
:1:7:
没有(Control.Monad.State.Class.MonadState Int m0)的实例
因使用“dashCounter”而产生
可能的解决方案:
为添加实例声明
(Control.Monad.State.Class.MonadState Int m0)
在'runPT'的第一个参数中,即'dashCounter'
在表达式中:runPT dashCounter(0::Int)“”中
在“it”的方程式中:
it=runPT dashCounter(0::Int)“”------
我觉得我应该在它上面运行state,或者应该有一个函数已经在内部运行了,但是我似乎不知道从这里可以走到哪里

编辑:我应该更清楚地指定,我不想使用parsec的状态处理。原因是我有一种感觉,我不想让它的回溯影响它收集到的东西,因为我正准备解决这个问题

然而,McCann先生已经弄明白了这应该如何结合在一起,最终的代码是这样的:

dashCounter = do
  str <- many1 dash
  count <- get
  return (count,str)

dash = do
  c <- char '-'
  modify (+1)
  return c

test = runState (runPT dashCounter () "" "----------") 0
dashCounter=do

str如果您想使用Parsec提供的用户状态组件作为内置功能,那么您可以使用
getState
modifyState
一元函数

虽然使用
dash
的返回似乎没有什么用处,但我还是尽量忠实于您的示例程序

import Text.Parsec

dashCounter :: Parsec String Int (Int, [()])
dashCounter = do
  str <- many1 dash
  count <- getState
  return (count,str)

dash :: Parsec String Int ()
dash = do
  char '-'
  modifyState (+1)

test = runP dashCounter 0 "" "---"
import Text.Parsec
dashCounter::Parsec字符串Int(Int,[()]))
dashCounter=do

str你实际上遇到了很多问题,所有这些问题在第一次出现时都相对不明显

从最简单的开始:
dash
返回
()
,这似乎不是您想要的,因为您正在收集结果。您可能需要类似于
dash=char'-'parsecss()a->SourceName->s->ParseError a
。与monad transformers一样,
Parsec
是应用于标识monad的
ParsecT
的同义词。虽然
ParsecT
确实提供了用户状态,但您显然不想使用它,而且
ParsecT
无论如何都不会给出
MonadState
的实例。这里是唯一相关的实例:
MonadState s m=>MonadState s(ParsecT s'u m)
。换句话说,要将解析器视为状态单子,必须将
ParsecT
应用于其他一些状态单子

这就给我们带来了下一个问题:模糊性。您使用了很多类型类方法,但没有类型签名,因此可能会遇到GHC不知道您实际需要的类型的情况,因此您必须告诉它

现在,作为一个快速解决方案,让我们首先定义一个类型同义词,为我们想要的monad transformer堆栈命名:

type StateParse a = ParsecT String () (StateT Int Identity) a
dashCounter
相关的类型签名:

dashCounter :: StateParse (Int, String)
dashCounter = do str <- many1 dash
                 count <- get
                 return (count,str)
现在,在GHCi中:

Main> runStateParse dashCounter "" "---" 0
(Right (3,"---"),3)

另外,请注意,在转换器堆栈周围使用
newtype
而不仅仅是类型同义词是非常常见的。在某些情况下,这有助于解决歧义问题,并明显避免以巨大的类型签名结束。

虽然这些答案解决了这一特定问题,但它们通过这样的方法忽略了更严重的潜在问题。我想在这里为其他任何看到这个答案的人描述一下

用户状态和使用StateT转换器之间存在差异。内部用户状态在回溯时重置,但StateT未重置。考虑下面的代码。如果有破折号,我们想在计数器上加一个,如果有加号,我们想在计数器上加两个。它们产生不同的结果

可以看出,使用内部状态和连接StateT变压器都能提供正确的结果。后者的代价是必须明确地提升操作,并且对类型更加小心

import Text.Parsec hiding (State)
import Control.Monad.State
import Control.Monad.Identity

f :: ParsecT String Int Identity Int
f = do
  try dash <|> plus
  getState

dash = do
  modifyState (+1)
  char '-'
plus = do
  modifyState (+2)
  char '+'

f' :: ParsecT String () (State Int) ()
f' = void (try dash' <|> plus')

dash' = do
  modify (+1)
  char '-'

plus' = do
  modify (+2)
  char '+'

f'' :: StateT Int (Parsec String ()) ()
f'' = void (dash'' <|> plus'')

dash'' :: StateT Int (Parsec String ()) Char
dash'' = do
  modify (+1)
  lift $ char '-'

plus'' :: StateT Int (Parsec String ()) Char
plus'' = do
  modify (+2)
  lift $ char '+'
看看这个:
runStateParse p sn inp count = runIdentity $ runStateT (runPT p () sn inp) count
Main> runStateParse dashCounter "" "---" 0
(Right (3,"---"),3)
import Text.Parsec hiding (State)
import Control.Monad.State
import Control.Monad.Identity

f :: ParsecT String Int Identity Int
f = do
  try dash <|> plus
  getState

dash = do
  modifyState (+1)
  char '-'
plus = do
  modifyState (+2)
  char '+'

f' :: ParsecT String () (State Int) ()
f' = void (try dash' <|> plus')

dash' = do
  modify (+1)
  char '-'

plus' = do
  modify (+2)
  char '+'

f'' :: StateT Int (Parsec String ()) ()
f'' = void (dash'' <|> plus'')

dash'' :: StateT Int (Parsec String ()) Char
dash'' = do
  modify (+1)
  lift $ char '-'

plus'' :: StateT Int (Parsec String ()) Char
plus'' = do
  modify (+2)
  lift $ char '+'
*Main> runParser f 0 "" "+"
Right 2
*Main> flip runState 0 $ runPT f' () "" "+"
(Right (),3)
*Main> runParser (runStateT f'' 0) () "" "+"
Right ((),2)