繁体   English   中英

Haskell 中的替代 ZipList 实例?

[英]instance Alternative ZipList in Haskell?

ZipList带有一个Functor和一个Applicative实例 ( Control.Applicative ) 但为什么不是Alternative呢?

  • 没有好的例子吗?
  • 下面推荐的那个呢?
    • 它有缺陷吗?
    • 没用吗?
    • 是否有其他合理的可能性(如Bool可以有两种方式幺半群),因此也不应该实例?

我搜索了“instance Alternative ZipList”(用引号先找到代码),只找到了库、一些教程、讲义,但没有实际实例。

Matt Fenwick 说ZipList A只会是一个幺半群,如果A是( 见这里)。 不管元素类型如何,列表都是幺半群。

AndrewC对同一问题的另一个回答讨论了Alternative实例的外观。 他说

Zip [1,3,4] <|> Zip [10,20,30,40]有两个明智的选择:

  1. Zip [1,3,4]因为它是第一个 - 与 Maybe 一致
  2. Zip [10,20,30,40]因为它是最长的 - 与Zip []被丢弃一致

其中Zip基本上是ZipList

我认为答案应该是Zip [1,3,4,40] 让我们看看实例:

instance Aternative Zip where
  empty = Zip []
  Zip xs <|> Zip ys = Zip (go xs ys) where
    go []     ys = ys
    go (x:xs) ys = x : go xs (drop 1 ys)

我们可以在不知道类型参数a情况下生成的唯一Zip aZip [] :: Zip a ,因此对于empty几乎没有选择。 如果空列表是幺半群的中性元素,我们可能会倾向于使用列表连接。 但是, go不是(++)因为drop 1 每次我们使用第一个参数列表的一个条目时,我们也会从第二个参数列表中删除一个。 因此,我们有一种覆盖:左边的参数列表隐藏了右边的(或全部)的开头。

[ 1, 3, 4,40]   [10,20,30,40]   [ 1, 3, 4]   [ 1, 3, 4]
  ^  ^  ^  ^      ^  ^  ^  ^      ^  ^  ^      ^  ^  ^
  |  |  |  |      |  |  |  |      |  |  |      |  |  |
[ 1, 3, 4] |    [10,20,30,40]   []|  |  |    [ 1, 3, 4]
[10,20,30,40]   [ 1, 3, 4]      [ 1, 3, 4]   []

ziplists 背后的一个直觉是过程:有限或无限的结果流。 压缩时,我们组合流,这由Applicative实例反映。 当到达列表末尾时,流不会产生更多元素。 这就是Alternative实例派上用场的地方:我们可以命名一个并发替换(替代,真的),在默认进程终止后立即接管。

例如,我们可以编写fmap Just foo <|> pure Nothing将 ziplist foo每个元素包装到Just然后继续使用Nothing 生成的 ziplist 是无限的,在所有(真实)值用完后恢复为默认值。 这当然可以通过在Zip构造函数中附加一个无限列表来手动完成。 然而以上更优雅,并且不假设构造函数的知识,从而导致更高的代码可重用性。

我们不需要对元素类型做任何假设(比如作为幺半群本身)。 同时,定义并不简单(就像(<|>) = const那样)。 它通过对第一个参数进行模式匹配来使用列表结构。

上面给出的<|>的定义是关联的,空列表实际上是空元素。 我们有

Zip [] <*> xs  ==  fs <*> Zip []  ==  Zip []     -- 0*x = x*0 = 0
Zip [] <|> xs  ==  xs <|> Zip []  ==  xs         -- 0+x = x+0 = x
(fs <|> gs) <*> xs  ==  fs <*> xs <|> gs <*> xs
 fs <*> (xs <|> ys) ==  fs <*> xs <|> fs <*> ys

所以你可以要求的所有法律都得到满足(列表串联不是这样)。

此实例与Maybe实例一致:选择偏向左侧,但当左侧参数无法产生值时,右侧参数会接管。 功能

zipToMaybe :: Zip a -> Maybe a
zipToMaybe (Zip [])    = Nothing
zipToMaybe (Zip (x:_)) = Just x

maybeToZip :: Maybe a -> Zip a
maybeToZip Nothing  = Zip []
maybeToZip (Just x) = Zip (repeat x)

