2016-07-28 20 views
1

Ich bin ziemlich neu in Haskell und diese Woche fand ich diese besondere Funktion in ein paar Vortragsfolien. Ich versuche zu verstehen, warum die folgende Funktion nicht um einen Parameter umfassen muss:Warum muss in dieser Funktion kein Parameter angegeben werden?

-- Return all final segments of the argument, longest first 
-- (horrible runtime complexity, used here for illustration purposes only) 
tails :: [a] -> [[a]] 
tails = reverse . map reverse . inits . reverse 

Wenn ich es wie tails "thisisastring" nennen würde dann wäre dies ein gültiges Argument sein. Ist es nicht notwendig, einen Parameter anzugeben, zum Beispiel tails xs = .... Alle anderen Funktionen, die ich vorher gesehen habe, waren auf diese Weise.

+1

FYI, das ist ein * schrecklich ineffizient * Definition von 'tails'. Es würde zumindest etwas sinnvoller sein, "inits" in Bezug auf "tails" zu definieren, obwohl das auch nicht großartig wäre. "Tails", die richtig implementiert sind, sind von Natur aus viel effizienter als "Inits": "O (n)" und nicht "O (n^2)". – dfeuer

+1

Ich bemerkte bereits, dass es von einigen Vortragsfolien war, so dass der Punkt darin bestand, ein neues Konzept zu illustrieren. Effizienz spielt hier keine Rolle. – markvdlaan93

Antwort

3

Der Parameter ist implizit. Oder um es anders auszudrücken, reverse . map reverse . inits . reverse wertet eine Funktion des Typs [a] -> [[a]] aus.

ein einfacheres Beispiel betrachten:

double_impl x = x * 2 
double = double_impl 

Die Art der double hier ist die gleiche Art wie double_impl, dh ein Parameter typeclass nimmt Num:

main = do 
    print $ double_impl 5 
    print $ double 5 

-- Out: 10 
-- Out: 10 
+0

Ich beginne es ein wenig zu verstehen. Ich versuche herauszufinden, warum die Schwänze zu [a] -> [[a]] führen. Beginnend mit rückwärts (am Ende wegen.), Braucht es [a] -> [a], danach braucht man [a] -> [[a]], was gilt, weil die Ausgabe von reverse [a] ist. Danach stecke ich fest, da Map eine Funktion benötigt (wieder umgekehrt), aber auch [a], während [[a]] als Ausgabe von inits ausgegeben wird. Wie funktioniert das? – markvdlaan93

+0

'map' erfordert * nicht * an' [a] '; curry, map ist eine Funktion, die ein 'a -> b 'nimmt und eine Funktion vom Typ' [a] -> [b] 'zurückgibt. 'map f list' ist wirklich zwei Funktionsanwendungen; 'map f' erzeugt eine Funktion und diese Funktion wird auf' list' angewendet. (Die Funktionsanwendung ist linksassoziativ, so dass 'map f list' äquivalent zu' (map f) list' ist.) – chepner

2

Wir können sehen, dass tails ist eine Funktion, indem Sie den Typ überprüfen.

Um seinen Typ zu berechnen, beginnen wir mit dem Schreiben der Typen aller Zwischenfunktionen in der Komposition. Beachten Sie, dass wir für jeden Auftritt einer Funktion neue Typvariablen verwenden.

reverse :: [a] -> [a] 
inits :: [b] -> [[b]] 
map :: (c -> d) -> [c] -> [d] 

Jetzt haben wir map reverse[[e]] -> [[e]] hat geben, da wir c=d=[e] für irgendeine Art bekommen e aus dem Vergleich der Ausdrücke

reverse :: c -> d -- for some c and d 
reverse :: [e] -> [e] -- for some e 

Daher sind die letzten beiden Zwischenprodukte haben Typen

map reverse :: [[e]] -> [[e]] 
reverse :: [f] -> [f] 

Jetzt beginnen wir Versuche, Typen zu vergleichen. Lassen Sie mich zuerst betonen, dass dies offensichtlich KEINE REALEN TYPEN sind! (Sorry für die alle Kappen, aber ich will jemand nicht entgehen lassen.)

inits . reverse :: [a] -*- [a] = [b] -*> [[b]] 
-- I'm using a -*- b -*> c to denote the type a -> c obtained by 
-- composing a function of type a -> b with one of type b -> c. 
-- The *s are to break the double dashes up, 
-- so they aren't parsed as a comment. 
-- Anyway, looking at this type, we see 
-- we must have [a] = [b], so a = b 
-- we can rewrite the type of inits . reverse as 
inits . reverse :: [a] -> [[a]] 

dann für die nächste Zusammensetzung:

map reverse . inits . reverse :: [a] -*- [[a]] = [[e]] -*> [[e]] 
-- again, we have [[a]] = [[e]], so e = a, and we have 
map reverse . inits . reverse :: [a] -> [[a]] 

Schließlich haben wir

reverse . map reverse . inits . reverse :: [a] -*- [[a]] = [f] -*> [f] 
-- This time we have [[a]] = [f], so we must have f = [a], so the type 
-- of the final composition is 
tails = reverse . map reverse . inits . reverse :: [a] -> [[a]] 

Da tails den Typ [a] -> [[a]] hat, muss es eine Funktion sein, die eine Liste von a s als Argument akzeptiert und eine Liste mit Listen von a s zurückgibt.

4

Dies wird als punktfreier Stil bezeichnet (wobei "Punkt" ein mathematischer Ausdruck ist, der im Grunde "Argument" bedeutet).

Auch tails xs = ... ist nur syntaktischer Zucker für tails = \xs -> ..., so alles, was Sie tun müssen, um sich zu überzeugen, dass tails eine Funktion ist, ist zu erkennen, dass

  1. reverse, map reverse und inits sind alle Funktionen:

    • map ist eine Funktion höherer Ordnung; Es nimmt eine Funktion als Argument und gibt eine andere Funktion zurück.

    • map reverse ist eine Funktion, weil map auf die Funktion reverse angewendet wird.

  2. Die Zusammensetzung von zwei Funktionen ist eine weitere Funktion (unter der Annahme, dass die Typen zusammenpassen, so dass wir auf das Ergebnis jeder Zusammensetzung konzentrieren können, anstatt überprüfen davon jede Zusammensetzung Typ-Kontrollen.)
Somit

  • reverse . map reverse ist eine Funktion,
  • so reverse . map reverse . inits ist eine Funktion,
  • und reverse . map reverse . inits . reverse ist eine Funktion.

Da tails zugewiesen wird, um den Wert von reverse . map reverse . inits . reverse, tails selbst ist auch eine Funktion.