2010-03-04 10 views
9

Tengo una función de la formaUsando una variable de ajuste de patrones en Ocaml o F #

'a -> ('a * int) list -> int 

let rec getValue identifier bindings = 
    match bindings with 
    | (identifier, value)::tail -> value 
    | (_, _)::tail -> getValue identifier tail 
    | [] -> -1 

Puedo decir que identifier no se está obligado el camino me gustaría que a y está actuando como una nueva variable dentro de la expresión del partido. ¿Cómo consigo que identifier sea lo que se pasa a la función?

Ok! Lo arreglé con un patrón de protección, es decir, | (i, value)::tail when i = indentifier -> value , pero me parece feo en comparación con la forma en que originalmente quería hacerlo (solo estoy usando estos idiomas porque son bonitos ...). ¿Alguna idea?

+0

Su enfoque original me recuerda la unificación de Prolog, es bastante declarativo que funcional. – ron

Respuesta

10

Puede usar patrones activos F # para crear un patrón que hará exactamente lo que necesita. F # admite patrones activos parametrizados que toman el valor que está igualando, pero también toma un parámetro adicional.

Aquí es un ejemplo bastante estúpido que falla cuando el value es cero y por lo demás correctamente y devuelve la suma del valor y el parámetro especificado:

let (|Test|_|) arg value = 
    if value = 0 then None else Some(value + arg) 

Puede especificar el parámetro de comparación de patrones de esta manera:

match 1 with 
| Test 100 res -> res // 'res' will be 101 

Ahora, podemos definir fácilmente un patrón activo que comparará el valor emparejado con el argumento de entrada del patrón activo. El patrón vuelve activa unit option, lo que significa que no se une ningún valor nuevo (en el ejemplo anterior, volvió algún valor que asignamos a un símbolo res):

let (|Equals|_|) arg x = 
    if (arg = x) then Some() else None 

let foo x y = 
    match x with 
    | Equals y -> "equal" 
    | _ -> "not equal" 

Puede utilizar esto como una anidada patrón, por lo que debería poder volver a escribir su ejemplo utilizando el patrón activo Equals.

+0

Interesante (la escritura de los patrones activos para un tipo debe parecer una repetición, pero solo debe escribirlos una vez y aprovecharlos muchas veces). ¿Cómo se muestran en una firma de módulo? –

+0

El patrón activo 'Igual' es genérico y funciona para cualquier tipo que admita la comparación (una restricción especial en las variables de tipo disponibles en F #). Los patrones activos se muestran como funciones de un nombre especial en la firma. La firma tiene el siguiente aspecto: 'val (| Igual a | _ |): 'a ->' a -> unidad cuando 'a: equality' –

3

Esta es una queja común, pero no creo que haya una buena solución en general; un patrón de guardia es generalmente el mejor compromiso. Sin embargo, en ciertos casos específicos existen alternativas, como marcar literales con el atributo [<Literal>] en F # para que se puedan comparar.

5

Esto no es directamente una respuesta a la pregunta: cómo hacer coincidir patrones con el valor de una variable. Pero tampoco está completamente relacionado.

Si desea ver qué poderosa coincidencia de patrones podría haber en un lenguaje tipo ML similar a F # u OCaml, eche un vistazo a Moca.

También puedes echarle un vistazo al código generado por Moca :) (no es que haya nada de malo en que el compilador haga muchas cosas por ti. En algunos casos, es deseable, incluso, pero muchos programadores les gusta sentir que saben lo que costarán las operaciones que están escribiendo).

+0

+1: ¡genial! Parece una opción interesante para implementar DSL. – Juliet

6

Una de las bellezas de los lenguajes funcionales son las funciones de orden superior. Al usar esas funciones sacamos la recursión y solo nos enfocamos en lo que realmente queremos hacer. Que es para obtener el valor de la primera tupla que coincide con su identificador de lo contrario devuelve -1:

let getValue identifier list = 
match List.tryFind (fun (x,y) -> x = identifier) list with 
    | None  -> -1 
    | Some(x,y) -> y 

//val getValue : 'a -> (('a * int) list -> int) when 'a : equality 

Este paper por Graham Hutton es una gran introducción a lo que puede hacer con funciones de orden superior.

+1

* ojos explotan * El estilo sin puntos es inteligente, pero no se puede leer y no se puede mantener. 'fun x -> x |> List.map fst |> List.filter (fun y -> y = identifier)' expresa lo mismo sin sacrificar la legibilidad. – Juliet

+0

Punto tomado. Espero que esta revisión sea mejor. :) –

+0

Cierto, tryFind sería el mejor uso del lenguaje ya que solo estaba temporalmente usando -1 como sustituto de None. Pero me alegro de haber aprendido un poco más sobre la coincidencia de patrones y el enlace variable. –

2

Lo que estás tratando de hacer se llama un patrón de igualdad, y no lo proporciona Objective Caml. Los patrones de Object Caml son estáticos y puramente estructurales. Es decir, si un valor coincide con el patrón depende únicamente de la estructura del valor, y de una manera que se determina en tiempo de compilación. Por ejemplo, (_, _)::tail es un patrón que coincide con cualquier lista no vacía cuyo encabezado es un par. (identifier, value)::tail coincide exactamente con los mismos valores; la única diferencia es que este último une dos nombres más identifier y value.

Aunque algunos idiomas tienen patrones de igualdad, existen consideraciones prácticas no triviales que los hacen problemáticos. Cual igualdad? Igualdad física (== en Ocaml), igualdad estructural (= en Ocaml), o alguna igualdad personalizada dependiente del tipo? Además, en Ocaml, hay una indicación sintáctica clara de qué nombres son carpetas y qué nombres son referencia de valores previamente encuadernados: cualquier identificador de minúsculas en un patrón es un encuadernador. Estas dos razones explican por qué Ocaml no tiene patrones de igualdad integrados. La forma idiomática de expresar un patrón de igualdad en Ocaml está en guardia. De esta forma, queda inmediatamente claro que la coincidencia no es estructural, que identifier no está vinculado por esta coincidencia de patrón, y que igualdad está en uso. En cuanto a lo feo, eso está en el ojo del espectador: como programador habitual de Ocaml, encuentro que los patrones de igualdad son feos (por las razones anteriores).

match bindings with 
| (id, value)::tail when id = identifier -> value 
| (_, _)::tail -> getValue identifier tail 
| [] -> -1 

En F #, que tienen otra posibilidad: active patterns, que le permiten predefinir los guardias que se refieren a un solo sitio en un patrón.

Cuestiones relacionadas