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,19,16,18,20] outerZipWith (+) 0 0 [1..11] [10..14] == [11,6,7,8,9,10,11]
我发现自己经常需要它,而且我宁愿使用一个常用的习惯用法来使我的代码更易于编写(并且更易于维护),而不是实现outerZipWith,或者如果长度为<长度bs然后zipWith f(重复a)bs else zipWith f as(bs repeat b).
解决方法
首先,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实例并使用标识值来填充列表.但是,使用newtype包装器等转换为适当的Monoid和从适当的Monoid转换可能不会比当前实现更简单.
另外,您对列表索引作为键的评论实际上可以进一步发展;任何具有类似密钥概念的Functor与Reader monad是同构的,a.k.a.是从键到值的显式函数. Edward Kmett一如既往地编写了一堆编码这些抽象概念的软件包,在本例中是从the representable-functors
package开始构建的.如果你不介意写十几个实例只是为了开始至少……