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

Tru*_* Ta 8 ocaml if-statement

在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.
Run Code Online (Sandbox Code Playgroud)

如何美化上面的代码?我喜欢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;
}
Run Code Online (Sandbox Code Playgroud)

Pat*_*atJ 5

returnOCaml中没有声明,但您可以在例外的帮助下模拟一个声明:

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
Run Code Online (Sandbox Code Playgroud)

另一个有用的解决方案是使用延迟评估:

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
Run Code Online (Sandbox Code Playgroud)

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


ivg*_*ivg 5

核心库提供了一个with_return函数,允许您从函数中执行非本地存在:

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)
Run Code Online (Sandbox Code Playgroud)

但通常最好使用模式匹配或重新考虑代码.例如,如果您有一个谓词列表,并且根据谓词是什么,您想要返回一个值,这意味着您可以将其编码为某个映射结构中的搜索:

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

当然,在这种情况下,您没有短路评估.您可以使用延迟评估或使用thunk来解决此问题.但除非你的计算非常重或产生副作用,否则它不值得.