I tienen un código que tiene una estructura equivalente a esto:¿Cómo puedo garantizar la eficacia cuando uso la aplicación parcial en 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
es decir f1
es una función que calcula un resultado caro en base a los dos primeros argumentos y, a continuación, utiliza este con el tercer argumento. Tenía la esperanza de que una aplicación parcial en los primeros 2 argumentos, luego la aplicación repetida del tercer argumento daría lugar a que el costoso cálculo solo se ejecutara una vez. Por desgracia, este no es el caso:
test1 = do
putStrLn "test 1"
let p = f1 2 3
print (p 0.1)
print (p 0.2)
print (p 0.3)
resultados en:
*Main> test1
test 1
call cheap
call expensive
6.1
call cheap
call expensive
6.2
call cheap
call expensive
6.3
*Main>
Yo he llegado con lo que parece ser una solución:
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)
lo que resulta en:
*Main> test2
test 2
call cheap
call expensive
6.1
call cheap
6.2
call cheap
6.3
*Main>
Pero esto parece bastante complicado . ¿Hay alguna manera más limpia de evitar las llamadas redundantes al costoso calc?
Además, no pruebe tales cosas en ghci, compile con optimizaciones. En casos simples como este, GHC compartirá 'p' incluso con la definición original de' f1'. (Pero debe usar el consejo de dbaupp, sin embargo, en situaciones más complicadas, el compilador puede no ser capaz de introducir el intercambio.) –
Utilicé ghci de forma consciente, ya que confiar en las optimizaciones parece peligroso. El tiempo de ejecución del programa real diferirá en un orden de magnitud si no se descubre el intercambio. – timbod
Correcto, no deberías (ciegamente) confiar en las optimizaciones. Por eso dije que deberías seguir los consejos de dbaupp. Sin embargo, si desea saber cómo se comportará su código, no hay sustituto para probar realmente su código. El código que está probando en ghci es diferente, puede tener características completamente diferentes que el código real. GHCi es ideal para probar la corrección, pero no para la velocidad ni el uso de la memoria. –