Reputation: 6252
I've created a function that gets all integers from 1
to n
and then combines with the same sequence to create a sequence of tuples of all combinations. So passing it the integer 2
would give you [(1,1);(1,2);(2,1);(2,2)]
:
let allTuplesUntil x =
let primary = seq { 1 .. x }
let secondary = seq { 1 .. x }
[for x in primary do
for y in secondary do
yield (x,y)]
This implementations works, but it uses an inner and outer for loop, similar to what I would do in c#
.
Could this be achieved in a more idiomatic functional way? Would a more functional way typically be more desirable or is this acceptable in a functional language because of its brevity and clarity?
I'm relatively new to f#
and looking for some feedback.
Upvotes: 3
Views: 267
Reputation: 4488
First, just because there is a for
doesn't mean its not functional. In this example you go over each element and yield a new element that will turn into a new element of a new immutable list. Such feature is also named "List Comprehension" and part of languages like Haskell. Imperative would be to loop over a list and mutate the list.
Second, remember that other functions like map
, fold
, filter
also just loop over each element, like a for
expression. They are just less powerful than a for
loop.
Third, even if it would be "not 100% functional". Who cares? Code should be easily readable and understandable. The intention of two for
loops is easy to understand.
Fourth, the equivalent function of the for
expression is usually the bind
or in this case the Seq.collect
function. You also could write, this code.
[for x in primary do
for y in secondary do
yield (x,y)]
Like this:
primary |> Seq.collect (fun x ->
secondary |> Seq.collect (fun y ->
[x,y]
))
I prefer the for
loops for readability!
Upvotes: 1
Reputation: 861
These loops are part of what's called computation expression, which is quite idiomatic to F#. It's just made to look like familiar loops. I can't see any problem with your code being written in this way. If what you want is to get rid of the loops, you could hide them in functions:
let cartesianProduct xs ys =
xs |> Seq.collect (fun x -> ys |> Seq.map (fun y -> x, y))
cartesianProduct [1;2;3] ['a';'b';'c']
val it : seq<int * char> = seq [(1, 'a'); (1, 'b'); (1, 'c'); (2, 'a'); ...]
Upvotes: 5