Haskell:Equation Expander 1+(1+(1+(1+(...))))=∞

时间:2010-12-10 00:52:05

标签: haskell expression equation expansion equational-reasoning

Haskell是否存在方程扩展器?

类似于foldr.com1+(1+(1+(1+(…))))=∞

我是Haskell的新手我很难理解为什么某些方程比其他方程更优选。我认为如果我能看到方程扩展会有所帮助。

例如,我发现foldr vs foldl一开始很难理解,直到我看到它们被扩展。

foldr :: (a -> b -> b) -> b -> [a] -> b
foldr k z xs = go xs
             where
               go []     = z
               go (y:ys) = y `k` go ys

foldl :: (a -> b -> a) -> a -> [b] -> a
foldl f z0 xs0 = lgo z0 xs0
             where
                lgo z []     =  z
                lgo z (x:xs) = lgo (f z x) xs

从定义中我可以看到foldr扩展如下:

foldr (+) 0 [1..1000000] -->
1 + (foldr (+) 0 [2..1000000]) -->
1 + (2 + (foldr (+) 0 [3..1000000])) -->
1 + (2 + (3 + (foldr (+) 0 [4..1000000]))) -->
1 + (2 + (3 + (4 + (foldr (+) 0 [5..1000000])))) -->

foldl扩展如下:

foldl (+) 0 [1..1000000] -->
foldl (+) (foldl (+) 0 [1]) [2..1000000]) --> 
foldl (+) (foldl (+) (foldl (+) 0 [1])) [3..1000000]) --> 

或来自Haskell Wiki on foldr fold foldl'

let z1 =  0 + 1
in foldl (+) z1 [2..1000000] -->

let z1 =  0 + 1
    z2 = z1 + 2
in foldl (+) z2 [3..1000000] -->

let z1 =  0 + 1
    z2 = z1 + 2
    z3 = z2 + 3
in foldl (+) z3 [4..1000000] -->

let z1 =  0 + 1
    z2 = z1 + 2
    z3 = z2 + 3
    z4 = z3 + 4
in foldl (+) z4 [5..1000000] -->

然而,我在更大的方程式上遇到麻烦,理解为什么事情就像他们在Haskell中那样工作。例如,第一个筛选功能使用1000个过滤器,而第二个筛选功能仅需24个来找到1001个过滤器。

primes = sieve [2..]
   where
    sieve (p:xs) = p : sieve [x | x <- xs, rem x p /= 0] 



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

Haskell Wiki on Primes

我花了很长时间锻炼并手工扩展。我已经了解它是如何工作的。但是,扩展某些表达式的自动化工具将大大提高我对Haskell的理解。

此外,我认为它还可以帮助解决寻求优化Haskell代码的问题:

是否有扩展Haskell表达式的工具?

3 个答案:

答案 0 :(得分:4)

David V.感谢您提供这些链接。 Repr绝对值得添加到我的工具箱中。我想添加一些我认为有用的额外库。

HackageDB : Trace (截至2010年12月12日)

  • ghc-events图书馆和程序:用于解析GHC .eventlog文件的库和工具
  • hood library:通过就地观察进行调试
  • hpc-strobe library:用于运行Haskell程序的Hpc生成的选通
  • hpc-tracer程序:带AJAX接口的Tracer

Hook包似乎是我正在寻找的。我今天晚些时候会发布更多样本。

<强> Hood

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

答案 1 :(得分: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))"

答案 2 :(得分:1)

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

(如果您认为它不合适,请仅在0以下进行下调。我将删除它。)


只要你有点经验,你可能就不想再看到事物的扩展方式了。你会想要了解事情是如何运作的,然后取代它为何起作用的问题;通过观察它如何扩展,你将不会获得太多收益。

分析代码的方法比您想象的要简单得多:只需将每个参数/变量标记为“已评估”或“未评估”或“待评估”,具体取决于其因果关系的进展情况。

两个例子:


1。) fibs

所有Fibonacci数字的列表是

fibs :: (Num a) => [a]
fibs = 1 : 1 : zipWith (+) fibs (tail fibs)

前两个要素已经过评估;因此,将第3个元素(其值为2)标记为待评估,并将所有剩余的元素标记为未评估。然后第三个元素将是纤维和尾纤的第一个元素的(+) - 组合,它们将是纤维的第一和第二元素,它们已被标记为已评估。这适用于待评估的第n个元素和分别已经评估过的(n-2)和(n-1)个元素。

您可以通过不同方式对此进行可视化,例如:

  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&lt; P 2; h只包含素数因为xs既不包含p,也不包含任何可被p下面任何素数整除的数字。
  • t包含p²以上的所有x数。 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

我已经描述了它的功能here(没有分析)。

要训练此类分析,您可能需要阅读一些标准库的来源;即sourceData.List中的scanl,scanr,unfoldr。