2011-11-27 3 views
7

Si potrebbe pattern-match contro molteplici argomenti di una funzione con la creazione di una tupla e poi destrutturazione in un match espressione:In OCaml, qual è il modo canonico di corrispondenza con più argomenti di una funzione?

let f x y = 
    match x, y with 
    | pattern1 -> expr1 
    | ... 

In alternativa, se non avete bisogno di una funzione al curry, si potrebbe fare questo rendendo f prendere una tupla come unico argomento:

let f (x, y) = function 
    | pattern1 -> expr1 
    | ... 

il vantaggio di quest'ultimo metodo è che non c'è bisogno di scrivere gli argomenti due volte ogni volta che si definisce una funzione. Ma le funzioni che prendono una tupla sembrano non essere così popolari di quelle al curry.

Quindi quale dei due è considerato canonico, o preferito, nella comunità OCaml?

MODIFICA: proprio come il pad indicato di seguito, intendo let f = function blah blah nel secondo frammento di codice.

risposta

8

Una tupla non è solo un costrutto sintattico, rappresenta una vera struttura di dati. Ciò significa che fun (x,y) è (molto leggermente) meno efficiente rispetto a f x y nel caso in cui x e non siano già tuplati, poiché una tupla deve essere allocata. Se questo non è chiaro, l'equivalente in Java sarebbe

void foo(X x, Y y) { ... } 
void bar(Tuple<X,Y> t) { ... } 

/* client code */ 
X x = new X(); 
Y y = new Y(); 

foo(x, y); // Just uses x and y directly 
bar(new Tuple<X,Y>(x, y)); // Has to "new" a Tuple 

Per questo motivo, è generalmente preferibile evitare l'uso di tuple come argomenti della funzione a meno che non si dispone di una buona ragione per farlo.

P.S. Una considerazione analoga vale per le dichiarazioni tipo di dati, dove il seguente sono leggermente diverso:

type 'a foo = Foo of 'a * 'a; 
type 'a bar = Bar of ('a * 'a); 

Foo è un costruttore tipo di dati che prende due argomenti. Bar è un costruttore che accetta un argomento (una tupla).

4

In realtà, f = function... è una scorciatoia di f (x, y) = match (x, y) with... così:

let f = function 
    | pattern1_of_x_y -> expr1 
    | ... 

è lo stesso:

let f (x, y) = 
    match x, y with 
    | pattern1 -> expr1 
    | ... 

(Si noti che c'è un errore nella vostra seconda formulazione; queste due versioni non sono compatibile).

Come è stato sottolineato, non è possibile evitare di utilizzare match ... with... in una funzione al curry. Personalmente, preferisco la forma al curry di una funzione poiché è più flessibile, specialmente con un'applicazione parziale. Inoltre, la corrispondenza dei modelli non viene applicata solo negli argomenti delle funzioni; sono usati essenzialmente ovunque in OCaml, il che rende la costruzione match ... with... ancora più importante.

Ogni volta che si individua il modello di utilizzo come sopra, provare a sostituire match ... with... con function. È solo una questione di stile, quindi non c'è niente di più preferito qui.

8

Questa soluzione è canonica:

let f x y = 
    match x, y with 
    | pattern1 -> expr1 
    | ... 

Il compilatore ottimizza questo caso particolare e in realtà non allocare un blocco per la tupla (x, y).

3

Il modo canonico è una funzione al curry e su match sulla tupla, ovvero il primo frammento.

Questo è il modo in cui viene scritta la libreria standard (guarda le fonti di libreria standard, ad esempio molte funzioni in list.ml).

Questo è anche il modo in cui viene ottimizzata l'implementazione, in particolare il compilatore di codice nativo. Se si crea una tupla o un altro blocco e lo si distrugge immediatamente senza passarlo a funzioni che prevedono un blocco, il compilatore di codice nativo spesso lo rileva ed evita di allocare del tutto un blocco. Anche se si finisce per allocare un blocco, è più efficiente rendere la durata del blocco più corta possibile, per aumentare le possibilità che il blocco rimanga nell'heap secondario e nella cache del processore.