【问题标题】:Confusion about right folds in Haskell对 Haskell 中正确折叠的困惑
【发布时间】:2014-02-11 02:10:09
【问题描述】:

仅作背景介绍我是 Haskell 和 FP 初学者,自学。

我在Learn You a Haskell for great good 上经历了折叠。

在这个我遇到了这个功能

map' :: (a -> b) -> [a] -> [b]  
map' f xs = foldr (\x acc -> f x : acc) [] xs

一切都很好,但据我了解,lambda x 的第一个参数与[] 匹配,第二个accxs 匹配。正确的?混乱始于作者说Then, we prepend it to the accumulator, which is was []. 第二个参数acc[] 是第一个参数如何匹配?没有意义。

但他的实现正在运行,而我的(将 [] 和 xs 互换为参数)给出了一个很大的错误

Practice.hs:88:41:
    Couldn't match type `a' with `b'
      `a' is a rigid type variable bound by
          the type signature for map' :: (a -> b) -> [a] -> [b]
          at Practice.hs:87:9
      `b' is a rigid type variable bound by
          the type signature for map' :: (a -> b) -> [a] -> [b]
          at Practice.hs:87:9
    Expected type: [b]
      Actual type: [a]
    In the second argument of `foldr', namely `xs'
    In the expression: foldr (\ x acc -> f x : acc) xs []
    In an equation for map':
        map' f xs = foldr (\ x acc -> f x : acc) xs []
Failed, modules loaded: none.

我在这里缺少什么? foldr 在内部使用 flip 吗?还是我完全理解错了?

【问题讨论】:

  • 嗯,我不确定您所说的“匹配”是什么意思。最终,在您的示例中,给定函数在其 second 参数中使用 [] 调用。
  • lambda 不适用于[]xs。相反,它是foldr 的第一个参数。 foldr 的第二个和第三个参数分别是 []xs
  • @DavidYoung 我的意思是,在我看来,第一个参数(到 lambda)对应于第一个参数([]),就像在命令式编程中一样。我忘记了 lambda 也是 foldr 的一个参数以及其他 2 个参数。并且由foldr 将其应用于其他两个参数。就像@TomEllis 说的那样。
  • @TomEllis 您应该将该评论作为答案。这是我忽略的主要问题,导致了这个问题。
  • 好的,我已经回答了。

标签: haskell lambda fold


【解决方案1】:

lambda 不适用于[]xs。相反,它是foldr 的第一个参数。 foldr 的第二个和第三个参数分别是[]xs

【讨论】:

    【解决方案2】:

    看看折叠函数的“符号”形式是什么样子会有所帮助。如果我们有一个任意元素 [b1, b2, b3, b4] 和初始元素 a 的列表,那么:

    foldr f a [b1, b2, b3, b4] = f b1 (f b2 (f b3 (f b4 a)))
    

    相反,foldl 看起来像。

    foldl f a [b1, b2, b3, b4] = f (f (f (f a b1) b2) b3) b4
    

    这当然忽略了执行的惰性部分,但总体思路仍然成立。

    在您的函数中,您折叠一个包含两个参数的函数,该函数将在 f 下转换的元素推送到 cons 列表中。

    map' f xs = foldr (\x acc -> f x : acc) [] xs
    

    像上面那样将其扩展到 (xs=[x0,x1,...,xn]) 的位置:

    map' f xs = (f x0 : (f x1 : (f x2 : ... (f xn : []))))
    

    省略号只是中间所有元素的伪代码。我们看到的正是元素智能映射。希望这有助于建立直觉。

    【讨论】:

    • foldr g z [a,b,c,...,n] == g a (g b (g c (... (g n z)...))) 对我来说似乎更明显,但 YMMV。 :)
    • 这个扩展确实帮助我理解了正在发生的事情。但是下面这句话对我来说就像绕口令。 In your function you fold a function of two arguments which pushes a an element transformed under f onto a cons list. 要明确这一点,这里your functionmap'a function 是lambda。对吗?
    • @AseemBansal(由于斯蒂芬离线,我会回答)是的。 lambda 表达式定义了一个有两个参数的函数。顺便说一句,我更喜欢r(“递归结果”)而不是acc(“累加器”),它更适合左折叠。助记符。 :)
    【解决方案3】:

    foldr的类型开始,从Hoogle开始。

    foldr :: (a -> b -> b) -> b -> [a] -> b
    

    由此可见,lambda 的 second 参数必须与foldr 的第二个参数匹配,即acc 匹配[] 并且x 是@ 的一个元素987654328@,因为 lambda 的第一个参数的类型为a,而foldr 的第三个参数的类型为[a]

    请注意,foldlfoldr 具有不同的签名,因此交换了 lambda 中的参数。

    【讨论】:

    • 这帮助我理解了类型签名的用处,并证明了发生的事情是正确的。但这并没有解决整体的困惑。还是谢谢。它有帮助。
    【解决方案4】:

    看看foldr的实现可能最简单:

    foldr            :: (a -> b -> b) -> b -> [a] -> b
    foldr k z = go
              where
                go []     = z
                go (y:ys) = y `k` go ys
    

    那就举个简单的例子吧:

    foldr (+) 0 [0, 1, 2, 4]
    

    并准确遵循递归并生成“脊椎”时发生的情况。

    foldr 脊柱的图像:

    我建议使用笔和纸追踪发生的情况。

    【讨论】:

    • 漂亮的图形,是你制作的还是在某个地方找到的?
    • 我希望你有一个漂亮的程序来为你做这件事!
    • @Eric 好吧,有:youtube.com/watch?v=X4-212uMgy8(真空),但 OP 应该用笔和纸来学习。
    • 漂亮,但这个答案省略了任何对 OP 被卡住的上下文的引用。
    【解决方案5】:

    另一种解释,使用长变量名来达到效果:

    map :: (a -> b) -> [a] -> [b]
    map f = foldr step []
        where 
          -- If you have an incomplete solution to your problem, and the first
          -- element of the input list, what is the last step you need to finish? 
          step elem incompleteSolution = f elem : incompleteSolution
    

    使用 foldr 之类的函数的好处在于,当您编写 step 函数时,step 的第二个参数将是较小版本问题的正确结果。

    一种有用的思考方式是想象foldr 已经几乎解决了您的所有问题,但仍然缺少最后一步。例如,如果您正在尝试求解map f (x:xs),请想象foldr 已经计算出map f xs 的解。使用不完整的解决方案fx,您需要执行的最后一步是什么才能获得完整的解决方案?好吧,正如代码 sn-p 所示,您将f 应用于x,并将其放在不完整的解决方案前面,您就完成了。

    foldr 的魔力在于,一旦你弄清楚了要为 step 写什么,以及要为 [] 基本情况使用什么,那么你就完成了。您的step 函数本身并不关心输入列表——它所能看到的只是一个输入列表元素和一个不完整的解决方案。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-17
      • 1970-01-01
      • 2011-06-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多