Aage
Aage

Reputation: 6252

A more functional way to create tuples from two arrays

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

Answers (2)

David Raab
David Raab

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

DevNewb
DevNewb

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

Related Questions