Haskell是否存在方程扩展器?
类似于foldr.com:1+(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的理解。
此外,我认为它还可以帮助解决寻求优化Haskell代码的问题:
是否有扩展Haskell表达式的工具?
答案 0 :(得分:4)
David V.感谢您提供这些链接。 Repr
绝对值得添加到我的工具箱中。我想添加一些我认为有用的额外库。
HackageDB : Trace (截至2010年12月12日)
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”中,
Sieve应该在p之后返回素数列表,所以至少要对下一个素数进行评估。
函数定义的其余部分是递归,其中下一个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(没有分析)。
要训练此类分析,您可能需要阅读一些标准库的来源;即source的Data.List中的scanl,scanr,unfoldr。