it-swarm.dev

Potenzierung in Haskell

Kann mir jemand sagen, warum das Haskell-Präludium zwei separate Funktionen für die Potenzierung definiert (d. H. ^ und **)? Ich dachte, das Typensystem sollte diese Art der Verdoppelung beseitigen.

Prelude> 2^2
4
Prelude> 4**0.5
2.0
82
skytreebird

Es gibt tatsächlich drei Potenzierungsoperatoren: (^), (^^) Und (**). ^ Ist eine nicht negative ganzzahlige Exponentiation, ^^ Eine ganzzahlige Exponentiation und ** Eine Gleitkomma-Exponentiation:

(^) :: (Num a, Integral b) => a -> b -> a
(^^) :: (Fractional a, Integral b) => a -> b -> a
(**) :: Floating a => a -> a -> a

Der Grund ist die Typensicherheit: Ergebnisse numerischer Operationen haben im Allgemeinen den gleichen Typ wie die Eingabeargumente. Sie können jedoch ein Int nicht zu einer Gleitkomma-Potenz erhöhen und erhalten ein Ergebnis vom Typ Int. Das Typensystem verhindert dies: (1::Int) ** 0.5 Erzeugt einen Typfehler. Gleiches gilt für (1::Int) ^^ (-1).

Anders ausgedrückt: Num Typen werden unter ^ Geschlossen (sie müssen keine multiplikative Inverse haben), Fractional Typen werden unter ^^ Geschlossen , Floating Typen werden unter ** geschlossen. Da es keine Instanz von Fractional für Int gibt, können Sie sie nicht auf eine negative Potenz erhöhen.

Idealerweise ist das zweite Argument von ^ Statisch nicht negativ (derzeit löst 1 ^ (-2) eine Laufzeitausnahme aus). Es gibt jedoch keinen Typ für natürliche Zahlen in Prelude.

121

Das Typensystem von Haskell ist nicht leistungsfähig genug, um die drei Potenzierungsoperatoren als einen auszudrücken. Was Sie wirklich wollen, ist etwa so:

class Exp a b where (^) :: a -> b -> a
instance (Num a,        Integral b) => Exp a b where ... -- current ^
instance (Fractional a, Integral b) => Exp a b where ... -- current ^^
instance (Floating a,   Floating b) => Exp a b where ... -- current **

Dies funktioniert nicht wirklich, selbst wenn Sie die Klassenerweiterung für mehrere Parameter aktivieren, da die Instanzauswahl klüger sein muss, als dies Haskell derzeit zulässt.

28
augustss

Es definiert nicht zwei Operatoren - es definiert drei! Aus dem Bericht:

Es gibt drei Potenzierungsoperationen mit zwei Argumenten: (^) erhöht eine beliebige Zahl auf eine nichtnegative Ganzzahl, (^^) erhöht eine gebrochene Zahl auf eine beliebige ganzzahlige Potenz und (**) nimmt zwei Gleitkomma-Argumente an. Der Wert von x^0 oder x^^0 ist 1 für jedes x, einschließlich Null; 0**y ist nicht definiert.

Das heißt, es gibt drei verschiedene Algorithmen, von denen zwei exakte Ergebnisse liefern (^ und ^^), während ** gibt ungefähre Ergebnisse. Durch Auswahl des zu verwendenden Operators wählen Sie den aufzurufenden Algorithmus.

10
Gabe

Für ^ Muss das zweite Argument ein Integral sein. Wenn ich mich nicht irre, kann die Implementierung effizienter sein, wenn Sie wissen, dass Sie mit einem integralen Exponenten arbeiten. Auch wenn Sie etwas wie 2 ^ (1.234) wollen, ist Ihr Ergebnis offensichtlich ein Bruchteil, obwohl Ihre Basis ein Integral 2 ist. Sie haben mehr Optionen, damit Sie genauer steuern können, welche Typen in Ihre Exponentiationsfunktion ein- und ausgehen.

Das Typensystem von Haskell hat nicht das gleiche Ziel wie andere Typensysteme wie C, Python oder LISP. Duck Typing ist (fast) das Gegenteil der Haskell-Denkweise.

4
Dan Burton