2017-02-07 4 views
6

Ich mag den Additions-Operator komponieren (+) eine Funktion dieser Art zu machen:"Kette" der Additionsoperator in eine Funktion, die 3 Argumente akzeptiert?

Num a => a -> a -> a -> a 

Wie, das entspricht dies:

(\a b c -> a + b + c) 

aber ohne Lambda-Ausdrücke zurückgreifen zu müssen.


Ich habe bereits versucht

((+) . (+)) 

, die ich arbeiten würde erwartet, aber überraschenderweise nicht.

+0

Iirc, Gelassenheit erfordert, dass die Funktion nur 1 Argument akzeptiert. Die binären Operatoren nehmen jeweils 2 – Carcigenicate

+0

Und warum nicht einfach die Liste mit '+' falten? – Carcigenicate

+0

@Carcigenicate hmm, das ist seltsam. Das hätte ich nicht erwartet. Gibt es einen Weg, es zu tun? - edit: und was ist fold, und welche liste? – theonlygusti

Antwort

11

http://pointfree.io gibt die punktfreie Version \a b c -> a + b + c als ((+) .) . (+).

Informell funktioniert Zusammensetzung nur "intuitiv" für Funktionen erster Ordnung, die weder Funktionen als Argumente nehmen noch Funktionen als Werte zurückgeben. (+) ist eine Funktion höherer Ordnung; Es nimmt einen Wert vom Typ Num a => a und gibt eine Funktion vom Typ Num a => a -> a zurück. Wenn Sie versuchen, Funktionen höherer Ordnung in einer naiven Art und Weise zu komponieren, das Ergebnis ist nicht das, was Sie erwarten:

:t (+) . (+) 
(+) . (+) :: (Num a, Num (a -> a)) => a -> (a -> a) -> a -> a 

die Definitionen der beiden Funktionen vor:

(+) :: Num z => z -> z -> z 
(.) :: (b -> c) -> (a -> b) -> (a -> c) 
f . g = \x -> f (g x) 

Dann

(+) . (+) == (.) (+) (+) 
      == \x -> (+) ((+) x) 

Aufgrund des Currierens wird eine Funktion übergeben, keine Zahl, als erstes Argument der ersten (+).


Wie bekommen wir h a b c = a + b + c-h = ((+) .) . (+)? Beginnen Sie mit dem Umschreiben des Infix-Ausdrucks als Präfix-Ausdruck, indem Sie die Tatsache verwenden, dass (+) linksassoziativ ist. Als nächstes wenden wir abwechselnd eine eta-Umwandlung an, um ein Argument und eine Zusammensetzung zu eliminieren, um ein Argument in Position zu bringen, um eliminiert zu werden. Ich habe versucht, die Funktionen, die für die Anwendung der Komposition verwendet werden, sehr eindeutig zu identifizieren.

 == \a b -> (+) ((+) a b)  -- eta conversion to eliminate c 
    == \a b -> (+) (((+) a) b) -- parentheses justified by currying 
    --   f  g   -- f = (+), g = ((+) a) 
    -- \a b -> f ( g b) 
    -- \a b -> (f . g) b  -- definition of (.) 
    == \a b -> ((+) . ((+) a)) b 
    == \a -> (+) . ((+) a)  -- eta conversion to eliminate b 
    == \a -> (.) (+) ((+) a)  -- prefix notation 
    == \a -> ((.) (+)) ((+) a) -- parentheses justified by currying 
    == \a -> ((+) .)((+) a)  -- back to a section of (.) 
    --   f  g  -- f = ((+) .), g = (+) 
    -- \a ->  f  (g a) 
    -- \a -> ( f . g) a  -- definition of (.) 
    == \a -> (((+) .) . (+)) a 
    == ((+) .) . (+)    -- eta conversion to eliminate a 
+0

Es wäre anschaulich, die Arbeitsweise von '((+).) Zu analysieren. (+) '. –

6

zwar etwas Rauschen führt, könnte man uncurry :: (a -> b -> c) -> (a,b) -> c und curry :: ((a,b) -> c) -> a -> b -> c zu Zwischenspeichern, die Argumente des zweiten Plus in einem Tupel verwenden:

