替代方案中的“some”和“many”有什么用处?(疑问句)

53

AlternativeApplicative的扩展,声明了empty<|>和这两个函数:

一个或多个:

some :: f a -> f [a]

零个或多个:

many :: f a -> f [a]
如果定义了,somemany应该是方程的最小解:

If defined, some and many should be the least solutions of the equations:

some v = (:) <$> v <*> many v

many v = some v <|> pure []
我找不到somemany的定义。它们有什么意义和实际用途吗?它们是否被使用?我无法从这个定义中理解它们的目的。 更新:我不是在问什么是Alternative,只是想知道somemany是什么。

2
虽然这里有一些不错的答案,但是这个问题可能是这个这个这个的重复。 - is7s
它们就是它所说的:用于多次应用(例如解析器)的组合器,将结果收集在列表中。我提供了一个基本的示例,其中定义很容易理解。 - Will Ness
1
@WillNess 谢谢,我没想到解析器。但仍然困扰我的是为什么它们被包含在Alternative中,因为这些函数对于基本类是未定义的。 - Petr
2
一个重复的问题得到了这个非常好的答案 - sjakobi
5个回答

阿里云服务器只需要99元/年,新老用户同享,点击查看详情
24

简而言之: some 表示“一个或多个”,many 表示“零个或多个”结果,这些结果是通过反复执行相同计算方式来收集的,采用了熟悉的最大匹配规则。为了使其有意义,必须进行一些状态传递(和修改),从而在某种程度上减少可能性的范围,否则它将无限重复。状态传递和 解析密切相关。


一个基本的例子实例:with
import Control.Monad(Functor(..))
import Control.Applicative
import Data.Char

-- char string parser
newtype P a = P { runP :: String -> [(a,String)] }

-- runP (P p) s = p s

instance Functor P where
  -- fmap :: (a -> b) -> f a -> f b
  fmap f (P q) = P (\s -> [ (f y,ys) | (y,ys) <- q s])

instance Applicative P where
  -- pure :: a -> f a
  pure x = P (\s -> [(x,s)])
  -- (<*>) :: f (a -> b) -> f a -> f b
  P p <*> P q = P (\s -> [(x y, ys) | (x,xs) <- p s, (y,ys) <- q xs])

letter = P p where      -- sample parser
  p (x:xs) | isAlpha x = [(x,xs)]
  p _ = []
我们有。
*Main Data.Char> runP letter "123"
[]
*Main Data.Char> runP letter "a123"
[('a',"123")]
*Main Data.Char> runP ( (:) <$> letter <*> pure []) "a123"
[("a","123")]
*Main Data.Char> runP ( (:) <$> letter <*> ((:)<$>letter <*> pure []) ) "a123"
[]
*Main Data.Char> runP ( (:) <$> letter <*> ((:)<$>letter <*> pure []) ) "ab123"
[("ab","123")]   -- NOT NICE ^^^^^^^^^^^^^^^^^^^^ -}
然后,带着

instance Alternative P where
  -- (<|>) :: f a -> f a -> f a
  P p <|> P q = P (\s-> p s ++ q s)
  -- empty :: f a   -- the identity of <|>
  empty = P (\s-> [])
我们得到
*Main Data.Char> runP (many letter) "ab123"
[("ab","123"),("a","b123"),("","ab123")]
*Main Data.Char> runP (some letter) "ab123"
[("ab","123"),("a","b123")]

*Main Data.Char> runP (optional letter) "ab123"
[(Just 'a',"b123"),(Nothing,"ab123")]
*Main Data.Char> runP (optional letter) "123"
[(Nothing,"123")]

Prelude Main Data.Traversable> runP (sequenceA $ replicate 2 letter) "ab123"
[("ab","123")]               --  NICE  ^^^^^^^^^^^^^^^^^^^
-}

1
好的解释! - wind2412
离题,元问题:我注意到您已创建了缺失的[haskell-alternative]标签。 一段时间以前,我曾考虑过做同样的事情,甚至编制了一个我们可能想要添加到其中的问题列表,但陷入了沉思,是否将MonadPlus和Alternative合并为单个标签(我的本能倾向)还是将它们分开,并命名标签(我最初想到[monadplus-and-alternative])。 我很感谢您对这些问题的意见-这可以帮助我最终下定决心 :) - duplode
1
@WillNess 好主意,[alternative-functor] 听起来很不错! - duplode
1
@WillNess 重新标记完成!到目前为止,[tag:alternative-functor] 已经有了64个问题,进展顺利。虽然 [tag:monadplus] 有39个问题,其中14个与 [alternative-functor] 共享,但它并不感觉很稳定,尽管 这个未解决的争议宝石 或许已经值得入场费了。 - duplode
@duplode 做得好,也是必要的(但却不被人赞赏)!顺便说一下,这两个数字都相当不错;我遇到过很多只有十几个问题的标签,但仍然感觉很必要,所以39绝对不是一个小数字。 :) - Will Ness
显示剩余4条评论

19
在STM应用程序中,some的意思是:持续尝试直到至少成功一次,然后继续执行,直到失败。而many的意思是:尽可能多地执行此操作,直到失败为止。

整洁,从未想过! - PyRulez

18

我倾向于在Applicative解析器组合库中看到它们。

a :: Parser [String]
a = some (string "hello")

我注意到在parsers默认的Parsing定义中many用于实现某些功能。

我认为,由于Parsec是解析器组合库的主要示例,它重新定义了像(<|>)这样的东西,所以隐藏了some/many的使用。


6
Will提供了一个很好的例子来激励使用这些方法,但是您似乎仍然对类型类有误解。 类型类定义列出了所有实例的方法的类型签名。它还可以提供这些方法的默认实现,这就是Alternative的some和many方法正在发生的事情。 为了成为有效实例,所有方法都必须为该实例定义。因此,您发现的那些没有专门为一些或许多定义实例的方法使用了默认实现,它们的代码与您的问题中列出的完全相同。 因此,为了明确起见,由于类型类定义中给出了默认定义,因此确实定义了some和many,并且可以与所有Alternative实例一起使用。

2
我理解所有的内容。为了澄清,对于最基本的类型,如[]Maybesomemany的定义只是循环。因此,尽管它们的定义是有效的,但它们没有意义。 - Petr
5
我很喜欢@PetrPudlák的这个答案(谢谢,is7s)。 - Will Ness
@WillNess 的确,这个答案非常好。 - Petr

3
regex-applicative 包为 RE(正则表达式)类型定义了一个自定义的many 方法。正则表达式和 RE 解析器必须是有限大小的,因此使用默认定义的 somemany 可能会导致无限循环!幸运的是,many 就相当于经典的正则表达式中的 *。 该软件包还包括一个 some 的定义,但它看起来太像默认定义,没有什么有趣的内容。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,