【问题标题】:map . foldr function composition - Haskell地图 。 foldr 函数组合 - Haskell
【发布时间】:2013-02-02 18:52:30
【问题描述】:

那么,让我们直奔主题吧。

:t (map.foldr)
(map.foldr) :: (a1 -> a -> a) -> [a] -> [[a1] -> a]

什么是[[a1] -> a]? 我真的很想理解这个组合,所以我这样做了:

-- map.foldr

    map.foldr :: (a1 -> a -> a) -> [a] -> [[a1] -> a]

    map :: (a1 -> b1) -> [a1] -> [b1]
    (.) :: (y -> w) -> (x -> y) -> x -> w
    foldr :: (a -> b -> b) -> b -> [a] -> b

    y = (a1 -> b1)      w = ([a1] -> [b1])
    x = (a -> b -> b)   y = (b -> [a] -> b)

    y = (a1 -> b1)  
    y = (b -> [a] -> b)
_________________________

此时会发生什么?谢谢!

【问题讨论】:

  • foldr 类型错误,应该是(a -> b -> b) -> b -> [a] -> b

标签: haskell map fold function-composition


【解决方案1】:

要回答这个问题,最好回忆一下 foldrmap 做了什么。

两者中比较复杂的是foldr,有类型

--              list to be folded
--                              v
foldr :: (a -> b -> b) -> b -> [a] -> b
--             ^          ^
--folding function        terminal value

要折叠的列表实际上是一个conses链(:)和一个终端空列表:

1 : 2 : 3 : []

foldr 的作用是将:[] 构造函数分别替换为折叠函数和终止值:

foldr (+) 0 (1 : 2 : 3 : []) == 1 + 2 + 3 + 0

map 函数更简单。一种思考方式是获取一个函数和一个列表,并将该函数应用于列表的每个参数:

map :: (a -> b) -> [a] -> [b]
--        ^         ^
-- function         list

但是,您也可以将其视为一个函数,并将其提升为一个作用于列表的函数:

map :: (a -> b) -> ( [a] -> [b] )
--        ^              ^
-- function              function on lists

组合这两个函数是什么意思,map . foldr?请注意,这只是一个接一个地应用函数 - 特别是,

(map . foldr) f == map (foldr f)

由于您首先应用foldr,因此您必须将其应用到函数f :: a -> b -> b,然后您会返回另一个函数:

foldr f :: b -> [a] -> b
--         ^     ^
--terminal val   list to be folded

现在你申请map,它提升了作用于列表的功能:

map (foldr f) :: [b] -> [[a] -> b]
--                ^          ^
--list of terminal vals      functions that fold lists

这种类型看起来很奇怪,但它是有效的。现在,您可以给它一个终端值列表,而不是单个终端值,然后您会得到一个折叠函数列表 - 您提供的每个终端值都有一个。


为了更清楚,我们可以查看一个特定的函数,(+),它具有类型

(+) :: Num a => a -> a -> a

如果我们把它代入上面的等式,我们得到

(map . foldr) (+) :: Num a => [a] -> [[a] -> a]
--                             ^          ^
--         list of terminal vals          functions that fold lists

如果我们现在将其应用于列表[0, 1, 2],我们会得到一个包含三个函数的列表:

(map . foldr) (+) [0,1,2] :: Num a => [[a] -> a]

我们可以使用map ($x) 成语将列表中的每个函数应用于特定参数。它必须是一个数字列表,我会选择[3,4,5]。仔细观看:

> map ($[3,4,5]) ((map.foldr) (+) [0,1,2])
[12, 13, 14]

使用(+)作为折叠函数,列表[3,4,5]被折叠了三次,每次都有不同的终端值:

3 + 4 + 5 + 0 == 12
3 + 4 + 5 + 1 == 13
3 + 4 + 5 + 2 == 14

当终端值为0时,我们只需得到值的总和:3 + 4 + 5 == 12。当最终值为 1 时,我们得到的值比值的总和多一 (13),而当最终值为 2 时,我们得到的值比总和 (14) 多 2。

【讨论】:

  • 在哪里可以找到有关“map ($x)”习语的更多信息?我不明白。哦,谢谢你的回答,真的很有帮助:)
  • 运算符$f $ x = f x 定义,即它将左侧的函数应用于右侧的参数。这完全没有必要,但它在两个方面很有帮助: 1. $ 运算符具有最低优先级并关联右(与具有最高优先级并关联左的函数应用程序不同),因此您可以编写 f a $ g b $ h c 而不是 f a (g b (h c)) , 和 2. 你可以在一个部分中使用它,所以你可以写($f)而不是\a -> f a。因此,代码map ($x)map (\a -> x a) 的含义相同。
【解决方案2】:

要从上次中断的地方继续,y 的两个定义必须相等:

y = (a1 -> b1) = (b ->  [a] -> b)
               = (b -> ([a] -> b))

所以我们可以得出结论:

a1 = b
b1 = [a] -> b

函数组合被提供了两个函数参数,所以结果类型只是:

x -> w

但我们知道:

x = a -> b -> b
w = [a1] -> [b1] = [b] -> [[a] -> b]

所以,结果类型是:

(x -> w) = ((a -> b -> b) -> ([b] -> [[a] -> b]))
         =  (a -> b -> b) ->  [b] -> [[a] -> b]

等于:

(a1 -> a -> a) -> [a] -> [[a1] -> a]

【讨论】:

    【解决方案3】:
    map.foldr :: (a1 -> a -> a) -> [a] -> [[a1] -> a]
    
    map :: (a1 -> b1) -> [a1] -> [b1]
    (.) :: (y -> w) -> (x -> y) -> x -> w
    foldr :: (a -> b -> b) -> b -> [a] -> b
    
    -- if you substitute: x = (a -> b -> b)   y = (b -> [a] -> b)
    -- then you get for map :: (b -> ([a] -> b)) -> [b] -> [[a] -> b]
    -- so if composition operator applied:
    map . foldr :: (a -> b -> b) -> [b] -> [[a] -> b]
    

    【讨论】:

      猜你喜欢
      • 2013-01-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-24
      • 1970-01-01
      • 2016-04-30
      • 2010-12-01
      • 1970-01-01
      相关资源
      最近更新 更多