2012-03-25 83 views
8

我試圖傳入一個字符串來獲取反轉的字符串。爲什麼我不能這樣做:爲字符串OCaml函數參數模式匹配

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

編譯器說這是一個語法錯誤。我不能使用^來解構參數嗎?

+0

此外,對於下面的解釋:如果在字符串中應編譯器甚至拆分字符串? 「abc」改爲「a」「bc」或「ab」「c」 – 0x434D53 2015-11-25 15:51:21

回答

15

這樣做的原因是字符串沒有以與列表相同的方式表示爲數據類型。因此,雖然cons(:)是構造函數,但^是而不是。相反,字符串被表示爲沒有遞歸定義的低級別類型(如列表所示)。有一種方法可以將字符串匹配爲字符列表,使用SML中的函數(可以在OCaml中編寫),稱爲'explode'和'implode',分別將字符串轉換爲char列表,​​反之亦然。 Here's an example implementation of them.

+0

謝謝。但無論如何,像C++那樣「超載」操作數呢?這絕對會更好的抽象。 – lkahtz 2012-03-25 22:33:18

+1

沒有辦法讓操作員過載。字符串被定義爲較低級別的類型,並且當它們基本上不屬於歸納定義的類型時,不能將它們人爲地強制爲數據類型。 – 2012-03-25 23:12:12

1

當您編寫模式匹配表達式時,不能在模式中使用任意函數。您只能使用構造函數,它看起來像未評估的函數。例如,函數「+」在整數上定義。所以表達式1+2被評估並給出3;評估功能「+」,所以你不能匹配x+y。這裏是一個試圖定義自然數的函數,檢查數量是否爲零:

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

這可不行!出於同樣的原因,您的字符串示例無法工作。函數「^」是在字符串上計算的,它不是構造函數。

x+1上的匹配僅適用於未評估數字的符號表達由未評估的運算符+和符號常量1構成的符號表達式。這在OCAML中並非如此。整數通過機器編號直接實現。

當你匹配變體類型時,你匹配構造函數,這是未評估的表達式。例如:

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

此工作,因爲該'a option類型是做出來的一個象徵性的表達方式,如Some x。在這裏,Some不是一個被評估的函數,它提供了一些其他的值,而是一個「構造函數」,你可以把它看作是一個永遠不會被評估的函數。表達式Some 3不再被評估;它保持原樣。只有在這樣的功能上,您才能進行模式匹配。

列表也是構造函數構建的符號,未評估的表達式;構造函數是::x :: y :: []的結果是一個未評估的表達式,僅爲了美觀方便而由列表[x;y]表示。出於這個原因,你可以在列表上進行模式匹配。

+0

感謝您的回答。如果可以提及OCaml中字符串的模式匹配是否可行並且如何實現,那將會很好。 – 2017-09-25 19:11:38

1

作爲Kristopher Micinski explained,由於它們不是列表,因此無法對字符串進行模式匹配。

但是,您可以使用explode將它們轉換爲列表。下面是使用explode和其對應implodereverse功能與模式匹配:

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

使用方法如下:

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

這表明,它適用於單字節字符而不是多字節的人。

這裏是explodeimplode與助手一起的方法:

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)