3 回答
David V. 感谢您提供这些链接。Repr
绝对值得添加到我的工具箱中。我想添加一些我发现有用的附加库。
HackageDB:跟踪(截至 2010 年 12 月 12 日)
- ghc-events 库和程序:用于从 GHC 解析 .eventlog 文件的库和工具
- hood 库:通过就地观察进行调试
- hpc-strobe 库:Hpc 为正在运行的 Haskell 程序生成的闪光灯
- hpc-tracer 程序:带有 AJAX 接口的 Tracer
Hook 包似乎是我正在寻找的。我将在今天晚些时候发布更多示例。
main = runO ex9
ex9 = print $ observe "foldl (+) 0 [1..4]" foldl (+) 0 [1..4]
输出
10
-- foldl (+) 0 [1..4]
{ \ { \ 0 1 -> 1
, \ 1 2 -> 3
, \ 3 3 -> 6
, \ 6 4 -> 10
} 0 (1 : 2 : 3 : 4 : [])
-> 10
}
我不知道 Hackage 库(因为我刚刚进入 Haskell)。它让我想起了 Perl 的 CPAN。感谢您提供这些链接。这是个很棒的资源。
这绝不是对您问题的完整答复,但我在 Haskell-Cafe 上发现了一段对话,其中有一些答复:
http://www.haskell.org/pipermail/haskell-cafe/2010-June/078763.html
该线程链接到这个包:
http://hackage.haskell.org/package/repr根据页面“允许您将重载表达式呈现为其文本表示”
提供的示例是:
*Repr> let rd = 1.5 + 2 + (3 + (-4) * (5 - pi / sqrt 6)) :: Repr Double
*Repr> show rd
"fromRational (3 % 2) + 2 + (3 + negate 4 * (5 - pi / sqrt 6))"
这是一个未提出问题的答案,可以将其视为一个长评论。
(如果您认为它不适合,请仅在低于 0 时投反对票。我会删除它。)
一旦您更有经验,您可能就不想再看到事情的扩展方式了。你会想了解事物是如何工作的,然后取代它为什么工作的问题;仅仅通过观察它的扩展方式,您将不会获得太多收益。
分析代码的方法比您想象的要简单得多:只需将每个参数/变量标记为“已评估”或“未评估”或“待评估”,具体取决于它们的因果关系的进展。
两个例子:
1.)小谎言
所有斐波那契数列是
fibs :: (Num a) => [a]
fibs = 1 : 1 : zipWith (+) fibs (tail fibs)
前两个元素已经被评估;因此,将第 3 个元素(其值为 2)标记为待评估,而其余所有元素均未评估。然后,第三个元素将是 fibs 和 tail fibs 的第一个元素的 (+)-组合,这将是 fibs 的第一个和第二个元素,它们已经被标记为已评估。这适用于待评估的第 n 个元素以及 (n-2)-nd 和 (n-1)-st 已分别评估的元素。
您可以通过不同的方式将其可视化,即:
fibs!!(i+0)
+ fibs!!(i+1)
= fibs!!(i+2)
(fibs)
zipWith(+) (tail fibs)
= (drop 2 fibs)
1 : 1 : 2 : 3 ...
(1 :)1 : 2 : 3 : 5 ...
(1 : 1 :)2 : 3 : 5 : 8 ...
2.)您的示例“筛子(p:ps)xs”
primes = 2: 3: sieve (tail primes) [5,7..]
where
sieve (p:ps) xs = h ++ sieve ps [x | x <- t, rem x p /= 0]
-- or: filter ((/=0).(`rem`p)) t
where (h,~(_:t)) = span (< p*p) xs
在“筛子 (p:ps) xs”中,
- p 被评估,
- ps 未计算,并且
- xs 是一个评估的无限偏筛列表(不包含 p 但包含 p²),您可以猜测读取递归和/或认识到 h 的值需要是素数。
Sieve 应该返回 p 之后的素数列表,因此至少要评估下一个素数。
- 下一个素数将在列表 h 中,这是所有(已经过筛的)数字 k 的列表,其中 p < k < p²;h 只包含素数,因为 xs 既不包含 p 也不包含任何可被任何低于 p 的素数整除的数。
- t 包含 p² 以上的所有 xs 数。t 应该被评估为惰性而不是尽快,因为甚至可能不需要评估 h 中的所有元素。(只有 h 的第一个元素需要计算。)
函数定义的其余部分是递归,其中下一个 xs 是 t,所有 n*p 都被筛选出来。
在 foldr 的情况下,分析表明“go”只是为了加快运行时间而定义的,而不是为了提高可读性。这是一个更容易分析的替代定义:
foldr :: (a -> b -> b) -> b -> [a] -> b
foldr (.:) e (x:xs) = x .: (foldr (.:) e xs)
foldr (.:) e [] = e
我在这里描述了它的功能(没有分析)。
要训练这种类型的分析,您可能需要阅读一些标准库的源代码;即Data.List源中的scanl 、scanr、展开器。