curry $ (+) . uncurry (+) :: Num a => a -> a -> a -> a 

oder vielleicht mehr semantisch lesbar:

curry ((+) . uncurry (+)) :: Num a => a -> a -> a -> a 

uncurry nimmt also eine Funktion (hier (+)) auf und wandelt sie in eine Funktion um: uncurry (+) :: Num a => (a,a) -> a. Als Ergebnis haben Sie die (+) in eine Funktion umgewandelt, die ein Tupel nimmt.

Jetzt können wir (.) verwenden, um eine Zusammensetzung mit den ersten (+) zu machen:

(+) . uncurry (+) :: Num a => (a,a) -> (a -> a) 

So, jetzt haben wir eine Funktion, die ein Argument (das Tupel (a,a)) und erzeugt eine Funktion, die ein a nimmt (die zweiter Operand der ersten (+)) und berechnet die Summe. Das Problem ist natürlich, dass wir das Tupel loswerden wollen. Wir können dies tun, indem wir die Funktion in eine curry übergeben. Dies transformiert die Tupel-Funktion ((a,a) -> (a -> a)) in eine Funktion, die die Argumente getrennt nimmt (a -> (a -> (a -> a))).

4

Notiere die Signatur der Funktion Zusammensetzung Betreiber:

(.) :: (b -> c) -> (a -> b) -> a -> c 
     ^  ^  ^
       Functions 

It 2 ​​Funktionen übernimmt, die jeweils die 1 Argument und gibt eine Funktion, die ein Argument des gleichen Typs wie die zweite Funktion nimmt , und gibt den gleichen Typ wie der erste zurück.

Ihr Versuch, zwei + s zu schreiben, funktionierte nicht, da + 2 Argumente benötigt, also ist dies ohne eine hashische/kreative Lösung nicht möglich.

An diesem Punkt, würde ich sagen, zwingen Zusammensetzung, wenn es nicht das Problem passt nur wird Ihr Leben schwieriger zu machen.

Wenn Sie mehrere Zahlen summieren möchten, können Sie eine Funktion wie schreiben konnte:

sum :: [Int] -> Int 
sum nums = foldl (+) 0 nums 

Oder da nums auf der Rückseite der Definition erscheint, kann es ganz fallen gelassen werden, um eine „punkt- nachgebend freie“Form:

sum :: [Int] -> Int 
sum = foldl (+) 0 

Es reduziert/+ über eine Liste von Zahlen klappt. Wenn Sie noch keine Falten benutzt haben, schauen Sie sich jetzt in ihnen an. Sie sind einer der wichtigsten Wege, um in Haskell Loopings zu erreichen. Es ist im Wesentlichen "implizite Rekursion", wenn Sie mit Listen arbeiten oder irgendetwas anderes iterierbar ist.

Mit der obigen Funktion definiert, können Sie es gerne verwenden:

sum [1, 2 3, 4, 5] 
+0

Ich denke, Sie sollten 'foldr (+) 0 Zahlen '(mit Klammern) und' = 'anstelle von' :: 'in der Zuweisung schreiben. Es ist normalerweise auch speichereffizienter, "foldl" über "foldr" zu verwenden, wenn die Funktion kommutativ ist. –

+0

@WillemVanOnsem Whoops. Idk, wie ich am Ende Doppelpunkte anstelle eines Gleichgestellten verwendet habe. Und danke, ich werde die Falte wechseln. – Carcigenicate

+0

Sie müssen immer noch Klammern über '(+)' 'sonst Haskell sieht dies als' (+) foldl 0' ... –

8

Sie benötigen diese seltsame Betreiber (.).(.), die manchmal ist definiert als .: (man denke an drei Punkten ...)

In GHCI

Prelude> let (.:) = (.).(.) 
Prelude> let f = (+) .: (+) 
Prelude> f 1 2 3 
> 6 

Hinweis dieser Operator kann auch als <$$> = fmap . fmap definiert werden.

Verwandte Themen