5
votes

I am completely new to F# (started using it today) and relatively new to functional programming (I have minor experience with Lisp). I want to exit a function by returning a value when a certain condition is met so that the rest of the loop is not executed. Here is a C# illustration of what I want to do:

bool CheckRow (int n, int i)
{
    for(int j = 0; j < 9; j++)
        if (n == sudoku[i][j])
            return false;

    return true;
}

I tried implementing the same function in F# like this (sudoku is an array2D):

let CheckRow (n : int) (i : int) : bool = 

    for j = 0 to 8 do
        if (n = sudoku.[i, j]) then
            false

    true

However, I get the following error at false within the if: "This expression was expected to have type unit but here has type bool". What is the proper way to "return" from within a F# function?

2
I would assume here that you aren't doing this in an idiomatic way, I think you could use the seq type to help you here and do it a more functional way - Callum Linington
If you think you need to "exit early" out of an F# function, that's usually a clue that you're thinking about the problem wrong. Here, you want to see if there is any value in the row that fulfills a certain condition. That's what the Array.exists function is for. (Or List.exists, or Seq.exists -- see Bartek Kobylecki's answer). - rmunn
Control Flow and the whole series is a nice and quick overview. - s952163
Two resources that you should know about if you're just getting started with F#: F# for Fun and Profit is the best intro to F# anywhere on the Net, and Exercism is an excellent collection of F# exercises, roughly in order from easy to hard, to help ease you into thinking with functions. - rmunn

2 Answers

6
votes

Higher-order functions are nice of course, but at some point someone has to write a loop (e.g. to implement the higher-order function), and that'll eventually be you, so it's nice to know how to write loops in F#. There is no early return from a for loop in F#, but other types of loops do allow this:

// While loop, imperative style
let checkRow n i =
    let mutable clear = true
    let mutable j = 0
    while clear && j < 9 do
        clear <- n <> sudoku.[i, j]
        j <- j + 1
    clear

// Tail-recursive style - more idiomatic F#
let checkRow n i =
    let rec loop j =
        if j = 9 then true
        elif sudoku.[i, j] = n then false
        else loop (j + 1)
    loop 0
3
votes

Normally you shouldn't need to break function earlier but rather end recursion on some case, otherwise call function recursively. Here recursion might be hidden because you are operating on lists or matrixes.

List.forall is one of those functions that implement recursion over the list and returns the result on the first occasion. You could write you function this way:

let CheckRow (expectedValue : int) (rowIndex : int) =
    [0..8] |> List.forall (fun colIndex ->
        sudoku.[rowIndex, colIndex] <> expectedValue)