13
4

3 回答 3

4

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。感谢您提供这些链接。这是个很棒的资源。

于 2010-12-10T12:39:23.910 回答
3

这绝不是对您问题的完整答复,但我在 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))"
于 2010-12-10T07:45:11.893 回答
1

这是一个未提出问题的答案,可以将其视为一个长评论。

(如果您认为它不适合,请仅在低于 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、展开器。

于 2010-12-23T21:31:14.940 回答