2012-10-16 20 views
8

Se l'operatore del tubo si crea in questo modo:Perché l'operatore di pipe funziona?

let (|>) f g = g f 

E usato in questo modo:

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

Poi quello che sembra fare è prendere List.Map e lo mette alle spalle (divertente x -> x * x * x) e non cambia nulla circa la posizione della [2; 4; 6]

Così ora sembra che questo:

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

Tuttavia questo non funziona.

Sto solo imparando f # per la prima volta ora. E questo mi ha infastidito mentre leggevo un libro su f #. Quindi potrei imparare cosa mi manca più tardi, ma ho deciso di chiedere comunque.

È ovvio però che mi manca qualcosa di importante. Dal momento che posso facilmente ricreare l'operatore del tubo. Ma non capisco perché funzioni. Potrei mettermi in imbarazzo molto presto mentre imparo di più. Oh bene.

+1

l'operatore non è prefisso prefisso - lo stai applicando alle cose sbagliate –

risposta

11

L'operatore di pipe è semplicemente zucchero sintattico per le chiamate con metodo concatenato. È molto simile a come le espressioni linq sono espresse in C#.

Spiegazione da here:

avanti tubo Operatore amo questo ragazzo. L'operatore di pipe inoltra viene semplicemente definita come:

let (|>) x f = f x 

E ha una firma di tipo:

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

che si traduce in: dato un tipo generico 'una, e una funzione che prende un' una e ritorna a 'b, quindi restituisce l'applicazione della funzione sull'input.

Invece di spiegare questo, lasciate che vi faccia un esempio di dove può essere utilizzato:

// 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)) 

Il codice è molto semplice, a meno di notare quanto sia indisciplinati la sintassi sembra. Tutto ciò che vogliamo fare è prendere il risultato di un calcolo e passarlo al calcolo successivo. Potremmo riscriverlo introducendo una serie di nuove variabili:

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

Ma ora è necessario mantenere tutte quelle variabili temporanee diritte. Ciò che l'operatore (|>) fa è prendere un valore e "inoltrarlo" a una funzione, essenzialmente permettendoti di specificare il parametro di una funzione prima della chiamata alla funzione. Questo semplifica drasticamente il codice F # consentendo di riunire le funzioni, in cui il risultato di uno viene passato al successivo. Quindi, per utilizzare lo stesso esempio, il codice può essere scritto chiaramente:

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

Edit:

In F # cosa si sta realmente facendo con una chiamata di metodo sta prendendo una funzione e poi applicarlo alla il parametro che segue, quindi nel tuo esempio sarebbe List.map (fun x -> x * x * x) applicato a [2;4;6].Tutto quello che fa l'operatore di pipe è prendere i parametri in ordine inverso e poi fare l'applicazione invertendoli indietro.

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

standard F sintassi # chiamata: f g

invertita sintassi di F # chiamata: g f

standard:

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

invertita:

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

Conosco il suo zucchero sintattico e l'ho creato proprio lì nel codice sopra. Ora sto chiedendo perché questa funzione funzioni: let (|>) f g = g f - o come funziona in modo diverso da come la ho capita nella mia domanda. – user1594138

+0

Grazie per le informazioni aggiuntive che hai inserito. Sono queste le 2 funzioni che vanno nell'operatore pipe forward ?: List.map (fun x -> x * x * x) - Perché come ho mostrato nel mio post, riordinando quelle 2 non lavorare – user1594138

+0

Ah, sto iniziando a prenderlo. Grazie! – user1594138

8

Le parentesi intorno |> significa che è un operatore infisso così il vostro esempio potrebbe essere scritto

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

Dal |> vale il suo primo argomento per il secondo, questo equivale a

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

Le parentesi non ne fanno un operatore infisso. Aggiungendo parentesi (in realtà parens) quando _using_ lo trasforma in una funzione, al contrario di un operatore. Una volta definiti, sono richiesti sia per gli operatori infissi che per i prefissi. – Abel

3

Se inserisci la tua definizione di |> in fsi e guarda la firma dell'operatore derivata dall'inferenza di tipo che noterai val (|>) : 'a -> ('a -> 'b) -> 'b, cioè l'argomento 'a viene assegnato alla funzione ('a -> 'b) produce 'b.

progetto NOW questa firma sul vostro espressione [2;4;6] |> List.map (fun x -> x * x * x) e si otterrà List.map (fun x -> x * x * x) [2;4;6], dove l'argomento è la lista [2;4;6] e la funzione è partially applied funzione di un argomento List.map (fun x -> x * x * x).

5

Come altri hanno già detto sopra, in pratica stai fraintendendo quale risultato2 risolverebbe. Si sarebbe in realtà decidere di

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

List.map prende due argomenti: una funzione da applicare a tutti gli elementi di una lista e un elenco. (fun x -> x * x * x) è il primo argomento e [2;4;6] è il secondo.

Fondamentalmente basta mettere quello che è sulla sinistra di |> dopo la fine di ciò che è sulla destra.

+2

È bello che tu fossi più esplicito sull'ordine degli argomenti, con il "... dopo la fine" ... Chiunque venisse in F # in ritardo nel gioco dopo aver visto '|>' di Elisir sarà sicuramente inciampato, dal momento che applica il valore a sinistra come argomento _primo_. – kevlarr