【问题标题】:Why do these folds stop at the head/tail?为什么这些褶皱会在头部/尾部停止?
【发布时间】:2013-05-16 17:14:12
【问题描述】:

我正在阅读 learnyouahaskell.com,目前正在调查折叠。书中有这些例子:

maximum' :: (Ord a) => [a] -> a  
maximum' = foldr1 (\x acc -> if x > acc then x else acc)  

reverse' :: [a] -> [a]  
reverse' = foldl (\acc x -> x : acc) []  

product' :: (Num a) => [a] -> a  
product' = foldr1 (*)  

filter' :: (a -> Bool) -> [a] -> [a]  
filter' p = foldr (\x acc -> if p x then x : acc else acc) []  

head' :: [a] -> a  
head' = foldr1 (\x _ -> x)  

last' :: [a] -> a  
last' = foldl1 (\_ x -> x) 

除了head'tail' 之外,我都了解。

我的理解是二进制函数应该依次应用于累加器和列表中的每个元素,从而遍历所有列表。为什么这会停在头部(或尾部)?

我知道_(下划线)的意思是“随便”或“我不在乎”,但如何停止遍历所有列表?

【问题讨论】:

    标签: function haskell functional-programming fold


    【解决方案1】:

    一个文件夹组合了两个项目 - 当前的“运行总计”排序项目和新项目。

    (\x _ -> x) 接受新项目并丢弃它,保留原始项目,因此所有剩余项目都将被忽略。

    让我们展开它:

    foldr1 (\x _ -> x) [1..100000]
    = (\x _ -> x) 1 (foldr (\x _ -> x) [2..100000])
    = 1
    

    由于不需要 (foldr (\x _ -> x) [2..100000]) 术语,因此不会对其进行评估(这是实际的惰性评估,或者更确切地说是不作为),因此运行速度很快。


    使用(\_ x -> x),新项目被采用而旧项目被忽略——这种情况一直持续到列表末尾,所以你得到了最后一个元素。它并没有回避其他的,它只是忘记了它们,除了最后一个。

    (\_ x -> x) 更易读的名称指的是它忽略第一个参数并返回第二个参数这一事实。我们就叫它secondArg吧。

    foldl1 (\_ x -> x) [1..4]
    = let secondArg = (\_ x -> x) in foldl secondArg 1 [2..4]
    = foldl (1 `secondArg` 2) [3..4] 
    = foldl ((1 `secondArg` 2) `secondArg` 3) [4]
    = foldl (((1 `secondArg` 2) `secondArg` 3) `secondArg` 4) []
    = (((1 `secondArg` 2) `secondArg` 3) `secondArg` 4)
    = 4
    

    【讨论】:

    • Haskell 的懒惰评估不会加快帖子中的head 吗? foldr1 (\x _ -> x) [1..10000000000000000] 在 GHCi 中运行只需不到一眨眼的时间。
    【解决方案2】:

    我们先来看看foldr1的定义:

    foldr1 :: (a -> a -> a) -> [a]       -> a
    foldr1    f                [x]       =  x
    foldr1    f                (x : xs)  =  f x (foldr1 f xs)
    

    然后,考虑调用您的函数head'

    head' :: [a] -> a  
    head' =  foldr1 (\x _ -> x)
    

    到一个列表,比如[2, 3, 5]

    head' [2, 3, 5]
    

    现在,填写head' 的右侧给出

    foldr1 (\x _ -> x) [2, 3, 5]
    

    回想一下[2, 3, 5](2 : 3 : 5 : []) 的语法糖。所以,foldr1 定义的第二种情况适用,我们屈服了

    (\x _ -> x) 2 (foldr1 (\x _ -> x) (3 : 5 : [])
    

    现在,减少应用程序会导致 2 绑定到 xfoldr1 (\x _ -> x) (3 : 5 : []) 绑定到被忽略的参数 _。左边是 lambda 抽象的右侧,x2 替换:

    2
    

    请注意,惰性求值会使被忽略的参数 foldr1 (\x _ -> x) (3 : 5 : []) 未求值,因此——希望这能回答你的问题——递归在我们处理完列表的其余部分之前停止。

    【讨论】:

      猜你喜欢
      • 2017-12-05
      • 1970-01-01
      • 1970-01-01
      • 2018-08-03
      • 2021-09-30
      • 1970-01-01
      • 1970-01-01
      • 2014-09-01
      • 2011-10-12
      相关资源
      最近更新 更多