Ho qualche codice che ha struttura equivalente a questo:Come si garantisce l'efficienza quando si utilizza un'applicazione parziale in Haskell puro?
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
cioè f1
è una funzione che calcola un risultato costoso basata sui primi due argomenti, e quindi utilizza questo con il terzo parametro. Speravo che un'applicazione parziale sui primi 2 argomenti, quindi l'applicazione ripetuta del terzo argomento avrebbe comportato il calcolo costoso solo in esecuzione una volta. Purtroppo questo non è il caso:
test1 = do
putStrLn "test 1"
let p = f1 2 3
print (p 0.1)
print (p 0.2)
print (p 0.3)
risultati in:
*Main> test1
test 1
call cheap
call expensive
6.1
call cheap
call expensive
6.2
call cheap
call expensive
6.3
*Main>
mi è venuta in mente quello che sembra essere una soluzione:
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)
conseguente:
*Main> test2
test 2
call cheap
call expensive
6.1
call cheap
6.2
call cheap
6.3
*Main>
Ma questo sembra abbastanza disordinato . C'è un modo più pulito per evitare le chiamate ridondanti al costoso calc?
Inoltre, non testare tali elementi in ghci, compilare con ottimizzazioni. In casi semplici come questo, GHC condividerà quindi 'p' anche con la definizione originale di' f1'. (Ma dovresti usare il consiglio di dbaupp, tuttavia, in situazioni più complicate, il compilatore potrebbe non essere in grado di introdurre la condivisione stessa.) –
Ho usato consapevolmente ghci qui, poiché contare su ottimizzazioni sembra pericoloso. Il tempo di esecuzione del programma reale sarà diverso per un ordine di grandezza se la condivisione non viene scoperta. – timbod
Giusto, non dovresti (ciecamente) fare affidamento sull'ottimizzazione. Ecco perché ho detto che dovresti seguire il consiglio di dbaupp. Tuttavia, se vuoi sapere come si comporterà il tuo codice, non c'è alcun sostituto per testare realmente il tuo codice. Il codice che stai testando in ghci è diverso, può avere caratteristiche completamente diverse rispetto al codice reale. GHCi è ottimo per testare la correttezza, ma né per la velocità né per l'utilizzo della memoria. –