如果将列表中每个元素的(隐式)索引视为其键,则zipWith
有点像关系内连接。它只处理两个输入都有值的键:
zipWith (+) [1..5] [10..20] == zipWith (+) [1..11] [10..14] == [11,13,15,17,19]
是否存在与外连接对应的规范对应功能?类似的东西:
outerZipWith :: (a -> b -> c) -> a -> b -> [a] -> [b] -> [c]
outerZipWith _ _ _ [] [] = []
outerZipWith f a' b' [] (b:bs) = f a' b : outerZipWith f a' b' [] bs
outerZipWith f a' b' (a:as) [] = f a b' : outerZipWith f a' b' as []
outerZipWith f a' b' (a:as) (b:bs) = f a b : outerZipWith f a' b' as bs
或者
outerZipWith' :: (a -> b -> c) -> Maybe a -> Maybe b -> [a] -> [b] -> [c]
outerZipWith' _ _ _ [] [] = []
outerZipWith' _ Nothing _ [] _ = []
outerZipWith' _ _ Nothing _ [] = []
outerZipWith' f a' b' [] (b:bs) = f (fromJust a') b : outerZipWith f a' b' [] bs
outerZipWith' f a' b' (a:as) [] = f a (fromJust b') : outerZipWith f a' b' as []
outerZipWith' f a' b' (a:as) (b:bs) = f a b : outerZipWith f a' b' as bs
所以我可以做到
outerZipWith (+) 0 0 [1..5] [10..20] == [11,13,15,17,19,15,16,17,18,19,20]
outerZipWith (+) 0 0 [1..11] [10..14] == [11,13,15,17,19,6,7,8,9,10,11]
我发现自己经常需要它,我宁愿使用一个常用的习惯用法来使我的代码更易于编写(并且更易于维护),而不是实现outerZipWith
或执行if length as < length bs then zipWith f (as ++ repeat a) bs else zipWith f as (bs ++ repeat b)
答案 0 :(得分:8)
这种事情出现了很多。这是PACT algebra的cogroup
操作。在我工作的地方,我们使用类型类来区分这三个操作:
zip
:结构交叉点。align
:结构联盟。liftA2
:结构性笛卡尔积。答案 1 :(得分:5)
这看起来很尴尬,因为你试图跳到最后而不是处理原始操作。
首先,zipWith
在概念上是zip
,后跟map (uncurry ($))
。这是一个重要的观点,因为(un)currying是zipWith
可能的原因。给定类型为[a]
和[b]
的列表,要应用函数(a -> b -> c)
并获取类型为[c]
的内容,您显然需要每个输入中的一个。执行此操作的两种方法恰好是列表的两个Applicative
实例,其中一个zipWith
为liftA2
。 (另一个例子是标准的,它给出了笛卡尔积 - 如果你愿意的话,就是交叉连接。)
您想要的语义与任何明显的Applicative
实例都不对应,这就是为什么它要困难得多。首先考虑outerZip :: [a] -> [b] -> [?? a b]
以及它将具有什么类型。结果列表的元素可以分别是a
,b
或两者。这不仅不符合任何标准数据类型,因此无法用表达式(A + B + A*B)
来表示任何有用的东西。
这种数据类型有自己的用途,这就是我my own package defining one的原因。我记得还有一个关于hackage(我认为辅助功能比我的少),但我不记得它叫什么。
坚持标准的东西,你最终需要一个合理的“默认值”,大致转化为拥有Monoid
实例并使用标识值填充列表。但是,使用Monoid
包装器等对适当的newtype
进行转换可能不会比当前的实现更简单。
顺便说一句,您对列表索引作为键的评论实际上可以进一步发展;具有类似密钥概念的任何Functor
与Reader
monad是同构的,a.k.a。是从键到值的显式函数。 Edward Kmett一如既往地拥有一堆编码这些抽象概念的软件包,在本例中是从the representable-functors
package构建的。可能会有所帮助,如果你不介意写一打实例,至少要开始......
答案 2 :(得分:3)
为什么不提供一个值列表,直到达到更长列表的末尾,而不是用常量填写较短的列表?这也消除了对Maybe
的需要,因为列表可以是空的(或有限长度)。
我无法找到任何标准内容,但如果没有按照您展示的方式完全重新实施zipWith
,我就开发了这样的length
测试版本:
outerZipWith :: (a -> b -> c) -> [a] -> [b] -> [a] -> [b] -> [c]
outerZipWith f as bs as' bs' = take n $ zipWith f (as ++ as') (bs ++ bs')
where n = max (length as) (length bs)