【问题标题】:How does this implementation of `init` work?`init` 的这个实现是如何工作的?
【发布时间】:2020-11-28 07:09:16
【问题描述】:

我正在学习 Haskell,我发现了一个使用 foldr 的有趣的 init 实现。但是,我很难理解它是如何工作的。

init' xs = foldr f (const []) xs id
    where f x g h = h $ g (x:)

假设我有一个[1,2,3] 的输入,那么它会变成

f 1 (f 2 ( f 3 (const []))) id

我将这些参数替换为f,最里面的参数变为h $ (const []) (1:),即h []。然而,当我想进一步减少表达时,我发现很难掌握。下一个变成f 2 (h []),也就是

h $ (h []) (2:)

如果它是这样工作的。这让我看起来很困惑。要匹配foldr 的类型,h 应该是[a] -> [a] 类型,而h [] 将只是[a] 类型,这不适用于(2:)

我还以另一种方式考虑过,f x g 返回一个类型为([a] -> [a]) -> [a] 的函数,考虑到之后应用id,这有点道理。但后来我意识到我仍然不知道h 在这里做什么。看起来h 将上次的g (x:) 传送到下一个应用程序。

当我想到将fold 用作累加器时,我是否错过了什么?

如果有人能帮我解决这个问题,我将不胜感激。

【问题讨论】:

  • 您不能以这种方式简化为h [],因为h 未定义:此时没有为此传递任何参数。您可以改为缩减为\h -> h [],这表示以后仍必须提供h。继续:f 2 (\h -> h []) = ...
  • foldr 只将两个参数传递给f。替换后,您将得到\h -> h $ (const []) (1:)

标签: function haskell functional-programming fold accumulate


【解决方案1】:

对于列表[1,2,3]init' 替换为:

init' [1,2,3]
  = foldr f (const []) [1,2,3] id
  = f 1 (foldr f (const []) [2,3]) id

这里f 被称为1xfoldr f (const []) [2,3]gid h,这意味着这被解析为:

id <b>(foldr f (const []) [2,3] (1:))</b>

因此,这意味着我们现在将在结果前面加上1:,而不是在递归中使用id。接下来我们可以将内部的foldr解析为:

foldr f (const []) [2,3] (1:)
 = f 2 (foldr f (const []) [3]) (1:)
 = (1:) (foldr f (const []) [3] (2:))

内部foldr 则结果为:

foldr f (const []) [3] (2:)
 = f 3 (foldr f (const []) []) (2:)
 = (2:) (foldr f (const []) [] (3:))

最后foldr f (const []) [] 将转换为const []

这意味着:

foldr f (const []) [3] (2:)
 = f 3 (foldr f (const []) []) (2:)
 = (2:) (foldr f (const []) [] (3:))
 = (2:) (const [] (3:))

const 将因此忽略参数(3:),并返回一个空列表。所以foldr f (const []) [3] (2:)的结果是[2]

foldr f (const []) [3] (2:)
 = f 3 (foldr f (const []) []) (2:)
 = (2:) (foldr f (const []) [] (3:))
 = (2:) (const [] (3:))
 = (2:) []
 = [2]

如果我们将其替换为 foldr f (const []) [2,3] (1:),我们会得到:

foldr f (const []) [2,3] (1:)
 = f 2 (foldr f (const []) [3]) (1:)
 = (1:) (foldr f (const []) [3] (2:))
 = (1:) [2]
 = [1,2]

这意味着init' [1,2,3] 将返回[1,2]

这意味着一个

foldr f (const []) [x<sub>1</sub>, x<sub>2</sub>, …, x<sub>n</sub>] (x<sub>0</sub>:)

将被替换为:

(x<sub>0</sub>:) (foldr f (const []) [x<sub>2</sub>, x<sub>3</sub>, …, x<sub>n</sub>] (x<sub>1</sub>:))

如果列表用完,那么它将替换:

foldr f (const []) [] (x<sub>n</sub>:)

与:

<b>const</b> [] (x<sub>n</sub>:)

所以const 函数将忽略最后一个元素。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-18
    • 2016-05-14
    • 2011-05-30
    • 1970-01-01
    相关资源
    最近更新 更多