我有一些代码,它的结构等同于:
import Debug.Trace
newtype SomeExpensiveHiddenType = SCHT Double
expensive :: Double -> Double -> SomeExpensiveHiddenType
expensive a b = SCHT $ trace "call expensive" (*) a b
cheap :: SomeExpensiveHiddenType -> Double -> Double
cheap (SCHT x) c = trace "call cheap" (+) x c
f1 :: Double -> Double -> Double -> Double
f1 a b c = let x = expensive a b in cheap x c例如,f1是一个函数,它根据前两个参数计算代价高昂的结果,然后将其与第三个参数一起使用。我曾希望对前两个参数的部分应用程序,然后重复应用第三个参数,将导致昂贵的计算只运行一次。不幸的是,事实并非如此:
test1 = do
putStrLn "test 1"
let p = f1 2 3
print (p 0.1)
print (p 0.2)
print (p 0.3)结果如下:
*Main> test1
test 1
call cheap
call expensive
6.1
call cheap
call expensive
6.2
call cheap
call expensive
6.3
*Main> 我想出了一个似乎是解决方案:
newtype X a = X { unX :: a }
f2 :: Double -> Double -> X (Double -> Double)
f2 a b = let x = expensive a b in X (cheap x)
test2 = do
putStrLn "test 2"
let p = unX $ f2 2 3
print (p 0.1)
print (p 0.2)
print (p 0.3)结果是:
*Main> test2
test 2
call cheap
call expensive
6.1
call cheap
6.2
call cheap
6.3
*Main> 但这看起来相当混乱。有没有一种更干净的方法可以避免对昂贵的calc的冗余调用?
发布于 2012-09-05 21:37:46
您可以只将第三个参数放在let中,以便共享x。
f2 a b = let x = expensive a b in \c -> cheap x c(在这种情况下,f2 a b = let x = expensive a b in cheap x也可以使用。)
你正在寻找的是编译器驱动的partial evaluation,这是一个很难解决的问题……至少它很难正确地实现,以至于它不在GHC中。
https://stackoverflow.com/questions/12282717
复制相似问题