3

我正在实现一个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吗?

4

3 回答 3

7

要推断诸如 的表达式的类型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]])

于 2017-10-07T12:31:28.567 回答
2

另一种看待它的方式是具有压缩操作的列表形成 an Applicative,而 s 的组合(嵌套)Applicative仍然是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]}]}

newtypeZipList是必需的,因为“裸”列表具有不同的Applicative实例,它形成所有组合而不是压缩。

于 2017-10-07T12:56:32.210 回答
1

是的,.是正常的函数组合运算符:

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),因此zipWith x必须具有类型[a] -> [b] -> [c]

外部 zipWith还需要一个函数,(a1 -> b1 -> c1)它匹配zipWith xif a1is [a]b1is[b]c1is [c]

因此,通过替换,zipWith (zipWith x)必须具有 type [[a]] -> [[b]] -> [[c]],因此 lambda 表达式的类型是(a -> b -> c) -> [[a]] -> [[b]] -> [[c]]

于 2017-10-07T12:41:42.763 回答