2016-08-03 13 views
4

Sagen, ich habe FunktionenSchreiben Sie f in Pointfree-Stil?

g :: a -> b, h :: a -> c 

und

f :: b -> c -> d. 

Ist es möglich, die Funktion

f' :: a -> a -> d 

gegeben durch

f' x y = f (g x) (h y) 

in Punkt fre zu schreiben der Stil ?.

Man kann die Funktion

f' a -> d, f' x = f (g x) (h x) 

in Punkt freien Stil schreiben, indem

Einstellung
f' = (f <$> g) <*> h 

aber ich kann nicht herausfinden, wie der allgemeineren Fall zu tun.

Antwort

22

Wir haben:

k x y = (f (g x)) (h y) 

und wir möchten k in Punkt-freien Stil schreiben. Das erste an k übergebene Argument lautet x. Was müssen wir mit x tun? Nun, zuerst müssen wir g darauf anrufen, und dann f, und dann tun etwas Phantasie, um dies auf (h y) anwenden.

k = fancy . f . g 

Was ist das fancy? Nun:

k x y = (fancy . f . g) x y 
     = fancy (f (g x)) y 
     = f (g x) (h y) 

So wünschen wir fancy z y = z (h y). Eta-reduzierend erhalten wir fancy z = z . h oder fancy = (. h).

k = (. h) . f . g 

Eine natürliche Art und Weise zu denken, es

       ┌───┐   ┌───┐ 
         x ───│ g │─── g x ───│ │ 
        / └───┘   │ │ 
       (x, y)      │ f │─── f (g x) (h y) 
         \  ┌───┐   │ │ 
         y ───│ h │─── h y ───│ │ 
          └───┘   └───┘ 

         └──────────────────────────────┘ 
             k 

Control.Arrow Geben Sie sein könnte:

k = curry ((g *** h) >>> uncurry f) 
+2

) sein Das ist sehr verschieden von dem Verfahren, das ich selbst befolge. Es ist immer erfrischend, ein neues zu sehen Weg. – dfeuer

5

Werfen Sie einen Blick auf online converter

Es

umgewandelt
f' x y = f (g x) (h y) 

in

f' = (. h) . f . g 

mit dem Fluss der Transformationen

f' = id (fix (const (flip ((.) . f . g) h))) 
f' = fix (const (flip ((.) . f . g) h)) 
f' = fix (const ((. h) . f . g)) 
f' = (. h) . f . g 
+0

Wie haben Sie/es geht von 'f 'xy = f (gx) (hy)' zu 'f' = id (fix (const (flip ((.). f. g) h))) '? – Gurkenglas

+0

Ich bin kein Experte darin. Sieht so aus, als hätte die Bibliothek eine Reihe vordefinierter Transformationen und führt die Suche im Bereich möglicher Transformationen durch. Und wenn man Vereinfachungen betrachtet, sieht es so aus, als hätte es einige redundante Funktionen. Zum Beispiel könnte der erste Schritt 'flip ((.). F. G) h 'ohne Kette von' id (fix (const ' –

3

Dies ist etwas länger, aber ein wenig leichter zu folgen, als (. h) . f. g.

Zuerst schreiben Sie einfach f' um ein Tupel statt zwei Argumente zu nehmen. (Mit anderen Worten, sind uncurrying wir Ihre ursprüngliche f'.)

f' (x, y) = f (g x) (h y) 

Sie ein Tupel ziehen auseinander mit fst und snd statt Muster auf sie passend:

f' t = f (g (fst t)) (h (snd t)) 

Verwendung von Funktions Zusammensetzung, die oben wird

f' t = f ((g . fst) t) ((h . snd) t) 

die, hey, viel wie die Version sieht man punktfrei mit applicative Stil machen könnte:

Das einzige verbleibende Problem ist, dass f' :: (a, a) -> d. Sie können dieses Problem beheben, indem sie explizit currying:

f' :: a -> a -> d 
f' = let g' = g . fst 
     h' = h . snd 
    in curry $ (f <$> g') <*> h' 

(. Dies ist sehr ähnlich, durch die Art und Weise, auf die Control.Arrow Lösung von Lynn hinzugefügt)

+0

Leichter zu folgen das '(. h). f. g'? Äh ... Ich glaube nicht. Das Verwenden der' Applicative (a ->) 'Instanz mit Tupeln gibt Ihnen das _worst beider Welten_ : kryptische Datenfluss - Semantik und peinliches Curry – leftaroundabout

+0

Ich finde die teilweise Anwendung von '(.)' noch weniger verständlich als 'Applicative (a ->)'. Der Benutzer hat bereits auf ein Verständnis (oder zumindest auf ein Bewusstsein) von 'Applicative' hingewiesen , also baute ich das. Die Frage war, wie man eine point-free Version der Funktion schreibt, ich würde mich nicht kümmern und bei dem vollkommen verständlichen 'fxy = f (gx) (hy)' bleiben. – chepner

3

Mit den „drei Regeln der Operator Abschnitte“, wie angewandt die Zusammensetzung Operator (.) Funktion,

(.) f g = (f . g) = (f .) g = (. g) f -- the argument goes into the free slot 
--  1   2   3 

dies ableitbar ist durch ein paar einfachen mechanischen Schritte:

k x y = (f (g x)) (h y)      -- a (b c) === (a . b) c 
     = (f (g x) . h) y 
     = (. h) (f (g x)) y 
     = (. h) ((f . g) x) y 
     = ((. h) . (f . g)) x y 

Schließlich ist (.) assoziativ, so dass die inneren Parens fallengelassen werden können.

Das allgemeine Verfahren ist bestrebt, die Situation zu erreichen, in der eta-Reduktion durchgeführt werden kann, dh wir können die Argumente loswerden, wenn sie in derselben Reihenfolge und sind außerhalb jegliche Klammern sind:

k x y = (......) y 
=> 
k x = (......) 

Lather , rinse, wiederholen.


Ein weiterer Trick ist zwei Argumente in eine oder umgekehrt zu drehen, mit der Gleichung

curry f x y = f (x,y)  

so, Ihre

f (g x) (h y) = (f.g) x (h y)      -- by B-combinator rule 
       = (f.g.fst) (x,y) ((h.snd) (x,y)) 
       = (f.g.fst <*> h.snd) (x,y)   -- by S-combinator rule 
       = curry (f.g.fst <*> h.snd) x y 

Dies ist das gleiche wie die answer by @chepner ist, aber Prägnanter präsentiert.

So sehen Sie, Ihre (f.g <*> h) x wird nur (f.g.fst <*> h.snd) (x,y). Gleicher Unterschied.


(weil für Funktionen, (<$>) = (.))