I think your code is actually pretty close to the idiomatic version. The only change I would make is that I would use for in a sequence expression instead of using yield! in combination with Seq.map . I also usually format the code in different ways (but this is only a personal preference), so I would write the following:
let rec pairs l = seq { match l with | h::t -> for e in t do yield h, elem yield! pairs t | _ -> () }
Itβs almost the same as Brian. If you want to get the list as a result, you can simply wrap it all in [ ... ] instead of seq { ... } .
However, in reality this is not so much - under the cover, the compiler still uses the sequence, and it simply adds the conversion to the list. I think it may actually be a good idea to use sequences until you need a list (because sequences are evaluated by lazilly, so you can avoid evaluating unnecessary things).
If you want to make this a little easier, abstracting part of the behavior into a separate (usually useful) function, you can write a function, for example. splits , which returns all list items along with the rest of the list:
let splits list = let rec splitsAux acc list = match list with | x::xs -> splitsAux ((x, xs)::acc) xs | _ -> acc |> List.rev splitsAux [] list
For example, splits [ 1 .. 3 ] will give [(1, [2; 3]); (2, [3]); (3, [])]. When you have this function, implementing the original problem becomes much simpler - you can simply write:
[ for x, xs in splits [ 1 .. 5] do for y in xs do yield x, y ]
As a guide for googling - the problem is called finding all 2-element combinations from a given set.