2012-03-25 6 views
8

Intenté pasar una cadena para obtener una cadena invertida. ¿Por qué no puedo hacer esto?Coincidencia de patrón de parámetro de función OCaml para las cadenas

let rec reverse x = 
    match x with 
    | "" -> "" 
    | e^s -> (reverse s)^e;; 

El compilador dice que es un error de sintaxis. ¿Puedo usar ^ para desestructurar parámetros?

+0

Además de la explicación a continuación: ¿Dónde en la cadena debe el compilador incluso dividir la cadena? "abc" a "a" "bc" o "ab" "c" – 0x434D53

Respuesta

15

La razón de esto es que las cadenas no se representan como un tipo de datos del mismo modo que las listas. Por lo tanto, mientras cons (: :) es un constructor,^es no. En cambio, las cadenas se representan como un tipo de nivel inferior sin una definición recursiva (como las listas). Hay una forma de hacer coincidir cadenas como una lista de caracteres, usando una función de SML (que puede escribir en OCaml) llamada 'explosión' y 'implosión' que --respectivamente - lleva una cadena a una lista de caracteres y viceversa . Here's an example implementation of them.

+0

Gracias. ¿Pero de todos modos es 'sobrecargar' el operando como lo hace C++? Eso definitivamente mejorará la abstracción. – lkahtz

+1

No. No hay forma de sobrecargar al operador. Las cadenas se definen como un tipo de nivel inferior, y no puede imponerlas artificialmente en un tipo de datos cuando son fundamentalmente _no_ un tipo definido inductivamente. –

1

Cuando escribe una expresión de coincidencia de patrón, no puede usar funciones arbitrarias en sus patrones. Solo puede usar constructores, que se parecen a funciones no evaluadas. Por ejemplo, la función "+" se define en enteros. Entonces la expresión 1+2 se evalúa y da 3; la función "+" se evalúa, por lo que no puede coincidir en x+y. Aquí hay un intento de definir una función en los números naturales que verifica si el número es cero:

let f x = match x with 
    | 0 -> false 
    | a+1 -> true 
;; 

¡Esto no puede funcionar! Por la misma razón, su ejemplo con cadenas no puede funcionar. La función "^" se evalúa en cadenas, no es un constructor.

La coincidencia en x+1 funcionaría solo si los números fueran expresiones no evaluadas simbolizadas hechas del operador no evaluado + y una constante simbólica 1. Este no es el caso en OCAML. Los enteros se implementan directamente a través de números de máquina.

Cuando coincide con un tipo de variante, hace coincidir en constructores, que son expresiones no evaluadas. Por ejemplo:

# let f x = match x with 
    | Some x -> x+1 
    | None -> 0 
;; 
val f : int option -> int = <fun> 

Esto funciona porque el tipo 'a option está hecho de una expresión simbólica, como Some x. Aquí, Some no es una función que se evalúa y proporciona algún otro valor, sino más bien un "constructor", que se puede considerar como una función que nunca se evalúa. La expresión Some 3 ya no se evalúa; permanece como está. Es solo en tales funciones que puede emparejar patrones.

Las listas también son expresiones simbólicas, no evaluadas, construidas a partir de constructores; el constructor es ::. El resultado de x :: y :: [] es una expresión no evaluada, que se representa mediante la lista [x;y] solo por conveniencia cosmética. Por esta razón, puede emparejar patrones en listas.

+0

Gracias por su respuesta. Sería genial si pudieras mencionar si y cómo la coincidencia de patrones en cadenas es posible en OCaml. –

1

Como Kristopher Micinski explained, no puede coincidir con el patrón en cadenas, ya que no son listas.

Pero puede convertirlos en listas, usando explode.Aquí está su función reverse con la coincidencia de patrones usando explode y su contraparte implode:

let rec reverse str = 
    match explode str with 
    [] -> "" 
    | h::t -> reverse (implode t)^string_of_char h 

utilizar de esta manera:

let() = 
    let text = "Stack Overflow ♥ OCaml" in 
    Printf.printf "Regular: %s\n" text; 
    Printf.printf "Reversed: %s\n" (reverse text) 

lo que demuestra que funciona para los caracteres de un solo byte, pero no para los multi-byte .

Y aquí están explode y implode junto con un método de ayuda:

let string_of_char c = String.make 1 c 

(* Converts a string to a list of chars *) 
let explode str = 
    let rec explode_inner cur_index chars = 
    if cur_index < String.length str then 
     let new_char = str.[cur_index] in 
     explode_inner (cur_index + 1) (chars @ [new_char]) 
    else chars in 
    explode_inner 0 [] 

(* Converts a list of chars to a string *) 
let rec implode chars = 
    match chars with 
    [] -> "" 
    | h::t -> string_of_char h^(implode t) 
Cuestiones relacionadas