【问题标题】:Using folds to make a function more elegant使用折叠让函数更优雅
【发布时间】:2014-11-05 12:50:29
【问题描述】:

我最初将我的函数作为一种解决方案提出,其中 myTakeWhile 将 (x:xs) 的元素作为列表返回,直到它到达函数参数等于 false 的元素。之后提出了另一种解决方案,如下所示。

myTakeWhile :: (a -> Bool) -> [a] -> [a] 
myTakeWhile p []     = []
myTakeWhile p (x:xs) = if p x then x : myTakeWhile p xs else []   

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

我在脑海中一步一步地通过折叠时遇到了真正的麻烦,尤其是在我下面尝试过的测试中,从列表左侧开始的右折叠的反直觉。

*Assignment1a> myTakeWhile (\x -> x `mod` 2 == 0) [1, 2, 3, 4, 5]
[]
*Assignment1a> myTakeWhile (\x -> x `mod` 2 == 0) [8, 10, 12, 1, 2, 3, 4, 5]
[8,10,12]

从根本上说,通过查看讲义,我有点了解折叠的工作原理。然而,上下文中的折叠让我感到困惑,即使删除了咖喱!如何逐步理解这个折叠?

【问题讨论】:

    标签: haskell fold


    【解决方案1】:

    让我们用[8,10,12,1,2]这个例子一步一步来。

    我假设您了解您可以考虑正确折叠foldr f a xs,方法是将: 替换为`f` 并将[] 替换为a 中的a

    f = \x acc -> if even x then x:acc else []:

    myTakeWhile even [8,10,12,1,2]
    = foldr f [] [8,10,12,1,2]
    = foldr f [] (8:10:12:1:2:[])
    { replace the : with `f` and [] with [] }
    = 8 `f` (10 `f` (12 `f` (1 `f` (2 `f` []))))
    { 2 is even so f 2 [] = 2:[] }
    = 8 `f` (10 `f` (12 `f` (1 `f` (2:[]))))
    { 2:[] = [2] }
    = 8 `f` (10 `f` (12 `f` (1 `f` [2])))
    { 1 is odd so f 1 [2] is [] }
    = 8 `f` (10 `f` (12 `f` ([])))
    { ([]) = [] }
    = 8 `f` (10 `f` (12 `f` []))
    { 12 is even so f 12 [] = 12:[] }
    = 8 `f` (10 `f` (12:[]))
    { 12:[] = [12] }
    = 8 `f` (10 `f` [12])
    { 10 is odd so f 10 [12] = 10:12 }
    = 8 `f` (10:[12])
    { 10:[12] = [10,12] }
    = 8 `f` [10,12]
    { 8 is odd so f 8 [10,12] is 8:[10,12] }
    = 8:[10,12]
    { 8:[10,12] = [8,10,12] }
    = [8,10,12]
    

    foldr 是如何工作的

    要了解为什么 foldr 会进行替换,您只需记住定义即可:

    foldr _ a []     = a
    foldr f a (x:xs) = f x (foldr f a xs) = x `f` (foldr f a xs)
    

    诀窍是递归思考(使用归纳法):

    foldr f a []
    { definition }
    a
    
    foldr f b (b:bs)
    { definition foldr x <- b; xs <- bs }
    = b `f` (foldr f a bs)
    { induction/recursion }
    = b `f` { bs with : replaced by `f` and [] by a }
    

    扩展示例

    foldr f a [b1,b2]
    { [b1,b2] = b1:b2:[] }
    = foldr f a (b1:b2:[])
    { definition foldr x <- b1; xs <- b2:[]}
    = b1 `f` (foldr f a (b2:[]))
    { definition foldr x <- b2; xs <- []}
    = b1 `f` (b2 `f` (foldr f a []))
    { definition foldr empty case }
    = b1 `f`(b2 `f` a)
    

    【讨论】:

    • 很好的答案,你能解释一下 foldr 定义的最后一行是如何工作的吗?我觉得这就是我难以掌握的地方。
    • @Bradley 我试着多解释一下,并举了另一个例子——这有帮助吗?
    猜你喜欢
    • 2017-07-31
    • 2010-09-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-26
    • 1970-01-01
    • 2021-12-30
    • 2011-10-27
    相关资源
    最近更新 更多