Categories
Tech

F# Friday – The find and tryFind Functions

The F# collections modules define the find function. The find operation traverses a collection and returns the first item in the collection that meets the given condition(s).

Perhaps you are a teacher. You have a type that represents a student with a name and a grade:

type Student(name: string, grade: int) =
    member x.Name = name
    member x.Grade = grade
    override x.ToString() =
        sprintf "(%s, %d)" name grade

And you have a randomized list of those students:

let students = [ Student("Sally", 79);
                 Student("Giedrius", 81);
                 Student("Aryana", 98);
                 Student("Ty", 94);
                 Student("Eloise", 86);
                 Student("Vergil", 89);
                 Student("Doug", 66);
                 Student("Delmar", 77);
                 Student("Makenna", 88);
                 Student("Orval", 93) ]

Maybe you want to reward a random A-student (i.e., a student with a grade of 90 or above) with a candy bar. You can use find to return the first A-student in the list:

let scholar = students 
               |> List.find (fun s -> s.Grade >= 90)
// val scholar : Student = (Aryana, 98)

So, that’s great: Aryana gets a Twix! (It is the only one with the cookie crunch, after all.)

You also happen to be one of those sadistic teachers, though, who likes to embarrass the students who are not living up to their potential. Therefore you pick a random F-student (i.e., with a grade less than 65) and outfit him/her with a dunce cap:

let dunce = students 
            |> List.find (fun s -> s.Grade < 65)

// An unhandled exception of type 
// 'System.Collections.Generic.KeyNotFoundException'
// occurred in FSharp.Core.dll

Whoa! Exception? Where did that come from? (Who’s the dunce now?)

That’s a shortcoming of find. If no element meets the condition, find throws an exception.

Enter tryFind. The tryFind function returns an Option. Even on an empty collection, tryFind does not throw an exception.

let dunceOpt = students 
               |> List.tryFind (fun s -> s.Grade < 65)
// val dunceOpt : Student option = None

Of course, you may give a test one week that slays the entire class: you may not have an A-student that week. You’d better use tryFind to find an A-student, too:

let scholarOpt =
    students 
    |> List.tryFind (fun s -> s.Grade >= 90)
// val scholarOpt : Student option =
//   Some (Aryana, 98)

Now you can test dunceOpt and scholarOpt before you use them to make sure someone is supposed to get a candy bar or a dunce cap before you start handing them out.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.