如何强制开始ocaml中的下一个表达式

how to force to start next expression in ocaml

我想编写一个函数来实现 grep 的基本用法:匹配文件中的模式。我想 match_file_pattern 到 return 匹配行列表。但是这里的代码无法编译,错误是:

Error: This expression has type string list but an expression was expected of type unit

代码是:

let match_file pattern file_name =
  let matched_lines = ref [] in
  let ic = open_in file_name in

  try
    while true
    do
      let line = input_line ic in
      if (Str.string_match (Str.regexp pattern) line 0)
      then
         matched_lines := line::!matched_lines
    done;**(*!matched_lines*)**(*I need add this to compile successfully*)
  with End_of_file ->
    close_in ic;
    List.rev !matched_lines;;

我认为错误是由于在ocaml中,close_in ic; List.rev !matched_lines被分组到"with"关键字的子表达式中,所以它的类型应该与"try"表达式匹配。我试图找到打破 close_in ic;List.rev !matched_lines 之间关系的方法,但失败了。

循环的类型是 unit,即使它从未完成。类型检查器不知道这一点,因此您需要使 try 下的表达式与异常处理程序具有相同的类型。

在这种情况下,您可以使用任意列表,例如 [],但它对 reader 具有误导性,并且不会泛化到提供表达式可能更复杂的情况正确的类型。

这里惯用的解决方案是放置一个 assert false,如果计算过,它会引发异常。与无限 while 循环不同,类型检查器知道 assert false 不是 return 并且它与任何类型兼容,因为永远不会产生值:

try
  while true do
    ...
  done;
  assert false
with ... -> ...

您可以使用 begin/end 或括号:

let match_file pattern file_name =
  let matched_lines = ref [] in
  let ic = open_in file_name in

  begin
  try
    while true
    do
      let line = input_line ic in
      if (Str.string_match (Str.regexp pattern) line 0)
      then
         matched_lines := line::!matched_lines
    done
  with End_of_file -> close_in ic
  end;
  List.rev !matched_lines

您的代码很好:

done后一个分号,用于指令排序,然后!matched_lines作为try代码部分的return值,然后with ....

这里没有歧义。编译器只是没有考虑到 End_of_file 总是被引发。

剩下的就是编码风格的问题了。我喜欢在这些技术上需要的表达式上添加 (* never reached *) 评论 - 这对于 assert false 提案以及 IMO 来说都是一个好主意。