Haskell 为什么zipWith.zipWith有效?
我正在实现一个函数Haskell 为什么zipWith.zipWith有效?,haskell,zipwith,Haskell,Zipwith,我正在实现一个函数combine::[[a]]->[[b]]->(a->b->c)->[[c]]],该函数给定两个二维列表,将给定函数f::a->b->c应用于二维列表的条目。换言之: [[a, b, c], [[r, s, t], [[f a r, f b s, f c t], combine [d, e, g], [u, v, w], f = [f d u, f e v, f g w], [h, i
combine::[[a]]->[[b]]->(a->b->c)->[[c]]]
,该函数给定两个二维列表,将给定函数f::a->b->c
应用于二维列表的条目。换言之:
[[a, b, c], [[r, s, t], [[f a r, f b s, f c t],
combine [d, e, g], [u, v, w], f = [f d u, f e v, f g w],
[h, i, j]] [x, y, z]] [f h x, f i y, f j z]]
现在我怀疑combine=zipWith。zipWith
,因为我已经尝试过了,它给了我预期的结果,例如
(zipWith . zipWith) (\x y -> x+y) [[1,2,3],[4,5,6]] [[7,8,9],[10,11,12]]
给出了预期的结果[[8,10,12],[14,16,18]
,但我不明白为什么会这样,因为我不明白zipWith的类型是如何工作的。zipWith
原来是(a->b->c)->[[a]]->[[b]]->[[c]]
()
此处是否仍在执行通常的函数合成?如果是这样,您能解释一下这是如何应用于zipWith
来推断表达式的类型,例如zipWith。zipWith
,你可以用下面的方式在头脑中模拟统一
第一个zipWith
具有类型(a->b->c)->([a]->[b]->[c])
,第二个(s->t->u)->([s]->[t]->[u])
和(。
具有类型(m->n)->(o->m)->(o->n)
要进行打字检查,您需要:
=m
(a->b->c)
=n
([a]->[b]->[c])
=o
(s->t->u)
=m
=([s]->[t]->[u])
=a
,[s]
=b
,[t]
=c
,因为第一个约束[u]
然后返回的类型是
o->n
,它是(s->t->u)->([a]->[b]->[c])
,从约束中进一步(s->t->u)->([[s]]->[[t]]->[[u]])
是的,
是正常的函数组合运算符:
Prelude> :type (.)
(.) :: (b -> c) -> (a -> b) -> a -> c
一种方法是,它接受一个a
值,首先调用a->b
函数,然后使用该函数的返回值调用b->c
函数。结果是一个c
值
查看(zipWith.zipWith)
的另一种方法是执行eta扩展:
Prelude> :type (zipWith . zipWith)
(zipWith . zipWith) :: (a -> b -> c) -> [[a]] -> [[b]] -> [[c]]
Prelude> :t (\x -> zipWith $ zipWith x)
(\x -> zipWith $ zipWith x)
:: (a -> b -> c) -> [[a]] -> [[b]] -> [[c]]
Prelude> :t (\x -> zipWith (zipWith x))
(\x -> zipWith (zipWith x))
:: (a -> b -> c) -> [[a]] -> [[b]] -> [[c]]
zipWith
本身的类型:
Prelude> :type zipWith
zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
因此,在上面的lambda表达式中,x
必须是(a->b->c)
,因此带有x的zipWith必须具有[a]->[b]->[c]
类型
如果a1
是[a]
,b1
是[b]
,而c1
是[c]
,则带的zipWith
外部zipWith
也需要一个函数
因此,通过替换,zipWith(zipWith x)
必须具有类型[[a]]->[[b]->[[c]]
,因此lambda表达式的类型是(a->b->c)->[[a]->[[b]->[[c]]
另一种看待它的方式是,使用压缩操作的列表形成一个应用程序,以及(嵌套)的Applicative
s仍然是Applicative
:
λ import Control.Applicative
λ import Data.Functor.Compose
λ let l1 = ZipList [ZipList [1,2,3], ZipList [4,5,6]]
λ let l2 = ZipList [ZipList [7,8,9], ZipList [10,11,12]]
λ getCompose $ (+) <$> Compose l1 <*> Compose l2
ZipList {getZipList = [ZipList {getZipList = [8,10,12]},
ZipList {getZipList = [14,16,18]}]}
λ导入控制。适用
λimport Data.Functor.Compose
λ设l1=ZipList[ZipList[1,2,3],ZipList[4,5,6]]
λlet l2=ZipList[ZipList[7,8,9],ZipList[10,11,12]]
λgetCompose$(+)Compose l1 Compose l2
ZipList{getZipList=[ZipList{getZipList=[8,10,12]},
ZipList{getZipList=[14,16,18]}]}
<代码> ZPLIST < /Cord> NeXType是必需的,因为“裸”列表有不同的<代码>应用程序实例,它形成了所有组合而不是拉链。实际上,您也可以考虑查看此类操作的类型。BTW,可以将加法运算符作为函数:<代码>(ZIPOS.ZIPOP)(+)[[1,2,3],[4],5],6]。][7,8,9],[10,11,12]
或<代码>()。((+))l1 l2
无数据。Functor.Compose
@Redu虽然更紧凑,但我发现新的无类型版本更难理解。是的,我必须同意,无点有时乍一看可能会令人困惑。基本上是()。(++)
是“\xy->”(++)xy”。