2012-09-05 6 views
7

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?

+3

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.) –

+0

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

+0

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. –

risposta

9

È possibile inserire il terzo argomento all'interno dello let, in modo che x sia condiviso.

f2 a b = let x = expensive a b in \c -> cheap x c 

(in questo caso f2 a b = let x = expensive a b in cheap x funziona anche.)


Quello che state cercando è compilatore-driven partial evaluation, e questo è un problema difficile ... almeno è sufficientemente difficile implementare correttamente che non è in GHC.

+0

(È stato veloce!) Confermo che in effetti risolve il mio problema. Fino ad ora, avrei considerato il mio f1 e il tuo f2 come equivalenti. Dovrò tenere a mente questa trasformazione in futuro ogni volta che scriverò funzioni destinate ad essere parzialmente applicate. – timbod

+0

@timbod il tuo 'f1' è' \ a -> \ b -> \ c-> lascia x = expens ab in economico xc', e 'f2' qui è' \ a -> \ b-> lascia x = expens ab in \ c-> cheap xc'. Quale per * eta-riduzione * è '\ a -> \ b-> lascia x = espande un b in x economico» (anche qui). Che è esattamente equivalente a * your 'f2' *, :) non c'è nemmeno boxe coinvolto perché' newtype' viene eliminato in fase di compilazione. 'newtype' viene in genere usato per consentire un'implementazione' instance' alternativa di alcuni typeclass o qualche tipo di trucco. Per * float * that * let * 'x = expens a b' sopra' \ c-> '* lambda * è una mossa audace, dura 4 _a compiler_ per decidere se ne vale la pena o meno. –

Problemi correlati