Ich habe einige Codes, die Struktur entspricht dies hat:Wie stelle ich die Effizienz bei Teilanwendungen in reinem Haskell sicher?
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
d.h. f1
ist eine Funktion, die ein teures Ergebnis auf der Grundlage der ersten beiden Argumente berechnet, und verwendet dann diese mit dem dritten Argumente. Ich hatte gehofft, dass eine teilweise Anwendung auf die ersten beiden Argumente und die wiederholte Anwendung des dritten Arguments dazu führen würden, dass die teure Berechnung nur einmal ausgeführt würde. Leider ist dies nicht der Fall:
test1 = do
putStrLn "test 1"
let p = f1 2 3
print (p 0.1)
print (p 0.2)
print (p 0.3)
Ergebnisse in:
*Main> test1
test 1
call cheap
call expensive
6.1
call cheap
call expensive
6.2
call cheap
call expensive
6.3
*Main>
ich habe kommen mit, was scheint eine Lösung zu sein:
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)
in resultierenden:
*Main> test2
test 2
call cheap
call expensive
6.1
call cheap
6.2
call cheap
6.3
*Main>
Aber das scheint ziemlich chaotisch . Gibt es eine sauberere Möglichkeit, die redundanten Aufrufe an die teure Kalkulation zu vermeiden?
Testen Sie auch solche Dinge nicht in ghci, kompilieren Sie mit Optimierungen. In einfachen Fällen wie diesem teilt GHC dann 'p' auch mit der ursprünglichen Definition von' f1'. (Aber Sie sollten den Rat von dbaupp trotzdem verwenden, in komplizierteren Situationen kann der Compiler das Teilen nicht selbst einführen.) –
Ich habe hier bewusst ghci verwendet, da es sich auf die Optimierung als gefährlich erweist. Die Laufzeit des realen Programms unterscheidet sich um eine Größenordnung, wenn die Freigabe nicht erkannt wird. – timbod
Richtig, Sie sollten sich (blind) nicht auf Optimierungen verlassen. Deshalb habe ich gesagt, du solltest dem Rat von Dbaupp folgen. Wenn Sie jedoch wissen möchten, wie sich Ihr Code verhält, können Sie Ihren Code nicht wirklich testen. Der Code, den Sie in ghci testen, ist anders, er kann völlig andere Eigenschaften haben als der echte Code. GHCi eignet sich hervorragend zum Testen der Korrektheit, aber weder für die Geschwindigkeit noch für die Speichernutzung. –