我尝试传入一个字符串来获取反向字符串。为什么我不能这样做:
let rec reverse x =
match x with
| "" -> ""
| e ^ s -> (reverse s) ^ e;;
编译器说它是语法错误。我不能用^
来构造参数吗?
答案 0 :(得分:18)
原因是字符串不像列表那样表示为数据类型。因此,虽然cons(::)是构造函数,但^ 不是。相反,字符串表示为较低级别类型而没有递归定义(如列表所示)。有一种方法可以将字符串作为字符列表进行匹配,使用来自SML的函数(可以在OCaml中编写),称为'explode'和'implode',它分别将字符串转换为字符列表,反之亦然。 Here's an example implementation of them.
答案 1 :(得分:2)
编写模式匹配表达式时,不能在模式中使用任意函数。您只能使用构造函数,它们看起来像未评估的函数。例如,函数“+”在整数上定义。所以表达式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]
表示。因此,您可以在列表上进行模式匹配。
答案 2 :(得分:2)
作为Kristopher Micinski explained,您无法像使用列表一样使用模式匹配来分解字符串。
但您可以使用explode
将它们转换为列表。这是您使用reverse
及其对应explode
进行模式匹配的implode
函数:
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)
这表明它适用于单字节字符,但不适用于多字节字符。
以下是explode
和implode
以及辅助方法:
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)