mam kod, który ma strukturę podobną do odpowiadającej poniżej:Jak zapewnić efektywność podczas stosowania częściowego zastosowania w czystym Haskell?
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
jest to funkcja, która oblicza drogi wyniku na podstawie pierwszych dwóch argumentów, a następnie za pomocą tego z trzecim argumentem. Miałem nadzieję, że częściowa aplikacja na pierwszych 2 argumentach, a następnie powtórzone zastosowanie trzeciego argumentu spowoduje, że kosztowne obliczenia będą wykonywane tylko raz. Niestety nie jest to przypadek:
test1 = do
putStrLn "test 1"
let p = f1 2 3
print (p 0.1)
print (p 0.2)
print (p 0.3)
skutkuje:
*Main> test1
test 1
call cheap
call expensive
6.1
call cheap
call expensive
6.2
call cheap
call expensive
6.3
*Main>
mam wymyślić, co wydaje się być rozwiązaniem:
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)
skutkuje:
*Main> test2
test 2
call cheap
call expensive
6.1
call cheap
6.2
call cheap
6.3
*Main>
Ale wydaje się to dość niechlujne . Czy istnieje lepszy sposób na uniknięcie zbędnych połączeń z drogim kalkulatorem?
Ponadto, nie testuj takich rzeczy w ghci, kompiluj z optymalizacją. W prostych przypadkach, takich jak ten, GHC będzie wtedy udostępniać 'p' nawet z oryginalną definicją' f1'. (Ale powinieneś skorzystać z porady dbaupp, jednak w bardziej skomplikowanych sytuacjach kompilator może nie być w stanie przedstawić samego udostępniania.) –
Świadomie użyłem tutaj ghci, ponieważ poleganie na optymalizacjach wydaje się niebezpieczne. Środowisko wykonawcze programu rzeczywistego różni się o rząd wielkości, jeśli udostępnianie nie zostanie wykryte. – timbod
Dobrze, nie powinieneś (na ślepo) polegać na optymalizacjach. Dlatego powiedziałem, że powinieneś przestrzegać porady Dbauppa. Niemniej jednak, jeśli chcesz się dowiedzieć, jak zachowa się twój kod, nie ma żadnego substytutu dla testowania twojego kodu. Kod, który testujesz w ghci, jest inny, może mieć zupełnie inną charakterystykę niż prawdziwy kod. GHCi doskonale nadaje się do testowania poprawności, ale nie do prędkości ani użycia pamięci. –