Ожидалось, что это выражение будет иметь тип bool, но здесь имеет тип unit error

#f#

#f#

Вопрос:

получение ошибки при попытке запустить эту строку кода, и я не могу понять, почему

     let validCol column value : bool = 
        for i in 0..8 do
            if sudokuBoard.[i,column] = value then 
                false
            else true
 

Ответ №1:

Как говорит Тайлер Хартвиг for , цикл не может возвращать значение, кроме unit .

С другой стороны, внутри понимания списка или seq выражения вычисления вы можете использовать for yield значения, а затем проверить, существует ли то, что вы ищете:

 let validCol column value : bool = 
    seq { for i in 0..8 do yield sudokuBoard.[i,column] }
    |> Seq.exists value
    |> not
 

Ответ №2:

В F # возвращается последний выполненный вызов, вы явно объявили, что возвращаете bool .

for Цикл не может вернуть или объединить несколько значений, вместо этого возвращается bun unit .

 let validCol column value : bool = 
    for i in 0..8 do
        if sudokuBoard.[i,column] = value then 
            false
        else 
            true
 

Здесь вам нужно выяснить, как объединить все bool , чтобы получить конечный результат. Я не совсем уверен, что это должно возвращать, или я бы привел пример.

Ответ №3:

Похоже, вы ищете короткий выход из цикла, как в C #, который вы можете использовать continue , break или return для выхода из цикла.

В F # способ добиться этого с точки зрения производительности — использовать хвостовую рекурсию. Вы могли бы достичь этого с помощью циклов while, но для этого требуются изменяемые переменные, которые не нужны для хвостовой рекурсии (хотя мы иногда ее используем).

Хвостовая рекурсивная функция — это функция, которая вызывает себя в самом конце и не смотрит на результат:

Так что это хвостовая рекурсия

 let rec loop acc i = if i > 0 then loop (acc   i) (i - 1) else acc
 

Где это не так

 let rec loop fib i = if i < 1 then 1 else fib (i - 1)   fib (i - 2)
 

Если компилятор F # определяет, что функция является хвостовой рекурсией, компилятор применяет к функции оптимизацию хвостовой рекурсии (TCO), по сути, он разворачивает ее в эффективный цикл for, который очень похож на цикл в C #.

Итак, вот один из способов записи validCol с использованием хвостовой рекурсии:

 let validCol column value : bool =
  // loops is tail-recursive
  let rec loop column value i =
    if i < 9 then
      if sudokuBoard.[i,column] = value then 
        false // The value already exists in the column, not valid
      else
        loop column value (i   1)  // Check next row.
    else
      true  // Reach the end, the value is valid
  loop column value 0
 

К сожалению; У компилятора F # нет атрибута для принудительного TCO (как у Scala или kotlin), и поэтому, если вы допустите небольшую ошибку, вы можете получить функцию, которая не является TCO. Кажется, я видел проблему GitHub о добавлении такого атрибута.

PS. seq понимание во многих случаях хорошо, но для решателя судоку я предполагаю, что вы ищете что-то максимально быстрое. seq понимание (и LINQ) Я думаю, что добавляет слишком много накладных расходов для решателя судоку, тогда как хвостовая рекурсия выполняется примерно так же быстро, как вы можете получить в F #.

PS. В .NET 2D-массивы работают медленнее, чем 1D-массивы, просто к вашему сведению. Не уверен, улучшилось ли оно с ядром dotnet.