2012-10-16 3 views
8

Wenn das Rohr Operator wie folgt erstellt:Warum arbeitet der Rohrbediener?

let (|>) f g = g f 

Und so benutzt:

let result = [2;4;6] |> List.map (fun x -> x * x * x) 

Dann, was es nimmt List.Map zu tun scheint und bringt es hinter (fun x -> x * x * x) Und etwas über die Position nicht von ändern [2; 4; 6]

es so aussieht So jetzt:

let result2 = [2;4;6] (fun x -> x * x * x) List.map 

Dies funktioniert jedoch nicht.

Ich lerne gerade f # zum ersten Mal jetzt. Und das störte mich beim Lesen eines Buches über f #. So könnte ich später herausfinden, was ich vermisse, aber ich habe mich trotzdem dazu entschieden, zu fragen.

Es ist offensichtlich, dass ich etwas wichtiges fehlt. Da kann ich den Pfeifenbediener einfach neu erstellen. Aber ich verstehe nicht, warum es funktioniert. Ich könnte mich sehr bald blamieren, wenn ich mehr lerne. Naja.

+1

der Operator ist Infix nicht Präfix - Sie wenden es auf die falschen Dinge –

Antwort

11

Der Pipe Operator ist einfach syntaktischer Zucker für verkettete Methodenaufrufe. Es ist sehr ähnlich wie linq Ausdrücke in C# ausgedrückt werden.

Erklärung von here:

Vorlauf Operator Ich liebe diesen Kerl. Der Vorlauf Operator ist einfach definiert als:

let (|>) x f = f x 

Und eine Art Signatur hat:

'a -> ('a -> 'b) -> 'b 

was übersetzt: ein generischer Typ gegeben ‚a, und eine Funktion, die eine nimmt‘ ein und kehrt zurück a 'b, dann die Anwendung der Funktion auf die Eingabe zurückgeben.

Anstatt dies zu erklären, lassen Sie mich Ihnen ein Beispiel, wo es verwendet werden kann:

// Take a number, square it, then convert it to a string, then reverse that string 
let square x   = x * x 
let toStr (x : int) = x.ToString() 
let rev (x : string) = new String(Array.rev (x.ToCharArray())) 

// 512 -> 1024 -> "1024" -> "4201" 
let result = rev (toStr (square 512)) 

Der Code sehr einfach ist, aber Hinweis, wie widerspenstig die Syntax sieht. Alles, was wir tun wollen, ist, das Ergebnis einer Berechnung zu nehmen und es an die nächste Berechnung zu übergeben. Wir könnten es neu schreiben, indem wir eine Reihe neuer Variablen einführen:

let step1 = square 512 
let step2 = toStr step1 
let step3 = rev step2 
let result = step3 

Aber jetzt müssen Sie alle diese temporären Variablen gerade halten. Der Operator (|>) nimmt einen Wert an und leitet ihn an eine Funktion weiter, so dass Sie im Wesentlichen den Parameter einer Funktion vor dem Funktionsaufruf angeben können. Dies vereinfacht den F # -Code erheblich, da Sie Funktionen zusammenführen können, wobei das Ergebnis von einem in das nächste übernommen wird. So verwendet das gleiche Beispiel kann der Code eindeutig geschrieben werden als:

let result = 512 |> square |> toStr |> rev 

bearbeiten:

In F #, was Sie wirklich mit einem Methodenaufruf zu tun ist, eine Funktion zu nehmen und dann nicht mehr anzuwenden, der folgende Parameter, so in Ihrem Beispiel wäre es List.map (fun x -> x * x * x) wird auf [2;4;6] angewendet.Alles, was der Rohrbediener macht, nimmt die Parameter in umgekehrter Reihenfolge und macht dann die Anwendung rückgängig.

Funktion: List.map (fun x -> x * x * x) Parameter: [2;4;6]

Standard-F # Aufruf-Syntax: f g

Reversed F # Aufruf-Syntax: g f

Standard:

let var = List.map (fun x -> x * x * x) [2;4;6] 

Umgekehrt:

let var = [2;4;6] |> List.map (fun x -> x * x * x) 
+0

Ich kenne seine syntaktischen Zucker und ich habe es genau dort im obigen Code erstellt. Ich frage jetzt, warum diese Funktion funktioniert: Lassen Sie (|>) f g = g f - oder wie es anders funktioniert als ich es verstehe, wie ich in meiner Frage geschrieben habe. – user1594138

+0

Danke für die zusätzlichen Informationen, die du eingibst. Sind das die 2 Funktionen, die in den Pipe-Forward-Operator gehen ?: List.map (Spaß x -> x * x * x) - Wie ich in meinem Beitrag gezeigt habe, ordnen diese 2 nicht arbeite nicht. – user1594138

+0

Ahh Ich fange an es zu bekommen. Vielen Dank! – user1594138

8

Die Klammern um |> bedeutet es ist ein Infixoperator so könnte Ihr Beispiel

let result = (|>) [2;4;6] (List.map (fun x -> x * x * x)) 

geschrieben werden, da |> mit dem zweiten erstes Argument gilt, ist dies gleichbedeutend mit

let result = (List.map (fun x -> x * x)) [2;4;6] 
+0

Die Klammern selbst machen es nicht zu einem Infix-Operator. Hinzufügen von Klammern (Parens, eigentlich), wenn _using_ es in eine Funktion verwandelt, im Gegensatz zu einem Operator. Nach Definition sind sie sowohl für Infix- als auch für Präfixoperatoren erforderlich. – Abel

3

Wenn Sie Geben Sie Ihre Definition von |> in fsi ein und schauen Sie sich die Signatur des Operators an, die durch Typinferenz abgeleitet wurde. Sie werden val (|>) : 'a -> ('a -> 'b) -> 'b bemerken, dh das Argument 'a wird der Funktionzugewiesenergibt 'b.

Jetzt projizieren diese Signatur auf Ihren Ausdruck [2;4;6] |> List.map (fun x -> x * x * x) und Sie werden List.map (fun x -> x * x * x) [2;4;6] erhalten, wo die Argumentliste ist [2;4;6] und die Funktion ist partially applied Funktion ein Argument List.map (fun x -> x * x * x).

5

Wie andere oben gesagt haben, sind Sie im Grunde falsch verstanden, was result2 würde zu lösen. Es wäre beheben tatsächlich

List.map (fun x -> x * x * x) [2;4;6]

List.map zwei Argumente nimmt: eine Funktion auf alle Elemente in einer Liste und einer Liste anzuwenden. (fun x -> x * x * x) ist das erste Argument und [2;4;6] ist die zweite.

Im Grunde nur, was links von |> nach dem Ende dessen, was rechts ist.

+2

Schön, dass du expliziter über die Argumentationsreihenfolge warst, mit dem "... nach dem Ende" ... Jeder, der zu spät zum Spiel kommt und Elixirs '|>' zuerst gesehen hat, wird definitiv gestolpert sein, da dies die Wert auf der linken Seite als _first_ Argument. – kevlarr

Verwandte Themen