Übersetzen „Warum Functional Programming Matters“ in Haskell
-
05-07-2019 - |
Frage
Für kulturelle und intellektuelle Bereicherung, habe ich beschlossen, ein wenig Haskell zu lernen. Ich habe rel="noreferrer"> Hughes und ich versuche seinen Code in wahres Haskell zu übersetzen. Ich habe einige meiner Versuch unten angebracht (für die numerischen Teile des Papiers, das Alpha-Beta-Algorithmus ist noch interessanter, aber ich würde auch ein Spiel Auswerter von Grund auf neu schreiben müssen!).
An diesem Punkt ist es eher eine Übung gewesen in Haskell Syntax als alles andere. Ich habe schon einfache Dinge getan wie repeat
in die native Haskell iterate
zu übersetzen, ein paar Funktionen übersetzen, die eine Menge von Klammern verwendet Zusammensetzung zu funktionieren (sie mehr punktfrei in den Prozess zu machen), etc.
Aber mein Code funktioniert sicher, und ich frage mich, ob es ausreichend „Haskell-ish“ ist. Kann keine Experten gibt einige Hinweise geben, mich aus?
-- 4.1 Newton-Raphson square roots
next n x = (x + n/x)/2.0
-- -- this is "iterate::(a->a)->a->[a]"
-- repeat f a = a : iterate f (f a)
within eps (a:b:rest) =
if abs(a-b) <= eps
then b
else within eps (b:rest)
sqroot a0 eps n = within eps (iterate (next n) a0)
relative eps (a:b:rest) =
if abs(a-b) <= eps*abs(b)
then b
else relative eps (b:rest)
relativesqrt a0 eps n = relative eps (iterate (next n) a0)
-- 4.2 numerical differentiation
easydiff f x h = (f (x+h) - f x) / h
differentiate h0 f x = map (easydiff f x) (iterate (/2) h0)
-- diff1a h0 eps f x = within eps (differentiate h0 f x)
diff1 h0 eps f = within eps . differentiate h0 f
elimerror n (a:b:rest) = (b*(2**n)-a)/(2**n-1) : elimerror n (b:rest)
-- need fromIntegral to make a non-integer out of the Int which comes out of round
order (a:b:c:rest) = fromIntegral (round (logBase 2 ((a-c)/(b-c)-1)))
improve s = elimerror (order s) s
--diff2a h0 eps f x = within eps (improve (differentiate h0 f x))
diff2 h0 eps f = within eps . improve . differentiate h0 f
--super s = map second (iterate improve s) -- how can we make this point-free?
super :: (RealFrac t, Floating t) => [t] -> [t]
-- w/o this it wants to be [double]->[double]
super = map second . iterate improve
-- second (a:b:rest) = b
second = head . tail
diff3 h0 eps f = within eps . super . differentiate h0 f
-- 4.3 integration
easyintegrate f a b = (f a + f b)*(b-a)/2
-- addpair becomes (uncurry (+))
integrate f a b = integ f a b (f a) (f b)
integ f a b fa fb =
(fa+fb)*(b-a)/2 : map (uncurry (+)) (zip (integ f a m fa fm) (integ f m b fm fb))
where m = (a+b)/2
fm = f m
-- test: following should be about pi
approxpi eps = within eps (improve (integrate (\x -> 4/(1+x*x)) 0 1))
superpi eps = within eps (super (integrate (\x -> 4/(1+x*x)) 0 1))
-- is there any way to keep track of the number of iterations? state monad, but seems like a lot of work...\
Keine korrekte Lösung
Andere Tipps
4.1 Newton-Raphson Quadratwurzeln
Diese beiden Linien
sqroot a0 eps n = within eps (iterate (next n) a0)
relativesqrt a0 eps n = relative eps (iterate (next n) a0)
sind fast identisch, so konnte man abstrakte Dinge einen Schritt weiter:
sqroot method a0 eps n = method eps (iterate (next n) a0)
relativesqrt = sqroot relative
withinsqrt = sqroot within
4.2 Numerische Differenzierung
Ich sehe den Punkt nicht h0
als Argument an die differentiate
Funktion als es ist nur der Ausgangspunkt für die 0
Begrenzungssequenz aufweist. (Das gleiche gilt nicht für a0
in dem Newton-Rhapson Fall, wo der Ausgangspunkt Rolle könnte).
Ich denke, es ist gleichermaßen geeignet ist, die Geschwindigkeit zu abstrakt über deren diese Grenze gegen Null geht:
differentiate rate f x = map (easydiff f x) (iterate rate 1)
Natürlich könnte man beides tun:
differentiate rate h0 f x = map (easydiff f x) (iterate rate h0)
In jedem Fall ist es eine ziemlich willkürliche Entscheidung.
4.2 Integration
Sie können mit
zipWith (+) (integ f a m fa fm) (integ f m b fm fb)
statt
map (uncurry (+)) (zip (integ f a m fa fm) (integ f m b fm fb))
, die ich denke, mehr lesbar ist.
Für within
und relative
würde ich bewachte Notation:
within eps (a:b:rest)
| abs(a-b)<=eps = b
| otherwise = within eps (b:rest)
Für second
, könnten Sie !! 1
schreiben. Vor allem, dass letzte ist persönliche Präferenz, ich denke.
Sie sollten auch die Art Signaturen geben.
Bearbeiten : Wenn Sie in den Verschleierungs sind, versuchen Sie:
within :: (Ord a, Num a) => a -> [a] -> a
within eps l@(_:xs) = snd. head . filter ((<= eps) . fst) $ zip zs xs
where
zs = zipWith (\ a b -> abs (a-b)) l xs
(Typ geprüft, nicht getestet - und ich weiß nie, ob ich die Filter richtig zu machen, oder wenn er negiert werden;)
Roger Costello hat zwei Teile Zusammenfassung der John Hughes Papier geschrieben Übersetzung der ursprünglichen Miranda Code zu Haskell. Hier sind Teil einer und Teil zwei seiner Zuschreibung.
Also, das ist eine gerade über Übersetzung (dh versuchen, den Code zu halten, so ähnlich wie möglich suchen) oder ein eingebettetes (dh wie viele idiomatische kleine Änderungen an dem Code wie möglich machen, ohne dass die Beispiele härter zu verstehen , natürlich) ein?
Unter der Annahme, dass dieses „Projekt“ ist noch im Gang ......