【问题标题】:Haskell foldl implementation with foldrHaskell foldl 使用 foldr 实现
【发布时间】:2016-09-04 07:01:16
【问题描述】:

我无法理解使用foldr 实现foldl 函数。我已阅读此问题 (Writing foldl using foldr),但在以下示例中仍有一些我不明白的地方:

fun :: [Int] -> [Int]
fun l = foldr g (const []) l 1
    where g x f lst = if gcd x lst == 1 then x : f x else f lst

该函数将一个列表作为参数并返回另一个列表,其中gcd(l[i], l[i + 1] = 1

我的问题如下:
1.xflst是谁
2、const[]是什么,为什么我不能使用id功能?

【问题讨论】:

  • 1:你的g函数的参数,2.const x = \y -> x

标签: haskell


【解决方案1】:

foldr 是一种奇怪的工具,比如自行车,一旦你掌握了它们就非常容易使用,但从一开始就很难学习。经过几年的经验,我已经非常擅长发现我可以用foldr 解决的问题,并立即正确地解决它们,但我可能需要一段时间才能弄清楚我做了什么足够详细的解释!

从实际的角度来看,我通常会用模糊的延续传递语言想到foldr。忽略foldr 仅应用于三个参数的“简单”情况,foldr 的应用如下所示:

foldr go finish xs acc1 acc2 ... where
  finish acc1 acc2 ... = ?
  go x cont acc1 acc2 ... = ?

acc1 等是“从左到右”传递的累加器。从概念上讲,结果由“从右到左”传递的单个值组成。

finish 获取累加器的最终值并生成结果类型的内容。这通常是最容易编写的部分,因为

foldr go finish [] acc1 acc2 ...
=
finish acc1 acc2 ...

因此,一旦您确定了您希望折叠产生的内容,编写 finish 就相当机械了。

go 获取单个容器元素、“延续”和累加器。如果这些累加器“转发”到延续以获得结果,它会传递修改后的值,并使用该结果构造自己的结果。

foldl 是一个特别简单的例子,因为它的go 函数只是返回它通过使用新的累加器参数折叠容器的其余部分而得到的结果。我认为看一个做得更多的例子更有启发性。这是一个接受数字容器并生成代表运行总和和运行乘积的对列表。

sumsProducts :: (Num n, Foldable f) => f n -> [(n, n)]
sumsProducts xs = foldr go finish xs 0 1
  where
    finish total prod = [(total, prod)]
    go x cont total prod =
      (total, prod) : cont (x + total) (x * prod) 

【讨论】:

    【解决方案2】:

    foldr的类型签名是这样的

    foldr :: Foldable t => (a -> b -> b) -> b -> t a -> b
    

    这意味着应用于其 3 个参数的 foldr 必须返回一个以 1 作为参数的函数。

    所以你可以将你的foldr 专门用于此

    foldr :: (Int -> (Int -> [Int]) -> (Int -> [Int])) 
            -> (Int -> [Int]) 
            -> [Int]
            -> (Int -> [Int])
    

    这意味着您的g 函数必须具有以下类型

    g :: Int -> (Int -> [Int]) -> Int -> [Int] 
    

    所以你的参数有类型

    x   :: Int
    f    :: Int -> [Int]
    lst :: Int
    

    foldr 在其第二个参数中需要Int -> [Int] 而不仅仅是Int,因此您不能将值[] 传递给它。

    幸运的是const 返回的函数忽略了它的参数并且总是返回一个常量表达式

    const [] :: a -> [b]
    

    在您的情况下,f 确实是某种累加器。但不是减少例如某个数字的值列表,您在此处链接函数。最后通过将1 传递给此函数链,它会被评估,然后构建您在fun 中返回的实际列表。

    【讨论】:

    • 但是g的类型不是只有g :: Int -> (Int -> [Int]) -> Int -> [Int]所以x :: Intf :: Int -> [Int]lst :: Int吗?因为gcd 应该将gcd x lst 中的两个整数作为参数。而且我仍然想知道 foldr 如何与 const[] 函数一起使用。它应该是某种累加器吗?
    • 哇,我完全把类型搞砸了。感谢您指出!你在这里积累了一个函数链。
    猜你喜欢
    • 2015-01-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多