Question

I been recently playing with F# . I was wondering instead of using a for loop to generate a sequence to element which are multiplied with every other element in the list how can I use a Seq map function or something similar to generate something like below.

So for e.g. I have a list [1..10] I would like to apply a fun which generates a result something like

[(1*1); (1*2);(1*3); (1*4); (1*5)......(2*1);(2*2);(2*3).....(3*1);(3*2)...]

How can i achieve this ?.

Many thanks for all you help.

Was it helpful?

Solution

let list = [1..10]

list |> List.map (fun v1 -> List.map (fun v2 -> (v1*v2)) list) |> List.collect id

The List.collect at the end flattens the list of lists. It works the same with Seq instead of List, if you want a lazy sequence.

Or, using collect as the main iterator, as cfern suggested and obsessivley eliminating anonymous functions:

let flip f x y = f y x

let list = [1..10]

list |> List.collect ((*) >> ((flip List.map) list))

OTHER TIPS

A list comprehension would be the easiest way to do this:

let allpairs L = 
    [for x in L do
        for y in L -> (x*y)]

Or, without using any loops:

let pairs2 L = L |> List.collect (fun x -> L |> List.map (fun y -> (x*y)))


Edit in response to comment: You could add a self-crossing extension method to a list like this:

type Microsoft.FSharp.Collections.List<'a> with
    member L.cross f = 
        [for x in L do
            for y in L -> f x y]

Example:

> [1;2;3].cross (fun x y -> (x,y));;
val it : (int * int) list =
  [(1, 1); (1, 2); (1, 3); (2, 1); (2, 2); (2, 3); (3, 1); (3, 2); (3, 3)]

I wouldn't use an extension method in F# myself, is feels a bit C#'ish. But that's mostly because I don't feel that a fluent syntax is needed in F# because I usually chain my functions together with pipe (|>) operators.

My approach would be to extend the List module with a cross function, not the type itself:

module List =
    let cross f L1 L2 = 
        [for x in L1 do
            for y in L2 -> f x y]

If you do this, you can use the cross method like any other method of List:

> List.cross (fun x y -> (x,y)) [1;2;3] [1;2;3];;
val it : (int * int) list =
  [(1, 1); (1, 2); (1, 3); (2, 1); (2, 2); (2, 3); (3, 1); (3, 2); (3, 3)]
> List.cross (*) [1;2;3] [1;2;3];;
val it : int list = [1; 2; 3; 2; 4; 6; 3; 6; 9]

Or we can implement a general cross product function:

let cross l1 l2 =
  seq { for el1 in l1 do
          for el2 in l2 do
            yield el1, el2 };;

and use this function to get the job done:

cross [1..10] [1..10] |> Seq.map (fun (a,b) -> a*b) |> Seq.toList

To implement the same thing without for loops, you can use the solution using higher-order functions posted by Mau, or you can write the same thing explicitly using recursion:

let cross xs ys =
  let rec crossAux ol2 l1 l2 =
    match l1, l2 with
    // All elements from the second list were processed
    | x::xs, [] -> crossAux ol2 xs ol2             
    // Report first elements and continue looping after
    // removing first element from the second list
    | x::xs, y::ys -> (x, y)::(crossAux ol2 l1 ys) 
    // First list is empty - we're done
    | [], _ -> []
  crossAux ys xs ys

This may be useful if you're learning functional programming and recursion, however, the solution using sequence expressions is far more practically useful.

As a side-note, the first version by Mau can be made a bit nicer, because you can join the call to List.map with a call to List.collect id like this (you can pass the nested processing lambda directly as a parameter to collect). The cross function would look like this (Of course, you can modifiy this to take a parameter to apply to the two numbers instead of creating a tuple):

let cross xs ys =
  xs |> List.collect (fun v1 -> 
    ys |> List.map (fun v2 -> (v1, v2))) 

Incidentally, there is a free chapter from my book avaialable, which discusses how sequence expressions and List.collect functions work. It is worth noting, that for in sequence expressions directly corresponds to List.collect, so you can write the code just by using this higher order function:

let cross xs ys =
  xs |> List.collect (fun v1 -> 
    ys |> List.collect (fun v2 -> [(v1, v2)] )) 

However, see the free chapter for more information :-).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top