I have an F# function and I want to try varying some of the parameters and testing all such combinations. Is this the right approach? (The parentheses get a bit dense...):
let MyFunc a b c x y z =
...
q
let UploadResult a b c x y z q =
...
()
let a = 5.0
let b = 0
let c = System.DateTime.Today
let xList = [-1.0; 0.0; 1.0]
let yList = [2; 4; 6; 8; 10]
let zList = [0.1; 0.001]
xList |> List.iter (fun x ->
(yList |> List.iter (fun y ->
(zList |> List.iter (fun z ->
MyFunc a b c x y z
|> UploadResult a b c x y z ))) ))
|> ignore
So I want to upload 3x5x2=30 results, and write it nicely. Thanks for any advice.
In fact, your primary goal is creating a Cross-Product (or Cartesian Product) of several lists, and there are several options considered "good practice" among F# developers:
1. (removed for
-comprehension as other answer already suggested this)
2. Use Computation Expressions (in the rest of Functional Programming world, it is often called Monad):
type Product () =
member this.Bind (l,f) = List.collect f l
member this.Return n = [n]
let ret02 = Product() {
let! x = xList
let! y = yList
let! z = zList
MyFunc a b c x y z
|> UploadResult a b c x y z
}
3. If you only worry about parentheses, use the high precedence, right associative backward pipe, (more info)
let inline (^<|) f a = f a
Then, your code would need a minimal modification (albeit still not very clean):
let ret03 =
xList |> List.iter ^<| fun x ->
yList |> List.iter ^<| fun y ->
zList |> List.iter ^<| fun z ->
MyFunc a b c x y z
|> UploadResult a b c x y z
|> ignore
For purely imperative operations like uploading results, I would not really worry too much. Using List.iter
as in your code sample does the trick. I would probably prefer for
loop, just because then it is obvious that this piece of code has side-effect that matters:
for x in xList do
for y in yList do
for z in zList do
MyFunc a b c x y z |> UploadResult a b c x y z
If you wanted to do something clever, you could write a function that produces all combination of parameters from two lists:
let product xl yl =
seq { for x in xl do
for y in yl do
yield x, y }
The nice thing is that you can also use it multiple times: product xList (product yList zList)
. This gives you back a list of tuples that you can again iterate over:
for (x,y), z in product (product xList yList) zList do
MyFunc a b c x y z
|> UploadResult a b c x y z
The not so nice thing about this is that you'll end up with nested tuples - which is why I'd probably go with just a plain loop. (Or if you always have exactly 3 lists, then the solution in the other answer, which is similar to product
, but optimized for 3 lists)
let MyFunc a b c x y z =
42
let UploadResult a b c x y z q =
printfn "%A %A %A %A %A %A %A" a b c x y z q
let a = 5.0
let b = 0
let c = System.DateTime.Today
let xList = [-1.0; 0.0; 1.0]
let yList = [2; 4; 6; 8; 10]
let zList = [0.1; 0.001]
let perm xs ys zs =
[for x in xs do
for y in ys do
for z in zs do
yield x,y,z]
let f (x,y,z) = MyFunc a b c x y z |> UploadResult a b c x y z
perm xList yList zList |> List.iter f