在一定条件下提取字符串中的子字符串

问题描述 投票:0回答:1

希望你一切都好! 我向你提出我的问题。我有一个字符串(在单行上),其想法是从字符串中的给定位置迭代该字符串,逐个字符向左移动,并在遇到分隔符时停止(分隔符位于列表中),然后执行以下操作右侧和末尾相同,将两者放在一起形成您要查找的单词。我编写了以下函数,但它不太有效,当我将自己定位在单词的末尾(如位置 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" *)

谢谢你们,祝大家好!

string ocaml
1个回答
0
投票

跳出的一个大问题是使用

==
来测试两个字符串是否相等。这是行不通的,因为
==
测试物理同一性,而不是像
=
那样测试结构同一性;确定两者在内存中是否是相同的项目,而不是它们是否包含相同的值。

# "" == "";;
- : 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
处的字符是
' '
,因此空字符串非常有意义。

© www.soinside.com 2019 - 2024. All rights reserved.