跳转到内容

另一个 Haskell 教程/勘误

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

此页面包含迄今为止在 YAHT 的 PDF 版本 中发现的勘误列表(其中一些可能已经在 在线 HTML 版本 中更正 - (导入))。

  • 练习 3.1:“我们已经看到乘法比除法绑定得更紧密。” - 就我而言,乘法和除法具有相同的优先级,因此它们绑定得同样紧密。也许你的意思是加法与乘法或乘法与幂?我看到它已经在在线版本中修复了。
  • 练习 7.1 的解决方案在第 79 页的“7.3 部分应用”下存在错误(请参阅 高级语言:部分应用,以获取已更正的相应 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)
华夏公益教科书