Home > database >  Head of empty list
Head of empty list

Time:10-22

I'm trying to make a function which returns a list of the first element of each sub-list, including empty lists being returned as [].

let firstCol (lst: 'a list list) =
  List.map List.head lst

This works as long as there are no empty lists, but I get the following error message when my input includes an empty list:

System.ArgumentException: The input list was empty.

How do I go about this? Thanks in advance.

CodePudding user response:

You can use List.tryHead or write your own function from the ground, or with helpers like List.fold/List.foldBack.

If you do List.tryHead you get an option as a result, either Some element, or None if the list is empty. So you must think what happens in the None case. You cannot return an empty list for a sub-list, because a list must have the same type. But you could for example skip empty lists. Or just keep the Option. As it indicates when a list was empty.

let xs = [[1;2;3];[];[4;5;6];[];[7;8;9]]

printfn "%A" (List.map List.tryHead xs)

returning

[Some 1; None; Some 4; None; Some 7]

You could skip the empty sub-lists

printfn "%A" (List.choose List.tryHead xs)

so you get

[1;4;7]

or do it on your own, with List.foldBack

let firstCol xs = 
    let folder xs acc =
        match List.tryHead xs with
        | Some x -> x :: acc
        | None   -> acc
    List.foldBack folder xs []

Or even more basic

let rec firstCol xs =
    match xs with
    | []           -> []
    | []::xss      -> firstCol xss
    | (x::xs)::xss -> x :: firstCol (xss)

The last version is not tail-recursive, but anyway, you should try and train to understand such a recursive definition. And be able to turn such a function into an tail-recursive on your own.

CodePudding user response:

What you're asking for can't be done with the signature you currently have. Consider this input:

[
    [1; 2]
    []
    [3; 4]
]

It looks like you're asking for the following output:

[
    1
    []
    3
]

However, that isn't a legal list in F# because its elements don't have the same type.

I think your best bet is just to use tryHead instead, as suggested in the other answers.

  • Related