7

我有一些结构与此等效的代码:

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

ief1是一个基于前两个参数计算昂贵结果的函数,然后将其与第三个参数一起使用。我曾希望前两个参数的部分应用,然后重复应用第三个参数会导致昂贵的计算只运行一次。不幸的是,这种情况并非如此:

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> 

但这似乎很混乱。有没有一种更简洁的方法可以避免对昂贵计算的冗余调用?

4

1 回答 1

9

您可以将第三个参数放在 , 中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也可以。)


您正在寻找的是编译器驱动的部分评估,这是一个难题......至少它很难正确实现,它不在 GHC 中。

于 2012-09-05T13:37:46.250 回答