2010-08-04 12 views
5

Ich habe kürzlich Haskell gelernt und sprach mit einem Freund, der durch SICP arbeitet. Wir waren neugierig, Common Lisp und Scheme zu vergleichen, und so entschied ich mich als Übung zu versuchen, Übung 1.29 in Haskell zu übersetzen.Haskell numerische Hierarchie in SICP-Übungen

Diese Übung verwendet eine Funktion Sigma, die die mathematische Summenfunktion Sigma darstellt. Diese Funktion benötigt eine Funktion f, die auf jeden Term angewendet wird, eine untere Grenze, eine Funktion, die auf jeden Term angewendet wird, um den nächsten Term zu erhalten, und eine obere Grenze. Es gibt die Summe von f zurück, die auf jeden Ausdruck angewendet wird.

simpsonIntegral soll Simpsons Regel verwenden, um das Integral der Funktion f über den Bereich [a, b] mit einer "Genauigkeit" n anzunähern. Ich habe Probleme, diese Funktion zu bekommen, weil es etwas zu verstehen scheint, was ich nicht über die beteiligten Typen verstehe.

Dieser Code wird mit der Version 6.12.1 von ghc kompiliert, aber simpsonIntegral wird einen Typkontext erhalten (Integral a, Fractional a), der keinen Sinn ergibt und die Funktion explodiert, sobald Sie sie aufrufen. Ich habe das an einem Punkt funktioniert, aber was ich getan habe, war so offensichtlich ein Hack, den ich hier fragen wollte, wie das idiomatisch gehandhabt werden würde.

Wie behandelt man idiomatisch die Integral -> Fractional/Real-Konvertierung in h benötigt? Ich las eine Reihe von Dingen, aber nichts schien offensichtlich und sauber.

sigma :: (Ord a, Num b) => (a -> b) -> a -> (a -> a) -> a -> b 
sigma f a next b = iter a 0 
    where 
    iter current acc | current > b = acc 
        | otherwise = iter (next current) (acc + f current) 

simpsonIntegral f a b n = 1.0 * (h/3) * (sigma simTerm 0 (1+) n) 
    where 
    h = (b - a)/n 
    simTerm k = (yk k) * term 
     where 
     yk k = f (a + h * k) 
     term = 
      case k of 
      0 -> 1 
      1 -> 1 
      otherwise -> if odd k then 4 else 2 

Antwort

3

Um oben auf Gerechtigkeit Antwort zu folgen: Wenn Sie neugierig sind darüber, wo die fromIntegral s zu setzen, werden die folgenden compiliert:

simpsonIntegral :: (Integral a, Fractional b) => (b -> b) -> a -> a -> a -> b 
simpsonIntegral f a b n = 1.0 * (h/3) * (sigma simTerm 0 (1+) n) 
    where 
    h = fromIntegral (b - a)/fromIntegral n 
    simTerm k = (yk k) * term 
     where 
     yk k = f (fromIntegral a + h * fromIntegral k) 
     term = 
      case k of 
      0 -> 1 
      1 -> 1 
      otherwise -> if odd k then 4 else 2 

Und scheint zu funktionieren:

*Main> simpsonIntegral (^3) 0 1 100 
0.2533333233333334 
*Main> simpsonIntegral (^3) 0 1 1000 
0.2503333333323334 
6
fromIntegral :: (Integral a, Num b) => a -> b 

r = fromIntegral i 
+0

Frage zu fromIntegral. Ich probierte dies, aber nicht an den richtigen Stellen per Travis 'Antwort, und ich dachte, dass es nicht funktionierte, weil nach diesem http://www.haskell.org/tutorial/numbers.html num keinen Divisionsoperator zur Verfügung stellt. Ist fromIntegral polymorph auf den Rückgabetyp und ghc leitet, dass ich einen Fractional Return-Typ möchte? – asm

+0

Ja, 'fromIntegral' ist in seinem Rückgabetyp' b' polymorph, da 'b' (der Rückgabetyp) ein beliebiger Datentyp sein darf, der ein Mitglied der' Num' -Typenklasse ist. Während die Klasse vom Typ "Num" keine Division bietet, stellen einige Datentypen, die Mitglieder dieser Typklasse sind, eine Division bereit, z. B. "Double". – yfeldblum

1

Das Problem ist, dass die Funktion "ungerade" erwartet, dass es ein Integral-Argument ist. Der Compiler sagt dann, dass Ihre Variable "k" vom Typ Integral ist. Mit der Operation "/" leitet der Compiler "k" aber auch vom Typ Fractional ab. Die Lösung kann so einfach sein wie die Umwandlung „k“ Integer, wo es wirklich benötigt wird:

if odd (round k) then 4 else 2 

Wenn Sie mehr über numerische Umwandlung in Haskell lernen, überprüfen Converting_numbers

Als Randbemerkung, hier ist eine andere Art des Schreibens Ihrer Sigma-Funktion:

sigma f a next b = sum $ map f $ takeWhile (<= b) $ iterate next a