Ich bin irgendwie neu in Haskell und ich habe Schwierigkeiten zu verstehen, wie abgeleitete Typen und solche Arbeiten.
%Vor%Was genau bedeutet das?
%Vor%Was sind die Unterschiede zwischen diesen?
Und der abgeleitete Typ von
foldr Karte ist [a] - & gt; [a - & gt; a] - & gt; [a]
aber warum ist das so?
Danke!
Wenn Sie das Beispiel von
nehmen %Vor%Dies bedeutet, dass map zwei Argumente benötigt
Und es kommt zurück
Sie können das Muster bereits sehen, aber es wird klarer, wenn wir 'a' und 'b'
ersetzenAlso wird diese Typdefinition
sein %Vor%Nun ist es also eine Funktion, die einen String zurücknimmt und Int und eine Liste von Strings zurückgibt und eine Liste von Ints zurückgibt.
Sagen Sie unsere Funktion, die einen String übernimmt und zurückgibt, und Int ist read
. read
verwendet den String, den Sie ihm geben, um ihn in etwas anderes zu konvertieren. Da wir es hier in einen Int-Kontext stellen, wird die Zeichenkette in int
Dies wird zu
führen %Vor% weil map
die Funktion read
und Maps für jedes Element der Liste übernimmt (anwendet). Sie müssen nicht read
sagen, es ist ein Argument wie read "1"
oder read n
, weil map
das für Sie erledigt.
Und das ist wirklich alles, was dazu gehört. Der Typ einer Funktion sagt nur, welche Typen sie benötigt und welchen Typ sie zurückgibt. Natürlich gibt es auch Curry, aber dazu wirst du später entweder absichtlich kommen oder nicht! Es bedeutet im Grunde, dass eine Funktion nicht viele Argumente akzeptiert, sondern nur eins. Nehmen wir an, Sie nehmen die Funktion +
. Wenn Sie 1+2
auswerten, gibt es eine Funktion zurück, die eine andere Zahl übernimmt, die zu 1
hinzugefügt wird, und da es hier eine andere Zahl gibt, 2
, wird diese verwendet. Sie hätten es als (1+)
auswerten und weitergeben können, eventuell später die Nummer hinzugefügt. Es ist klarer, wenn Sie nicht die Infix-Syntax von +
haben. Es hätte (+) 1 2
geschrieben werden können, also wird diese Anweisung zuerst (+) 1
, was vom Typ ist (vereinfacht! Ich werde hier nicht die Klasse Num-Klasse einführen) Int -> Int
. Also (+) 1
(oder (1+)
) ist nur eine weitere Funktion, auf die Sie einen Wert anwenden können. An diesem Punkt kann das Ergebnis zu 3 rechnen.
So sieht das in der Praxis aus: (Ignoriere den (Num a) =>
-Teil hier, wenn er dich verwirrt, es ist ein Konzept, von dem du später mehr erfährst. Ersetze einfach die a
-Typen hier durch Int
, wenn du willst, und Ignoriere den (Num a) =>
Teil komplett.)
Und zu Ihrer zweiten Frage: Sie definieren abgeleitete Typen NICHT ALLE. Sie werden "abgeleitet" genannt, weil der Compiler / Interpreter die Typen selbst "leitet" (liest: berechnet), ohne sie explizit zu benennen.
Über die foldX
Unterschiede: Sie alle machen genau dasselbe: Reduzieren Sie eine Liste auf einen einzelnen Wert. Der Unterschied zwischen den Funktionen ist lediglich die interne Definition. foldl
faltet die Liste von links und foldr
macht sie rechts.
Um eine Liste zusammenzufassen, können Sie alle so verwenden ...
%Vor% Sie sehen, mit Ausnahme von foldl1, liefern Sie eine Funktion zum Falten, einen Startwert (Akkumulator) und eine Liste zum Falten. fold1
hat einen eigenen Akku, du gibst ihn nicht selbst.
In der Praxis sollten Sie besser foldr
verwenden, da es im Gegensatz zu fold
für BIG-Listen geeignet ist, ohne aufgrund eines Stack-Überlaufs abzustürzen (tee, hee). Und Ausnahme von dieser Regel ist foldl'
(beachte das "'"!) In Data.Map
- es ist eine strikte Faltung, die auch für große Listen geeignet ist.
Wenn Sie Funktionen ihre Typen selbst geben möchten (wenn Sie sie geschrieben haben), betrachten Sie dieses Beispiel:
%Vor%Oder auf haskellische Weise
%Vor% In der ersten Zeile der beiden Beispiele ( double :: Int -> Int
) suchen Sie. So können Sie den Compiler oder Interpreter zwingen, die Funktion zu identifizieren - Sie können sie dann weglassen, und der Compiler / Interpreter wird sie herausfinden (und manchmal sogar besser, als man zuerst denken würde)
Funktionen in Haskell verwenden eine Notation namens Curry. Eine Definition wie
%Vor% bedeutet, dass plus
eine Funktion ist, die zwei Int
s akzeptiert und einen Wert vom Typ ganz rechts zurückgibt (der wiederum Int
ist). Genauer gesagt bedeutet currying, dass Sie mit teilweise angewandten Funktionen arbeiten, die Ihnen erlauben, Code wie
Kleinbuchstaben in Typensignaturen weisen auf eine sogenannte -Typ-Variable hin, die als Platzhalter für jeden möglichen Typ angesehen werden kann.
Die Definition
%Vor% bedeutet also Für alle Typen a
und b
, map
nimmt eine Funktion von a
bis b
und eine Liste von a
, von denen es eine Liste von erzeugt b
s .
Nehmen wir ein Beispiel
%Vor% Wir sehen, die angegebene Liste ist eine Liste von Int
, also a = Int
. Darüber hinaus sollte map
show
auf jedes Element anwenden, was ein String
zurückgibt. Da show
auf den Typ a -> b
der Definition passen muss, können wir folgern, dass b = String
.
Unser Ausdruck gibt ein [b]
zurück, also ist das Ergebnis ein [String]
.
Um solche Signaturen noch deutlicher zu machen, können wir sie mit einer ausführlicheren Syntax schreiben
%Vor% Dies gibt nun explizit an, dass map
mit allen Typen a
und b
arbeiten soll.
Für die Falten werfen Sie einen Blick auf ihre Definitionen . Angesichts des obigen Wissens sollten die Typ-Signaturen selbsterklärend sein, wenn klar ist, was die Funktionen tun sollen.
Beispiel: factorial n = foldl (*) 1 [1..n]
Die Signatur von foldr map
wird.
Sie können diese auch erhalten, indem Sie einfach :t foldr map
in den Haskell-Interpreter (GHCi) eingeben.
Die Kleinbuchstaben in den Typdeklarationen sind Typ Variablen . Das heißt, wenn ein a
vorhanden ist, muss dies derselbe Typ sein.
Klammerausdrücke (zB (a -> b)
) Teilausdrücke neigen dazu, zu bedeuten, dass die Funktion eine Funktion als einen ihrer Parameter akzeptiert.
Rechteckige Klammern zeigen eine Liste von etwas an.
So akzeptiert map
diese Argumente:
(a -> b)
Eine Funktion, die ein Argument [a]
eine Liste, in der die erste Argumentfunktion auf und erzeugt [b]
eine Liste als Ergebnis.
Wir können (.)
auf die gleiche Weise dekodieren:
(a -> b)
ist eine Funktion, die ein einzelnes Argument (c -> a)
ist eine Funktion, die den gleichen Typ wie das Argument der ersten Funktion c
ein Argument des gleichen Typs, das die zweite Funktion verwendet Haskell kann jedoch eine nette Sache machen, und das heißt, wenn Sie ein Argument weglassen, gibt es eine Funktion zurück, die die verbleibende Signatur hat. Daher wird (.)
häufig verwendet, um Funktionen zu ketten.
Nehmen wir an, wir haben 2 Funktionen ord :: Char -> Int
, die ein Zeichen annehmen und dessen Integer-Repräsentation und add32 :: Int -> Int
zurückgeben, was seinem Argument 32 hinzufügt.
Wenn wir upcaseVal = add32 . ord
schreiben, hat die Funktion upcaseVal jetzt die Signatur upcaseVal :: Char -> Int
, weil die Funktion (.)
sie mit c -> b
belassen hätte und wir sie in der Funktionsdefinition ersetzen würden.
Also müsste foldr map
so sein:
foldr
ist eine Funktion, die 2 Argumente akzeptiert, die map
ist. (a -> b -> b
), sehen wir das in map
a = b
Somit ist der letzte Typ:
%Vor%Aber ich denke, das muss Sie nicht so sehr stören, da der Compiler die meiste Zeit damit beschäftigt ist.
In Haskell ist eine Kleinschreibung eine Typvariable. Während Char
nur Char
, a
oder b
bedeutet, könnte Char
oder Bool
oder Integer
oder ein anderer Typ bedeuten. Der Schlüssel ist, dass alle a
s vom selben Typ sind. Und alle b
s sind vom selben Typ.
Zum Beispiel verwendet map
als erstes Argument eine Funktion, die eine Variable vom Typ a
und eine andere vom Typ b
zurückgibt. Dann gibt map
eine neue Funktion zurück, die eine Liste vom Typ a
übernimmt. Diese neue Funktion gibt eine Liste vom Typ b
zurück.
Angenommen, Sie hätten eine Funktion increment
, die einen Wert zu einer Ganzzahl hinzufügt. map increment [1,2,3]
würde letztendlich eine Liste [2,3,4] zurückgeben.
Die frühere Definition von map
mit den ersetzten Typvariablen würde wie folgt aussehen:
map :: increment -> [1,2,3] -> [2,3,4]
und increment
, BTW, würden wie
increment :: Integer -> Integer
Der Grund, warum es lustig geschrieben ist, ist, dass Sie Funktionen teilweise anwenden können. Hier werde ich eine neue Funktion basierend auf der teilweisen Anwendung der Karte machen:
%Vor%Dann könnten Sie
verwenden %Vor%würde das gleiche Ergebnis erzeugen
Tags und Links haskell types type-inference inferred-type