跳转到内容

另一个 Haskell 教程/勘误

来自维基教科书,开放世界中的开放书籍
Haskell
另一个 Haskell 教程
前言
简介
入门
语言基础 (解答)
类型基础 (解答)
IO (解答)
模块 (解答)
高级语言 (解答)
高级类型 (解答)
单子 (解答)
高级 IO
递归
复杂度

此页面包含一个错误列表,这些错误是在 YAHT 的 PDF 版本 中发现的(其中一些可能已经在 在线 HTML 版本 中修正了 - (导入)).

  • 练习 3.1:"我们已经看到乘法比除法结合得更紧密。" - 就我而言,乘法和除法具有相同的优先级,因此它们的结合程度相同。也许你指的是加法与乘法或乘法与乘方?我看到它已经在在线版本中修正了。
  • 练习 7.1 的解答(第 79 页,在“7.3 部分应用”下)存在一个 bug,练习内容如下(参见 高级语言:部分应用 获取对应的 HTML 版本(已修正))


练习

如果可能,将以下函数转换为无点风格。

[...]

func5 f l = foldr (\x y -> f (y,x)) 0 l
  • 在第 81 页,文本指出

"我们创建一个值,称之为 x,其值为 Red。然后我们将它应用于 colorToRGB。我们检查是否可以将 x 与 Red “匹配”。这种匹配失败,因为根据 Eq Color 的定义,Red 不等于 Yellow。"

显然,前提是错误的,为了使本段的其余内容有意义,初始值 x 应该为 'Yellow'。


  • 根据 PDF 文件 的第 172 页,解决方案如下
[...]

func5 = foldr (uncurry $ flip f) 0

然而,正如 Michael Mossey 在 Haskell-Beginners 邮件列表中 指出的,该解决方案实际上是错误的。

以下是正确的解决方案

[...]

func5 = flip foldr 0 . flip . curry 

作为旁注,Daniel Fischer 已经 提供 了使用类型验证解决方案正确性的简洁方法,如下所示


检查结果是否合理的一种简单方法是

示例

Prelude> :t flip foldr 0 . flip. curry
flip foldr 0 . flip. curry :: (Num c) => ((c, b) -> c) -> [b] -> c

Prelude> :t \f list -> foldr (\x y -> f (y,x)) 0 list
\f list -> foldr (\x y -> f (y,x)) 0 list :: (Num b) =>
                                             ((b, a) -> b) -> [a] -> b

Prelude> :t \f -> foldr (uncurry $ flip f) 0
\f -> foldr (uncurry $ flip f) 0 :: (Num b1) =>
                                    (b -> a -> b1 -> b1) -> [(a, b)] -> b1

因此你可以看到你的结果具有正确的类型,而 Hal 的结果没有。


在上面提到的描述中,前两行中的第一对显示

flip foldr 0 . flip. curry

(即,要验证的解决方案) 的类型是

(Num c) => ((c, b) -> c) -> [b] -> c

后两行中的第二对显示

\f list -> foldr (\x y -> f (y,x)) 0 list

(即,本质上是练习中给出的表达式,即 f l = foldr (\x y -> f (y,x)) 0 l) 的类型也是

(Num b) => ((b, a) -> b) -> [a] -> b

(请注意,这与上面的类型相同,只是使用重命名的变量)。

后两行中的第三对显示

\f -> foldr (uncurry $ flip f) 0

(即,Hal 在 PDF 版本 的教程中给出的解决方案) 的类型是

(Num b1) => (b -> a -> b1 -> b1) -> [(a, b)] -> b1

这是一个不同的类型。因此,Hal 给出的解决方案是错误的。

注意

(感谢 Michael Mossey 和 Daniel Fischer 在 Haskell-Beginners 邮件列表中就这个问题进行的讨论)。

-- Benjamin L. Russell


2. 练习 4.11

练习
4.11. 测试 CPS 风格的 fold 是否模拟了 foldr 和 foldl 中的任何一个。如果没有,差异在哪里?

它没有指定“CPS 风格的 fold”函数,因此读者应该假设练习是关于之前定义的 cfold

cfold’ f z [] = z
cfold’ f z (x:xs) = f x z (\y -> cfold’ f y xs)
cfold f z l = cfold’ (\x t g -> f x (g t)) z l

但 YAHT 中给出的解决方案实际上不是关于此函数。事实上,此函数的行为与 foldr 完全相同。解决方案是关于非常相似的函数:唯一的区别是 cfold' 的第二种情况下的参数顺序,即

cfold' f z (x:xs) = f z x (\y -> cfold' f y xs)
华夏公益教科书