我正在学习 OCaml,但在函数式编程方面遇到了一些麻烦......
我需要创建一个函数来替换给定整数列表的 a ,即字符串索引处的字符。举例来说,我得到 [1;4] 'u'“Hello”作为输入,它输出“Hullu”。
这就是我想出的:
let remplace_positions lst c str =
let rec acc lst c str n l_index ret =
match (n >= String.length str, List.nth lst l_index = n) with
| true, _ -> ret
| _, true -> acc lst c (n+1) (l_index+1) (ret ^ (String.make 1 c))
| _, _ -> acc lst c (n+1) (l_index+1) (ret ^ (String.make 1 (String.get str n)))
in
acc lst c str 0 0 ""
但是正如您所看到的,最后两个匹配案例存在错误。它们的类型是 string -> string,但我希望它们是 string 类型。
有谁知道如何在递归调用函数时解决这个问题?
当我测试你的功能时,我得到:
# let remplace_positions lst c str =
let rec acc lst c str n l_index ret =
match (n >= String.length str, List.nth lst l_index = n) with
| true, _ -> ret
| _, true -> acc lst c (n+1) (l_index+1) (ret ^ (String.make 1 c))
| _, _ -> acc lst c (n+1) (l_index+1) (ret ^ (String.make 1 (String.get str n)))
in
acc lst c str 0 0 "";;
Error: This expression has type int
but an expression was expected of type string
参考行中的
(n+1)
:
| _, true -> acc lst c (n+1) (l_index+1) (ret ^ (String.make 1 c))
这是因为您已指示
acc
的第三个参数应该是 string
,但随后您将其传递给 int
。
我建议您无论如何都会向
acc
传递太多参数,从而很难跟踪它们。如果它们不改变,则内部函数不需要将它们作为参数。
我们还可以将字符分解为字符串。
let remplace_positions lst c str =
let char_to_str c = String.make i c in
let rec acc str n l_index ret =
match (n >= String.length str, List.nth lst l_index = n) with
| true, _ -> ret
| _, true -> acc (n+1) (l_index+1) (ret ^ char_to_str c)
| _, _ -> acc (n+1) (l_index+1) (ret ^ char_to_str (String.get str n))
in
acc str 0 0 ""
同样的错误,但代码更容易阅读。
不过,说实话,你的基本方法并不值得保留。使用
List.nth
效率很低,因为它是 O(n) 操作而不是 O(1)。
您最终需要做的是迭代列表并将每个索引处的字符替换为您指定的字符。列表上的模式匹配提供了完成列表迭代的最惯用的方法。
let rec replace_chars lst c str =
let len = String.length str in
let cs = String.make 1 c in
match lst with
| [] -> str
| hd::tl ->
let front_of_str = String.sub str 0 hd in
let tail_of_str = String.sub str (hd + 1) (len - hd - 1) in
replace_chars tl c (front_of_str ^ cs ^ tail_of_str)
但是 OCaml 中字符串不可变会让事情变得混乱。所有获取子字符串并将它们连接在一起的操作确实占用了大量空间。如果它们是可变的,那就更容易了。
幸运的是,这就是
Bytes
模块的用途。我们可以转换为可变 bytes
类型,修改它,然后转换回 string
。
let replace_chars lst c str =
let b = Bytes.of_string str in
let rec aux lst =
match lst with
| [] -> b
| hd::tl -> (Bytes.set b hd c; aux tl)
in
String.of_bytes (aux lst)
但是真正迭代列表并对每个元素应用操作已经由
List.iter
处理了。
let replace_chars lst c str =
let b = Bytes.of_string str in
List.iter (fun i -> Bytes.set b i c) lst;
String.of_bytes b