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.