希望你一切都好! 我向你提出我的问题。我有一个字符串(在单行上),其想法是从字符串中的给定位置迭代该字符串,逐个字符向左移动,并在遇到分隔符时停止(分隔符位于列表中),然后执行以下操作右侧和末尾相同,将两者放在一起形成您要查找的单词。我编写了以下函数,但它不太有效,当我将自己定位在单词的末尾(如位置 4)时,它返回一个字符串“”而不是返回“this”。当我转到位置 1 时,它向我发送“他的”而不是“这个”。当我将自己置于 44 位置时,我输出“tha”而不是“that”。另一方面,在字符串中间的位置 36 或 40 处,它起作用。 谁能帮我修复这些错误吗?
(* separators *)
let sep_list = [';';',';'(';')';'.';' ';]
(* my string to test *)
let my_str = "this is my string for testing purpose, only that"
(* my function *)
let search_word_in_cursor (str:string) (char_list:char list) (pos:int) : string =
let rec foo_left (str:string) (char_list:char list) (pos:int) (ret:string) : string =
if ((List.exists (fun x -> Char.equal x str.[pos]) char_list) || (pos <= 0)) then ret
else if (pos == 0) then String.make 1 str.[pos] ^ ret
else foo_left str char_list (pos - 1) ((String.make 1 str.[pos]) ^ ret)
in
let rec foo_right (str:string) (char_list:char list) (pos:int) (ret:string) : string =
if ((List.exists (fun x -> Char.equal x str.[pos]) char_list) || (pos >= (String.length str - 1))) then ret
else foo_right str char_list (pos + 1) (ret ^ (String.make 1 str.[pos]))
in
let sl = foo_left str char_list pos "" in
let sr = foo_right str char_list pos "" in
if (sr == "" && sl == "") then ""
else if (sr == "" && sl != "") then sl
else if (sr != "" && sl == "") then sr
else (String.sub sl 0 (String.length sl - 1)) ^ sr
(* expects and results *)
let () =
print_string (search_word_in_cursor my_str sep_list 4); (* expected:"this" output:"" *)
print_string (search_word_in_cursor my_str sep_list 1);(* expected:"this" output:"his" *)
print_string (search_word_in_cursor my_str sep_list 44);(* expected:"that" output:"tha" *)
print_string (search_word_in_cursor my_str sep_list 36);(* expected:"purpose" output:"purpose" *)
print_string (search_word_in_cursor my_str sep_list 40)(* expected:"only" output:"only" *)
谢谢你们,祝大家好!
跳出的一个大问题是使用
==
来测试两个字符串是否相等。这是行不通的,因为 ==
测试物理同一性,而不是像 =
那样测试结构同一性;确定两者在内存中是否是相同的项目,而不是它们是否包含相同的值。
# "" == "";;
- : bool = false
# "" = "";;
- : bool = true
# let s = "" in s == s;;
- : bool = true
您也可以使用模式匹配来代替 if/else。
其次,您使用
List.exists
,其中 List.mem
是确定列表中是否存在某个值的更简洁的方法。 使用一套会更高效。
第三,您使用了很多额外的括号。我们来修剪一下吧。
第四,您实际上不需要将
char_list
或 str
参数传递给内部函数,因为它们可以使用传递给外部函数的参数。
第五,我们可以抽象掉很多重复的代码。
考虑到这些问题,让我们稍微重写一下代码。
let search_word_in_cursor (str:string) (char_list:char list) (pos:int) : string =
let last_pos = String.length str - 1 in
let string_of_char ch = String.make 1 ch in
let is_sep pos = List.mem str.[pos] char_list in
let rec foo_left (pos:int) (ret:string) : string =
if is_sep pos || pos <= 0 then
ret
else if pos = 0 then
String.make 1 str.[pos] ^ ret
else
foo_left (pos - 1) (string_of_char str.[pos] ^ ret)
in
let rec foo_right (pos:int) (ret:string) : string =
if is_sep pos || pos >= last_pos then
ret
else
foo_right (pos + 1) (ret ^ string_of_char str.[pos])
in
let sl = foo_left pos "" in
let sr = foo_right pos "" in
match sl, sr with
| "", "" -> ""
| "", _ -> sl
| _ , "" -> sr
| _ , _ -> String.sub sl 0 (String.length sl - 1) ^ sr
现在,如果我们运行它,我们会看到与您得到的相同的输出:
# search_word_in_cursor my_str sep_list 4;;
- : string = ""
# search_word_in_cursor my_str sep_list 1;;
- : string = "his"
# search_word_in_cursor my_str sep_list 44;;
- : string = "tha"
# search_word_in_cursor my_str sep_list 36;;
- : string = "purpose"
# search_word_in_cursor my_str sep_list 40;;
- : string = "only"
让我们逐步了解当您致电
search_word_in_cursor my_str sep_list 4
时会发生什么:
search_word_in_cursor my_str sep_list 4
foo_left 4 ""
is_sep ' '
""
foo_right 4 ""
is_sep ' '
""
""
发生这种情况是因为
my_str
中的第五个字符是 ' '
,它是分隔符。因此,在 foo_left
和 foo_right
中,这都满足条件中的第一个测试,并且返回 ""
。由于 sl
和 sr
都是空字符串,因此我们从函数返回 ""
。
让我们尝试下一个:
search_word_in_cursor my_str sep_list 1
foo_left 1 ""
is_sep 'h'
foo_left 0 "h"
is_sep 't'
0 <= 0
"h"
foo_right 1 ""
is_sep 'h'
foo_right 2 "h"
is_sep 'i'
foo_right 3 "hi"
is_sep 's'
foo_right 4 "his"
is_sep ' '
"his"
String.sub "h" 0 (String.length "h" - 1) ^ "his"
String.sub "h" 0 0 ^ "his"
"" ^ "his"
"his"
在这种情况和
44
情况下,缺少字母的原因是当我们到达字符串末尾时,我们返回累积的字符串,但不包括 current 字符。
我们可以通过累积该字符,然后向下或向上倒数一个位置来解决此问题。为此,我们只需在上述函数中更改 two 字符:将
>=
和 <=
更改为 >
和 <
。
let search_word_in_cursor (str:string) (char_list:char list) (pos:int) : string =
let last_pos = String.length str - 1 in
let string_of_char ch = String.make 1 ch in
let is_sep pos = List.mem str.[pos] char_list in
let rec foo_left (pos:int) (ret:string) : string =
if is_sep pos || pos < 0 then
ret
else if pos = 0 then
String.make 1 str.[pos] ^ ret
else
foo_left (pos - 1) (string_of_char str.[pos] ^ ret)
in
let rec foo_right (pos:int) (ret:string) : string =
if is_sep pos || pos > last_pos then
ret
else
foo_right (pos + 1) (ret ^ string_of_char str.[pos])
in
let sl = foo_left pos "" in
let sr = foo_right pos "" in
match sl, sr with
| "", "" -> ""
| "", _ -> sl
| _ , "" -> sr
| _ , _ -> String.sub sl 0 (String.length sl - 1) ^ sr
这太棒了!除了...
现在,
is_sep
可以尝试越界访问字符串。这可以通过更改布尔表达式的顺序来解决,以检查索引之前检查该索引处的字符是否是分隔符。
例如
pos < 0 || is_sep pos
因为
||
短路,我们不再获得越界访问权限。
至于您的第一个案例返回
""
而不是 "this"
,这几乎肯定源自对字符串中起始索引的误解。索引 4
处的字符是 ' '
,因此空字符串非常有意义。