gpt4 book ai didi

haskell - 如何强制 GHC 仅计算静态表达式一次

转载 作者:行者123 更新时间:2023-12-02 05:57:20 27 4
gpt4 key购买 nike

我有一个类似 SQL join 的简单示例,用于有序列表:如果 outer 参数为 True 则它是 union;否则是交集:

import System.Environment

main = do
[arg] <- getArgs
let outer = arg == "outer"
print $ length $ joinLists outer [1..1000] [1,3..1000]

joinLists :: (Ord a, Num a) => Bool -> [a] -> [a] -> [a]
joinLists outer xs ys = go xs ys
where
go [] _ = []
go _ [] = []
go xs@(x:xs') ys@(y:ys') = case compare x y of
LT -> append x $ go xs' ys
GT -> append y $ go xs ys'
EQ -> x : go xs' ys'
append k = if {-# SCC "isOuter" #-} outer then (k :) else id

当我分析它时,我发现每次调用 append 时都会评估 isOuter 条件:

stack ghc -- -O2 -prof example.hs && ./example outer +RTS -p && cat example.prof 

individual inherited
COST CENTRE MODULE no. entries %time %alloc %time %alloc
MAIN MAIN 44 0 0.0 34.6 0.0 100.0
isOuter Main 88 499 0.0 0.0 0.0 0.0

但我希望条件仅计算一次,因此 go 循环中的 append 替换为 (k :)id。我可以以某种方式强制它吗?与内存有关吗?

编辑:似乎我误解了探查器的输出。我向 append 定义添加了跟踪:

append k = if trace "outer" outer then (k :) else id

并且 outer 仅打印一次。

编辑2:如果我用无点定义替换append,则if条件仅评估一次:

 append = if outer then (:) else flip const

最佳答案

我会尝试将 lambda 向内推:

append = if {-# SCC "isOuter" #-} outer then \k -> (k :) else \k -> id

原始代码本质上是\k -> if external ...,它首先接受参数,然后测试防护。上面的代码在接受参数之前测试了守卫。

替代方案:

append | outer     = \k -> (k :) 
| otherwise = \k -> id

可以进一步将这些 lambda 简化为更易读的形式。

关于haskell - 如何强制 GHC 仅计算静态表达式一次,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40409184/

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