2012-03-25 16 views
8

Ho provato a passare una stringa per ottenere una stringa invertita. Perché non posso farlo:Corrispondenza modello parametro OCaml per stringhe

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

Il compilatore dice che si tratta di un errore di sintassi. Non posso usare ^ per i parametri di destructure?

+0

Inoltre per la spiegazione di seguito: Dove nella stringa deve il compilatore anche dividere la stringa? "abc" a "a" "bc" o "ab" "c" – 0x434D53

risposta

15

La ragione di ciò è che le stringhe non sono rappresentate come un tipo di dati nello stesso modo degli elenchi. Pertanto, mentre cons (: :) è un costruttore,^è non. Invece, le stringhe sono rappresentate come un tipo di livello inferiore senza una definizione ricorsiva (come sono gli elenchi). C'è un modo per far corrispondere le stringhe come una lista di caratteri, usando una funzione da SML (che puoi scrivere in OCaml) chiamata "esplode" e "implode" che - in modo relativo - prende una stringa in una lista char e viceversa . Here's an example implementation of them.

+0

Grazie. Ma è comunque necessario "sovraccaricare" l'operando come fa il C++? Ciò sicuramente migliorerà l'astrazione. – lkahtz

+1

No. Non c'è modo di sovraccaricare l'operatore. Le stringhe sono definite come un tipo di livello inferiore e non è possibile imporle artificialmente in un tipo di dati quando sono fondamentalmente _non_ un tipo definito in modo induttivo. –

1

Quando si scrive un'espressione di corrispondenza del modello, non è possibile utilizzare le funzioni arbitrarie nei propri motivi. È possibile utilizzare solo costruttori, che assomigliano a funzioni non valorizzate. Ad esempio, la funzione "+" è definita su numeri interi. Quindi l'espressione 1+2 viene valutata e dà 3; la funzione "+" viene valutata, quindi non è possibile trovare una corrispondenza su x+y. Ecco un tentativo di definire una funzione su numeri naturali che controlla se il numero è zero:

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

Questo non funziona! Per lo stesso motivo, il tuo esempio con le stringhe non può funzionare. La funzione "^" viene valutata su stringhe, non è un costruttore.

L'abbinamento su x+1 funzionava solo se i numeri erano espressioni simboliche non valutate costituite dall'operatore non valutato + e una costante simbolica 1. Questo non è il caso in OCAML. Gli integer sono implementati direttamente attraverso i numeri di macchina.

Quando si abbina un tipo di variante, si corrispondono ai costruttori, che sono espressioni non valutate. Ad esempio:

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

Ciò funziona perché il tipo 'a option è fatto di un'espressione simbolica, come Some x. Qui, Some non è una funzione che viene valutata e fornisce qualche altro valore, ma piuttosto un "costruttore", a cui si può pensare come una funzione che non viene mai valutata. L'espressione Some 3 non viene valutata ulteriormente; rimane così com'è. È solo su tali funzioni che è possibile eseguire il pattern-match.

Le liste sono anche espressioni simboliche e non valutate costruite con costruttori; il costruttore è ::. Il risultato di x :: y :: [] è un'espressione non valutata, che è rappresentata dall'elenco [x;y] solo per comodità cosmetica. Per questo motivo, è possibile eseguire il pattern-match sugli elenchi.

+0

Grazie per la risposta. Sarebbe bello se potessi menzionare se e come la corrispondenza dei pattern sulle stringhe sia possibile in OCaml. –

1

Come Kristopher Micinski explained, non è possibile eseguire lo schema di corrispondenza su stringhe poiché non sono elenchi.

Ma è possibile convertirli in elenchi utilizzando explode.Ecco la funzione reverse con pattern matching con explode e la sua controparte implode:

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

usare in questo modo:

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

che dimostra che funziona per caratteri a byte singolo, ma non per quelli multi-byte .

E qui sono explode e implode insieme a un metodo di supporto:

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) 
Problemi correlati