OCaml 这个变体表达式应该有类型 unit

OCaml This variant expression is expected to have type unit

我无法理解 if 语句的问题。
我的代码:

type chess_board = char array array;;
type position = int * int;;

let chess_board_1:chess_board = [|
    [|' ';' ';' ';' ';' ';' ';' ';' '|];
    [|' ';' ';' ';' ';' ';' ';' ';' '|];
    [|' ';' ';' ';' ';' ';' ';' ';' '|];
    [|' ';' ';' ';' ';' ';' ';' ';' '|];
    [|' ';' ';' ';' ';' ';' ';' ';' '|];
    [|' ';' ';' ';' ';' ';' ';' ';' '|];
    [|' ';' ';' ';' ';' ';' ';'Q';' '|];
    [|' ';' ';' ';' ';' ';' ';' ';' '|];
    |];;
    
let queen_targeting (chess:chess_board) pos =
    match pos with
    |(x0, y0) ->
        for y = 0 to Array.length chess -1 do
            for x = 0 to Array.length chess.(y) -1 do
                if chess.(y).(x) = 'Q' 
                then
                    if (
                        x = x0 ||         (* Check horizontaly *)
                        y = y0 ||         (* Check verticaly *)
                        x - x0 = y - y0   (* Check diagonaly *)
                    ) 
                    then true
                    else false
            done
        done
;;

queen_targeting chess_board_1 (3, 3);; (* Expected true *)

我收到这条错误消息:

File "[32]", line 27, characters 25-29:
Error: This variant expression is expected to have type unit
       The constructor true does not belong to type unit
Characters 874-878:
                      then true

我不知道这是什么意思。我测试了 if 语句在其他方法中返回 true/false 并且它工作得很好。我不知道为什么它在这种情况下不起作用,所以如果有人可以提供帮助,请帮忙。

“for 表达式”必须 return unit,因此不会传播结果。例如:

# let x = for y = 0 to 10 do true done;;

潜在客户发出此警告:

Warning 10: this expression should have type unit.
val x : unit = ()

一个解决方案是使用可变引用:

type chess_board = char array array;;
type position = int * int;;

let chess_board_1:chess_board = [|
    [|' ';' ';' ';' ';' ';' ';' ';' '|];
    [|' ';' ';' ';' ';' ';' ';' ';' '|];
    [|' ';' ';' ';' ';' ';' ';' ';' '|];
    [|' ';' ';' ';' ';' ';' ';' ';' '|];
    [|' ';' ';' ';' ';' ';' ';' ';' '|];
    [|' ';' ';' ';' ';' ';' ';' ';' '|];
    [|' ';' ';' ';' ';' ';' ';'Q';' '|];
    [|' ';' ';' ';' ';' ';' ';' ';' '|];
    |];;
    
let queen_targeting (chess:chess_board) pos =
    match pos with
    |(x0, y0) ->
        let result = ref false in
        for y = 0 to Array.length chess -1 do
            for x = 0 to Array.length chess.(y) -1 do
                if chess.(y).(x) = 'Q' 
                then
                    if (
                        x = x0 ||         (* Check horizontaly *)
                        y = y0 ||         (* Check verticaly *)
                        x - x0 = y - y0   (* Check diagonaly *)
                    ) 
                    then result := true
                    else result := false
            done
        done;
        false || !result

;;

queen_targeting chess_board_1 (3, 3);;

一个更实用的解决方案是使用 Array.iteri:

let queen_targeting (chess:chess_board) (x0,y0) =
  let exception Return of bool in
  let threat y x case =
    if case = 'Q' && (x = x0 || y = y0 || x - x0 = y - y0) then
      raise (Return true)
  in
  match Array.iteri (fun y -> Array.iteri (threat y)) chess with
  | () -> false
  | exception Return b -> b

这里的异常用于当我们发现一个女王瞄准该位置时立即返回。

其他答案已经解决了您的直接问题,但也有一些机会可以解决您的代码中不适合发表评论的问题。希望这些建议可以通过消除外来噪音,使您将来更容易对代码进行推理。

这听起来很迂腐,但请记住 OCaml 中没有条件 语句 。相反,它们是具有值的 expressions。我想你已经明白这一点了,但术语很重要。

此外,您使用条件语句的方式存在一些问题。如果您唯一要做的就是返回 true 或 false,那么直接使用布尔表达式即可。考虑一个简单的例子。

if a < b && b < c then true else false

这与:

没有区别
a < b && b < c

第三,当你有嵌套的条件时,这些通常可以被简化。我将稍微修改一下您的代码并暂时删除注释。

if chess.(y).(x) = 'Q' then
  if x = x0 || y = y0 || x - x0 = y - y0 then 
    true
  else 
    false

我们知道我们可以将其简化为:

if chess.(y).(x) = 'Q' then
  x = x0 || y = y0 || x - x0 = y - y0 

但是如果第一个条件通过,我们只会检查 ||ed 条件,所以我们可以将整个事情重写为:

chess.(y).(x) = 'Q' && (x = x0 || y = y0 || x - x0 = y - y0) 

这可以在@octachron 发布的答案中看到(但未详细说明)。