gpt4 book ai didi

haskell - 同态中的森林砍伐

转载 作者:行者123 更新时间:2023-12-03 15:03:30 25 4
gpt4 key购买 nike

维基百科写到 Hylomorphism :

In [...] functional programming a hylomorphism is a recursive function, corresponding to the composition of an anamorphism (which first builds a set of results; also known as 'unfolding') followed by a catamorphism (which then folds these results into a final return value). Fusion of these two recursive computations into a single recursive pattern then avoids building the intermediate data structure. This is an example of deforestation, a program optimization strategy.



(我的粗体标记)

使用 recursion-schemes图书馆
我写了一个非常简单的hylomorphism:
import Data.Functor.Foldable
main :: IO ()
main = putStrLn $ show $ hylosum 1000

hylosum :: Int -> Int
hylosum end = hylo alg coalg 1
where
-- Create list of Int's from 1 to n
coalg :: Int -> ListF Int Int
coalg n
| n > end = Nil
| otherwise = Cons n (n + 1)
-- Sum up a list of Int's
alg :: ListF Int Int -> Int
alg Nil = 0
alg (Cons a x) = a + x

在 cabal 文件中,我指示 GHC 优化代码:
name:                Hylo
version: 0.1.0.0
synopsis: Hylomorphisms and Deforestation
build-type: Simple
cabal-version: >=1.10

executable Hylo
main-is: Main.hs
ghc-options: -O2
build-depends: base >=4.10 && <4.11 , recursion-schemes
default-language: Haskell2010

使用 stackage lts-10.0 (GHC 8.2.2) 我用 stack build 编译并使用 stack exec Hylo -- +RTS -s 运行我得到:
500500
84,016 bytes allocated in the heap
3,408 bytes copied during GC
44,504 bytes maximum residency (1 sample(s))
25,128 bytes maximum slop
2 MB total memory in use (0 MB lost due to fragmentation)

现在我更改 hylosum 1000hylosum 1000000 (1000 倍以上),我得到:
500000500000
16,664,864 bytes allocated in the heap
16,928 bytes copied during GC
15,756,232 bytes maximum residency (4 sample(s))
29,224 bytes maximum slop
18 MB total memory in use (0 MB lost due to fragmentation)

因此堆使用量从 84 KB 增加到 16,664 KB。这比以前多了200倍。
因此我认为,GHC 不会做维基百科中提到的森林砍伐/融合!

这并不奇怪:变形从左到右创建列表项
(从 1 到 n)和 catamorphism 从右到左以相反的方式消耗元素
(从 n 到 1)并且很难看出 hylomorphism 是如何工作的
无需创建整个中间列表。

问题:
GHC 是否能够执行森林砍伐?
如果是,我必须在我的代码或 cabal 文件中进行哪些更改?
如果是,它是如何真正起作用的?
如果不是,问题出在哪里:在维基百科、GHC 还是在图书馆?

最佳答案

数据结构实际上被融合了,但生成的程序不是尾递归的。优化后的代码基本上是这样的(看不到 ConsNil):

h n | n > end = 0
| otherwise = n + h (n+1)

要评估结果,您必须首先评估 h (n+1)递归,然后将结果添加到 n .在递归调用期间,值 n必须保持存储在某处,因此我们观察到内存使用量增加为 end增加。

通过使递归调用位于尾部位置并携带一个恒定大小的累加器,可以获得更紧密的循环。我们希望代码对此进行优化:
-- with BangPatterns
h n !acc | n > end = acc
| otherwise = h (n+1) (n + acc)

hylosum ,调用 (+)发生在 alg , 我们将其替换为对由 hylo 建立的延续的调用。 .
alg :: ListF Int (Int -> Int) -> Int -> Int
alg Nil acc = acc
alg (Cons n go) !acc = go (n + acc)

有了这个,我看到堆中分配了一个常量 51kB。

关于haskell - 同态中的森林砍伐,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49135351/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com