为什么 zipWith.zipWith 有效?
Why does zipWith.zipWith work?
我正在实现一个函数 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
函数,然后使用该函数的 return 值调用 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)
,因此 zipWith x
必须具有类型 [a] -> [b] -> [c]
.
outerzipWith
还需要一个函数(a1 -> b1 -> c1)
,它匹配zipWith x
如果a1
是[a]
, b1
是 [b]
, c1
是 [c]
.
因此,通过替换,zipWith (zipWith x)
必须具有类型 [[a]] -> [[b]] -> [[c]]
,因此 lambda 表达式的类型是 (a -> b -> c) -> [[a]] -> [[b]] -> [[c]]
.
另一种看待它的方式是,带有压缩操作的列表形成一个 Applicative
,而 Applicative
s 的 composition(嵌套)仍然是 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]}]}
ZipList
新类型是必需的,因为 "bare" 列表有一个不同的 Applicative
实例,它形成所有组合而不是压缩。
我正在实现一个函数 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
函数,然后使用该函数的 return 值调用 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)
,因此 zipWith x
必须具有类型 [a] -> [b] -> [c]
.
outerzipWith
还需要一个函数(a1 -> b1 -> c1)
,它匹配zipWith x
如果a1
是[a]
, b1
是 [b]
, c1
是 [c]
.
因此,通过替换,zipWith (zipWith x)
必须具有类型 [[a]] -> [[b]] -> [[c]]
,因此 lambda 表达式的类型是 (a -> b -> c) -> [[a]] -> [[b]] -> [[c]]
.
另一种看待它的方式是,带有压缩操作的列表形成一个 Applicative
,而 Applicative
s 的 composition(嵌套)仍然是 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]}]}
ZipList
新类型是必需的,因为 "bare" 列表有一个不同的 Applicative
实例,它形成所有组合而不是压缩。