If statement 在OCaml中编写漂亮的多级嵌套if-then-else代码?

If statement 在OCaml中编写漂亮的多级嵌套if-then-else代码?,if-statement,ocaml,If Statement,Ocaml,在OCaml中,如果我必须使用许多if-then-else编写函数,下面是我愚蠢而丑陋的解决方案 let foo () = let a1 = ... in if (a1) then result1 else let a2 = ... in if (a2) then result2 else let a3 = ... in if (a3) then result3 else

在OCaml中,如果我必须使用许多if-then-else编写函数,下面是我愚蠢而丑陋的解决方案

let foo () =
  let a1 = ... in
  if (a1) then
    result1
  else
    let a2 = ... in
    if (a2) then
      result2
    else
      let a3 = ... in
      if (a3) then
        result3
      else
        let a4 = ... in
        if (a4) then
          result4
        else
          result5.
如何美化上述代码?我喜欢C/C++&Java风格,它使用“return”保存下一个if语句的缩进。 我可以用OCaml做同样的事情吗

int foo () = {
  bool a1 = ...;
  if (a1)
    return result1;

  bool a2 = ...;
  if (a2)
    return result2;

  bool a3 = ...;
  if (a3)
    return result3;

  bool a4 = ...;
  if (a4)
    return result4;

  return result5;
}

OCaml中没有
return
语句,不过您可以借助异常来模拟一个语句:

exception Ret of t

let my_ret x = raise (Ret x)

let foo () =
 try
  let a1 = ... in
  if a1 then my_ret result1;
  let a2 = ... in
  if a2 then my_ret result2;
  ...
 with Ret x -> x
另一个有用的解决方案是使用惰性评估:

let foo () =
 let a1 = lazy ...
 and a2 = lazy ...
 and a3 = lazy ...
 in
 match a1, a2, a3 with
 | lazy true, _, _ -> result1
 | _, lazy true, _ -> result2
 | _, _, lazy true -> result3
 | _, _, _ -> result4

这是使用lazy的示例之一,可能有更简洁的方式来表达您的计算。

核心库提供了一个带有返回的
函数,允许您执行非本地exists from函数:

open Core_kernel.Std
let foo () = with_return (fun goto -> 
    if a1 then goto.return 1;
    if a2 then goto.return 2;
    if a3 then goto.return 3;
    if a4 then goto.return 4;
    if a5 then goto.return 5;
    return 6)
但一般来说,最好使用模式匹配或重新思考代码。例如,如果您有一个谓词列表,并且根据哪个谓词为true,您希望返回一个值,这意味着您可以将其编码为某种映射结构中的搜索:

let foo () = [
  clause1, expr1;
  clause2, expr2;
  clause3, expr3;
] |> List.Assoc.find true 
  |> Option.value ~default:expr4

当然,在这种情况下,您没有短路评估。您可以使用延迟计算或thunks修复此问题。但是,除非你的计算真的很繁重或者产生副作用,否则它是不值得的。

if
语法结构在C和OCaml中的工作方式确实不一样。在C语言中,
如果
语法形式是语句,那么在OCaml中它们就是表达式。如果是
?:
三元运算符,那么在C中最接近OCaml
。如果您尝试使用此运算符而不是
If
重写C代码,您将面临相同的挑战。但这并不意味着这是不可能的,因为其他答案会给你答案

最简单的方法是将函数体切割成几个子函数(*),并使用continuations,这两种语言都适用:

let rec foo () =
  let a1 = … (* computation *) in
  if a1
  then result1
  else foo2 ()
and foo2 () =
  let a2 = … in
  if a2
  then result1
  else foo3 ()
and foo3 () = … (* etc *)
编写对象方法时可能仍然有点麻烦,但您始终可以使用内部函数来恢复方法范围内的“缩进平衡”

还要注意,
rec
关键字的唯一目的是允许每个延续在源布局中跟随其调用者,这里没有真正的递归



(*):@gsg在评论中也提到了它。

if
表达式不同,
match
子句扩展到函数末尾,即使它们包含多个语句,也不需要括号。因此,您可以:

let foo () =
  match ... with
  | true -> result1
  | false ->
  match ... with
  | true -> result2
  | false ->
  match ... with
  | true -> result3
  | false ->
  match ... with
  | true -> result4
  | false -> result5
在您的示例中,您没有显示
result1
来自何处,因此我不能确定,但您可能会发现让
返回带有结果的选项比返回bool更好,例如

let foo () =
  match ... with
  | Some result1 -> result1
  | None ->
  ...

OCaml没有一个
return
语句:
if
可能是一种方法。如果你感到绝望,你可以把事情分成另一个函数。没有理由把你的第一个例子缩进一个阶梯。几个编辑器会垂直缩进Ocaml中的一些monad库定义一些
返回
函数这个答案一开始让我感到困惑,但在看了命令式版本并思考了一会儿之后。。。这真的很有道理,谢谢你的提示!