是替代的态射(意思是psi x <|> psi y = psi (x <|> y)psi x <*> psi y = psi (x <*> y) )。

编辑:对于some / many方法我猜

some (Zip z) = Zip (map repeat z)
many (Zip z) = Zip (map repeat z ++ repeat [])

标签/索引

有趣的。 一个并非完全不相关的想法:ZipList 可以被视为普通列表,其中元素由它们在列表中的(递增)位置索引标记。 压缩应用程序通过配对相同索引的元素来连接两个列表。

想象一下带有由(非递减) Ord values标记的元素的列表。 Zippery应用程序将配对相同标记的元素,丢弃所有不匹配的元素(它有它的用途); zippery替代方案可以对标签值执行保持顺序的左优先联合(常规列表上的替代方案也是一种联合)。

这完全符合您对索引列表(又名 ZipLists)的建议。

所以是的,这是有道理的。

对值列表的一种解释是不确定性,这与列表的 monad 实例一致,但 ZipLists 可以解释为按顺序组合的同步值流。

有了这种流解释,您就不会考虑整个列表,因此选择最长的流显然是在作弊,并且从定义<|>的第一个 ZipList 故障转移到第二个的正确解释将是正如您在实例中所说的那样,在第一个完成时即时完成。

将两个列表压缩在一起并不仅仅是因为类型签名,而是对<|>的正确解释。

最长可能列表

当您将两个列表压缩在一起时,结果是两个长度中的最小值。 这是因为这是在不使用 ⊥ 的情况下满足类型签名的最长可能列表。 将其视为选择两个长度中较短的一个是错误的 - 它是可能的最长的。

类似地, <|>应该生成尽可能长的列表,并且它应该更喜欢左边的列表。 显然,它应该占用整个左侧列表并占用左侧列表中的右侧列表以保持同步/快速。

你的实例没问题,但它做了一些 ZipList 没有的事情
(a)瞄准最长的列表,以及
(b)在源列表之间混合元素。

压缩作为操作在最短列表的长度处停止。

这就是为什么我在回答中得出结论:

因此,唯一合理的替代实例是:

instance Alternative Zip where
   empty = Zip []
   Zip [] <|> x = x
   Zip xs <|> _ = Zip xs

这与 Maybe 和解析器的 Alternative 实例一致,它们说如果没有失败就应该做a如果失败就去b 您可以说较短的列表不如较长的列表成功,但我认为您不能说非空列表完全失败。

empty = Zip []被选中是因为它在列表的元素类型中必须是多态的,并且唯一的这样的列表是[]

为了平衡,我不认为你的实例很糟糕,我认为这更干净,但是嘿嘿,根据需要推出自己的实例!

事实上,ZipList 有一个明智的Alternative实例。 它来自一篇关于免费MonadPlus 的论文MonadPlusAlternative就是其中的例子):

instance Alternative ZipList where
  empty = ZipList []

  ZipList xs <|> ZipList ys = ZipList $ go xs ys where
    go [] bs = bs
    go as [] = as
    go (a:as) (_:bs) = a:go as bs

这是它的原始代码的性能更高的版本,它是

ZipList xs <|> ZipList ys = ZipList $ xs ++ drop (length xs) ys

我对Alternative指导直觉来自解析器,它表明如果您的替代品的一个分支以某种方式失败了,它应该被根除,从而导致一个Longest风格的Alternative可能不是非常有用。 这将是无偏见的(与解析器不同)但在无限列表上失败。

再说一次,正如您所建议的,他们必须做的就是形成Monoid 但是,您的偏向以ZipList通常不会体现的方式存在——您可以同样轻松地清晰地形成Alternative实例的反映版本。 正如您所指出的,这也是与Maybe的约定,但我不确定ZipList是否有任何理由遵循该约定。

没有明智的somemany我不相信,尽管实际上很少有Alternative具有这些 - 也许它们最好被隔离到Alternative的子类中。

坦率地说,我不认为您的建议是一个糟糕的实例,但我对它是ZipList所暗示的“替代实例”没有任何信心。 也许最好看看这种“扩展”实例还可以在哪里应用(树?)并将其编写为库。

暂无
暂无

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

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