2011-11-27 21 views
7

Usted podría patrón-partido contra múltiples argumentos de una función mediante la creación de una tupla y luego desestructuración en una expresión de coincidencia:En OCaml, ¿cuál es la forma canónica de comparar contra múltiples argumentos de una función?

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

Alternativamente, si usted no necesita una función al curry, podría hacer esto haciendo f tomar una tupla como el único argumento:

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

la ventaja de este último método es que usted no tiene que escribir los argumentos dos veces cada vez que se define una función. Pero las funciones que toman una tupla no parecen ser tan populares como las cursivas.

Entonces, ¿cuál de los dos se considera canónico, o preferido, en la comunidad OCaml?

EDITAR: Tal como se indica a continuación, me refiero a let f = function blah blah en el segundo fragmento de código.

Respuesta

8

Una tupla no es solo una construcción sintáctica, sino que representa una estructura de datos real. Esto significa que fun (x,y) es (muy ligeramente) menos eficiente que f x y en el caso en que x y y aún no están agrupados, porque hay que asignar una tupla. Si esto no está claro, el equivalente aproximado en Java sería

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 

Por esta razón, generalmente es preferible evitar el uso de tuplas como argumentos de la función a menos que tenga una buena razón para hacerlo.

P.S. Una consideración similar se aplica a las declaraciones de tipo de datos, en los que los siguientes son sutilmente diferentes:

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

Foo es un constructor de tipo de datos que toma dos argumentos. Bar es un constructor que toma un argumento (una tupla).

4

En realidad, f = function... es un atajo de f (x, y) = match (x, y) with... manera:

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

es lo mismo que:

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

(Observe que hay un error en su segunda formulación; esas dos versiones no son compatible).

Como ha señalado, no se puede evitar el uso de match ... with... en una función curried. Personalmente, prefiero la forma al curry de una función ya que es más flexible, especialmente con la aplicación parcial. Además, la coincidencia de patrones no solo se aplica en argumentos de función; se usan esencialmente en todas partes en OCaml, lo que hace que match ... with... sea aún más importante.

Siempre que detecte el patrón de uso como el anterior, intente reemplazar match ... with... con function. Es solo una cuestión de estilo, por lo que no hay nada más preferido aquí.

8

Esta solución es canónica:

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

El compilador optimiza este caso especial y en realidad no asigna un bloque para la tupla (x, y).

3

La manera canónica es una función al curry y match en la tupla, es decir, su primer fragmento.

Esta es la forma en que se escribe la biblioteca estándar (consulte las fuentes de la biblioteca estándar, por ejemplo, muchas funciones en list.ml).

Esta es también la forma en que se optimiza la implementación, especialmente el compilador de código nativo. Si crea una tupla u otro bloque y lo destruye inmediatamente sin pasarlo a funciones que esperan un bloqueo, el compilador de código nativo a menudo detecta eso y evita asignar un bloque por completo. Incluso si termina asignando un bloque, es más eficiente hacer que la duración del bloque sea lo más corta posible, para aumentar las posibilidades de que el bloque permanezca en el montón menor y en el caché del procesador.

Cuestiones relacionadas