How to idiomatically do this: given a list of stuff, find if an item in it meets a criteria in another list, if it does do one action, if it doesn't do another action. I saw code doing this in C# and I wondered how I would do it in F#.
Here is the code as it would be implemented imperatively in F#:
let find list1 list2 =
for i in list1 do
let mutable found = false
for k in list2 do
if i=k then //if the criteria is equals
found <- true
//do task using k & i
if found =false then
//do other task using i
()
How can this be done more functionally?
Same idea as yours, only slightly clearer. I distilled your "criteria" into an argument function f : 'a -> 'b -> bool.
let find f xs ys =
xs |> List.map (fun x -> (x, List.tryFind (f x) ys))
|> List.iter (function (x, None) -> printfn "%A" x
| (x, Some y) -> printfn "%A,%A" x y)
You'd use it like this:
find (=) [1;2;3;4] [1;3] (* Prints 1,1 -- 2 -- 3,3 -- 4. *)
Short answer
let crossJoin xs ys =
xs
|> Seq.map (fun x -> ys |> Seq.map (fun y -> (x, y)))
|> Seq.concat
let group f xs ys = ys |> crossJoin xs |> Seq.groupBy f
Longer answer
In general, when you need to compare every element in one list with every element in another list, you'll need a Cartesian product, which you can define like this:
let crossJoin xs ys =
xs
|> Seq.map (fun x -> ys |> Seq.map (fun y -> (x, y)))
|> Seq.concat
If, for example, you have:
let integers = [0 .. 10] |> List.toSeq
let strings = [0 .. 5] |> Seq.map string
the Cartesian product would be:
> crossJoin integers strings |> Seq.toList;;
val it : (int * string) list =
[(0, "0"); (0, "1"); (0, "2"); (0, "3"); (0, "4"); (0, "5"); (1, "0");
(1, "1"); (1, "2"); (1, "3"); (1, "4"); (1, "5"); (2, "0"); (2, "1");
(2, "2"); (2, "3"); (2, "4"); (2, "5"); (3, "0"); (3, "1"); (3, "2");
(3, "3"); (3, "4"); (3, "5"); (4, "0"); (4, "1"); (4, "2"); (4, "3");
(4, "4"); (4, "5"); (5, "0"); (5, "1"); (5, "2"); (5, "3"); (5, "4");
(5, "5"); (6, "0"); (6, "1"); (6, "2"); (6, "3"); (6, "4"); (6, "5");
(7, "0"); (7, "1"); (7, "2"); (7, "3"); (7, "4"); (7, "5"); (8, "0");
(8, "1"); (8, "2"); (8, "3"); (8, "4"); (8, "5"); (9, "0"); (9, "1");
(9, "2"); (9, "3"); (9, "4"); (9, "5"); (10, "0"); (10, "1"); (10, "2");
(10, "3"); (10, "4"); (10, "5")]
Given the crossJoin function, you can easily group these tuples according to a comparison function:
let group f xs ys = ys |> crossJoin xs |> Seq.groupBy f
Example:
> group (fun (x, y) -> x.ToString() = y) integers strings |> Seq.toList;;
val it : (bool * seq<int * string>) list =
[(true, seq [(0, "0"); (1, "1"); (2, "2"); (3, "3"); ...]);
(false, seq [(0, "1"); (0, "2"); (0, "3"); (0, "4"); ...])]
Now you have a sequence of tuples, where the first value in the tuple is either true or false.
If, for example, you want to perform a particular action for all matches, you can unpack the groups:
> group (fun (x, y) -> x.ToString() = y) integers strings |> Seq.filter fst |> Seq.head |> snd |> Seq.toList;;
val it : (int * string) list =
[(0, "0"); (1, "1"); (2, "2"); (3, "3"); (4, "4"); (5, "5")]
Then you can simply use Seq.iter or Seq.map to perform the action.
Notice that this uses lazy evaluation all the way through.
Unless your actual actions involve manipulating shared state, you have an embarrassingly parallel solution at hand, so you could easily spread the work over multiple processors.
This was the solution I came up with:
let findF list1 list2 =
list1
|> List.iter (fun i -> match (List.tryFind (fun k -> i=k) list2) with
| Some(k') -> printfn "i,k=%i,%i" i k'
| None -> printfn "i=%i" i )
Edit: Updated with correction from comment.
Related
Suppose I have a collection like [ "a"; "b"; "c" ] and I want to test every element against every other element.
I could generate all pairs like this:
let combinations xs =
Seq.allPairs xs xs
|> Seq.filter (fun (x, y) -> x <> y)
|> Seq.toList
combinations [ "a"; "b"; "c" ]
// [("a", "b"); ("a", "c"); ("b", "a"); ("b", "c"); ("c", "a"); ("c", "b")]
But for my test, I always know that f x y = f y x (since f is symmetric), so I want to trim the number of combinations tested:
let combinations xs =
Seq.allPairs xs xs
|> Seq.filter (fun (x, y) -> x <> y && x < y)
|> Seq.toList
combinations [ "a"; "b"; "c" ]
// [("a", "b"); ("a", "c"); ("b", "c")]
But this:
Doesn't seem like an efficient way to generate the test cases
Requires that x : comparison, which I don't think should be necessary
How should I implement this in F#?
Don't know about efficient - this looks like you need to cache the pairs already generated and filter on their presence in the cache.
The library implementation of Seq.allPairs goes along these lines:
let allPairs source1 source2 =
source1 |> Seq.collect (fun x -> source2 |> Seq.map (fun y -> x, y))
// val allPairs : source1:seq<'a> -> source2:seq<'b> -> seq<'a * 'b>
Then you integrate the caching and filtering into this, constraining both sequences to type seq<'a> and introducing the equality constraint.
let allPairs1 source1 source2 =
let h = System.Collections.Generic.HashSet()
source1 |> Seq.collect (fun x ->
source2 |> Seq.choose (fun y ->
if x = y || h.Contains (x, y) || h.Contains (y, x) then None
else h.Add (x, y) |> ignore; Some (x, y) ) )
// val allPairs1 :
// source1:seq<'a> -> source2:seq<'a> -> seq<'a * 'a> when 'a : equality
Test
allPairs1 [1..3] [2..4] |> Seq.toList
// val it : (int * int) list = [(1, 2); (1, 3); (1, 4); (2, 3); (2, 4); (3, 4)]
Because f is commutative, the simplest way to get all combinations is to project each item into a pair with the remainder of the list.
let rec combinations = function
| [] -> []
| x::xs -> (xs |> List.map (fun y -> (x, y))) # (combinations xs)
We don't need any comparison constraint.
let xs = [1; 2; 3; 4;]
combinations xs // [(1, 2); (1, 3); (1, 4); (2, 3); (2, 4); (3, 4)]
Checking the results with #kaefer's method:
combinations xs = (allPairs1 xs xs |> Seq.toList) // true
Another solution that assumes all elements are distinct (it uses position as identity):
let allSymmetricPairs xs =
seq {
let xs = Seq.toArray xs
for i = 0 to Array.length xs - 2 do
for j = i + 1 to Array.length xs - 1 do
yield xs.[i], xs.[j]
}
We can also pre-allocate the array, which may be faster if you plan to pull the whole sequence:
let allSymmetricPairs xs =
let xs = Seq.toArray xs
let n = Array.length xs
let result = Array.zeroCreate (n * (n - 1) / 2)
let mutable k = 0
for i = 0 to n - 2 do
for j = i + 1 to n - 1 do
result.[k] <- xs.[i], xs.[j]
k <- k + 1
result
I recently started learning how to program in F# and I have an assignment that is giving me some serious headaches.
I have to make a function that takes two arguments, an integer and a five element tuple of integers, and returns true if the sum of any three elements of the tuple is greater than the first argument, else false.
I started designing my code this way
{
let t3 = (1, 2, 3, 4, 5)
let intVal = 1
let check intVal t3 =
for t3
if (*sum of any three elements*) > intVal then true
else false
}
but at this point I am stuck and do not know how to proceed.
Easy way define - sort elements of tuple and compare with sum last three elements (ascending sort) :
let inline isAnyThreeGreaterThan2 limit (x1, x2, x3, x4, x5) =
[x1;x2;x3;x4;x5] |> List.sort |> Seq.skip 2 |> Seq.sum > limit
Example:
isAnyThreeGreaterThan2 15 (1, 2, 5, 5, 5) |> printfn "%A"
isAnyThreeGreaterThan2 14 (1, 2, 5, 5, 5) |> printfn "%A"
isAnyThreeGreaterThan2 15 (1, 2, 5, 5, 6) |> printfn "%A"
isAnyThreeGreaterThan2 15 (1, 2, 3, 4, 5) |> printfn "%A"
isAnyThreeGreaterThan2 12 (1, 2, 3, 4, 5) |> printfn "%A"
isAnyThreeGreaterThan2 11 (1, 2, 3, 4, 5) |> printfn "%A"
Print:
false
true
true
false
false
true
Link:
https://dotnetfiddle.net/7XR1ZA
It could be solved by converting the tuple into an array, getting the possible combinations out of it, summing those combinations and then verify if the any of the sums is greater than your parameter
(1,2,3,4,5)
|> Microsoft.FSharp.Reflection.FSharpValue.GetTupleFields
|> Array.toList
//Implementing this is left as and exercise to the reader
|> combinations 3
//converts the obj list as a int list and then sums the elements
|> List.map (fun x -> x |> List.map unbox<int> |> List.sum)
//Verifies if any sum is greater than intVal
|> List.exists (fun x -> x > intVal)
Something like this ought to do it:
let cross3 l1 l2 l3 =
[
for x in l1 do
for y in l2 do
for z in l3 do
yield x, y, z ]
module Tuple3 =
let distinct (x, y, z) =
let l = [x; y; z]
l |> List.distinct |> List.length = l.Length
let snd (x, y, z) = snd x, snd y, snd z
let inline sum (x, y, z) = x + y + z
let inline isAnyThreeGreaterThan limit (x1, x2, x3, x4, x5) =
let l = [x1; x2; x3; x4; x5] |> List.indexed
let legalCombinations =
cross3 l l l
|> List.filter Tuple3.distinct
|> List.map Tuple3.snd
legalCombinations |> List.exists (fun t3 -> Tuple3.sum t3 > limit)
Since this is an assignment, I'll leave it as an exercise to understand what's going on, but here's a sample FSI session:
> isAnyThreeGreaterThan 15 (1, 2, 5, 5, 5);;
val it : bool = false
> isAnyThreeGreaterThan 14 (1, 2, 5, 5, 5);;
val it : bool = true
> isAnyThreeGreaterThan 15 (1, 2, 5, 5, 6);;
val it : bool = true
> isAnyThreeGreaterThan 15 (1, 2, 3, 4, 5);;
val it : bool = false
> isAnyThreeGreaterThan 12 (1, 2, 3, 4, 5);;
val it : bool = false
> isAnyThreeGreaterThan 11 (1, 2, 3, 4, 5);;
val it : bool = true
I have seq of Tuple of type (x: int, y:bool) and I want to find the min x of the items who has y=isMin in valList if isMin is true.
let valList = seq{ for i =0 to 8 do yield (GetVal (i,not isMin),not isMin) }
let onlyMinType (x: int, y:bool) = if y==isMin then x
let maxVal = valList |> Seq.collect(onlyMinType) |> if isMin then Seq.min else Seq.max
maxVal
I dont know what to do here
Seq.collect(onlyMinType) and function onlyMinType
You could use Seq.minBy in a couple of different ways:
[(1, false); (2, true); (0, true); (-1, false)]
|> Seq.filter snd
|> Seq.minBy fst
|> fst
or
[(1, false); (2, true); (0, true); (-1, false)]
|> Seq.minBy (fun (n, b) -> if b then n else Int32.MaxValue)
|> fst
would work nicely.
I have seq of Tuples of type (x: int, y:bool) and I want to find the min x of the items who has y=isMin in valList ifisMinistrue`.
The idea is to use Seq.filter snd to filter values from seq which second value is true. Then use Seq.sortBy fst to sort seq basing on the first value of each tuple.
> (Seq.sortBy fst << Seq.filter snd) [(1, false); (2, true); (0, true)];;
val it : seq<int * bool> = seq [(0, true); (2, true)]
Aside from writing a loop that yields values, is there a simple/clean functional way of creating a lag (previous value) within a sequence.
Eg. If my sequence is 1 2 3 4 5 6 7 8 9 10 and my lag is 1 return a tuple that is
(Some(1), None), (Some(2), Some(1)), (Some(3), Some(2))...(Some(10), Some(9))
A lag of 2 would give (Some(1), None), (Some(2), None), (Some(3), Some(1))...
It's obviously easy to write this using a loop, but is that the right way?
One way is to use the functions in the Seq module:
let lag n sequence =
sequence
|> Seq.map Some
|> Seq.append (Seq.init n (fun _ -> None))
|> Seq.zip sequence
lag 2 [1..5] |> Seq.toList
> [(1, null); (2, null); (3, Some 1); (4, Some 2); (5, Some 3)]
petebu's answer (once a few mistakes are corrected) is a better answer. But I'll leave this here anyway.
let withLag n (source: seq<_>) =
source
|> Seq.windowed n
|> Seq.append (Seq.init n (fun _ -> [||]))
|> Seq.zip source
|> Seq.map (fun (x, arr) ->
let laggedValue = if arr.Length > 0 then Some arr.[0] else None
(x, laggedValue))
let l = List.init 5 id
l |> withLag 2 |> Seq.toList |> printfn "%A"
> [(0, null); (1, null); (2, Some 0); (3, Some 1); (4, Some 2)]
#light
let a1 = [| (1, 1); (2, 1); (3, 1) |]
let a2 = [| (1, 1); (2, 3); (3, 1) |]
let aa = Array.zip a1 a2
|> Array.filter(fun (x, y) -> x <> y)
I want to write a function to do this: it will return the different tuple from two arrays, but I also want to return the index of the different tuple in the second array and the corresponding tuple in the second array. (My code did not work totally yet!)
For my above example, I want to return: 1 and (2, 3)
Another example:
let a1 = [| (1, 1); (2, 1); (3, 1); (4, 1) |]
let a2 = [| (1, 1); (2, 3); (3, 1); (4, 2) |]
I want to return: 1 and (2, 3); 3 and (4, 2)
If you have any idea, please show me your code.
Besides, I am not used to the new place for F#, the format makes me feel difficult to find a good place to post my questions, therefore, I still post my question here.
let a1 = [| (1, 1); (2, 1); (3, 1) |]
let a2 = [| (1, 1); (2, 3); (3, 1) |]
let diff =
(a1, a2)
||> Array.mapi2(fun i t1 t2 -> (i, t1, t2))
|> Array.choose(fun (i, a, b) -> if a <> b then Some (i, b) else None)
Here's one way to do it:
let diff a b =
let s = Set.ofSeq a
b
|> Seq.mapi (fun i x -> i, x)
|> Seq.filter (fun (_, x) -> not (Set.contains x s))
Example
let a1 = [| (1, 1); (2, 1); (3, 1) |]
let a2 = [| (1, 1); (2, 3); (3, 1) |]
diff a1 a2 //output: seq [(1, (2, 3))]
This works for any collection (list, array, seq<_>, set, etc) and the sequences may be of different lengths. If you know you'll always be using arrays of equal length, you can optimize accordingly (see desco's answer).
let diff (a:(int * int)[]) b =
b
|> Array.mapi (fun i tp -> if a.[i] <> tp then (i, tp) else (-1, tp))
|> Array.filter (fun (x, _) -> x >= 0)
DEMO
> diff a1 a2;;
val it : (int * (int * int)) [] = [|1, (2, 3)); (3, (4, 2))|]