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
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 want to find tuple in a set by first two values and return third value of the tuple (or None if found nothing). I woluld like something like that:
type Point = (int * int * int)
type Path = Set<Point>
let f (x:int) (y:int) (p:Path) : int Option =
if Set.exists ((=) (x, y, _z)) p
then Some _z
else None
let p:Path = Set.ofList [ (0, 1, 100); (1, 1, 500); (1, 2, 50); ]
f 1 2 p
But this not works because, apparently, pattern matching does not allowed in expressions. What is the right approach? Thanks.
You can convert the set to list and use List.tryFind
let f (x:int) (y:int) (p:Path) : int Option =
Set.toList p
|> List.tryFind (fun (px, py, _) -> x = px && y = py)
|> Option.map (fun (_, _, pz) -> pz)
Iterating on hvester's answer:
let f (x:int) (y:int) (p:Path) : int Option =
p |> Seq.tryPick (function
| x', y', z' when x = x' && y = y' -> Some z'
| _ -> None)
tryPick essentially does a find and map in one step.
This is a pretty neat solution with fold
let f x y p = Set.fold (function |None -> (fun (x_,y_,z) -> if x=x_ && y=y_ then Some z else None) |f ->fun _ -> f) None p
Is this what you want to do?
let f (x:int) (y:int) (p:Path) : int Option =
match p |> Set.filter (fun (x', y', _) -> x' = x && y' = y) |> Set.toList with
| [(_, _, z)] -> Some z
| [] -> None
| _ -> failwith "More than one point was found!"
Example:
> let p:Path = Set.ofList [ (0, 1, 100); (1, 1, 500); (1, 2, 50); ];;
val p : Path = set [(0, 1, 100); (1, 1, 500); (1, 2, 50)]
> f 1 2 p;;
val it : Option<int> = Some 50
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)]
Let's say I have a sequence of sequences, e.g.
{1, 2, 3}, {1, 2, 3}, {1, 2, 3}
What is the best way to pivot or zip this sequence so I instead have,
{1, 1, 1}, {2, 2, 2}, {3, 3, 3}
Is there a comprehensible way of doing so without resorting to manipulation of the underlying IEnumerator<_> type?
To clarify, these are seq<seq<int>> objects. Each sequences (both internal and external) can have any number of items.
If you're going for a solution which is semantically Seq, you're going to have to stay lazy all the time.
let zip seq = seq
|> Seq.collect(fun s -> s |> Seq.mapi(fun i e -> (i, e))) //wrap with index
|> Seq.groupBy(fst) //group by index
|> Seq.map(fun (i, s) -> s |> Seq.map snd) //unwrap
Test:
let seq = Enumerable.Repeat((seq [1; 2; 3]), 3) //don't want to while(true) yield. bleh.
printfn "%A" (zip seq)
Output:
seq [seq [1; 1; 1]; seq [2; 2; 2]; seq [3; 3; 3]]
This seems very inelegant but it gets the right answer:
(seq [(1, 2, 3); (1, 2, 3); (1, 2, 3);])
|> Seq.fold (fun (sa,sb,sc) (a,b,c) ->a::sa,b::sb,c::sc) ([],[],[])
|> fun (a,b,c) -> a::b::c::[]
It looks like matrix transposition.
let data =
seq [
seq [1; 2; 3]
seq [1; 2; 3]
seq [1; 2; 3]
]
let rec transpose = function
| (_::_)::_ as M -> List.map List.head M :: transpose (List.map List.tail M)
| _ -> []
// I don't claim it is very elegant, but no doubt it is readable
let result =
data
|> List.ofSeq
|> List.map List.ofSeq
|> transpose
|> Seq.ofList
|> Seq.map Seq.ofList
Alternatively, you may adopt the same method for seq, thanks to this answer for an elegant Active pattern:
let (|SeqEmpty|SeqCons|) (xs: 'a seq) =
if Seq.isEmpty xs then SeqEmpty
else SeqCons(Seq.head xs, Seq.skip 1 xs)
let rec transposeSeq = function
| SeqCons(SeqCons(_,_),_) as M ->
Seq.append
(Seq.singleton (Seq.map Seq.head M))
(transposeSeq (Seq.map (Seq.skip 1) M))
| _ -> Seq.empty
let resultSeq = data |> transposeSeq
See also this answer for technical details and two references: to PowerPack's Microsoft.FSharp.Math.Matrix and yet another method involving mutable data.
This is the same answer as #Asti, just cleaned up a little:
[[1;2;3]; [1;2;3]; [1;2;3]]
|> Seq.collect Seq.indexed
|> Seq.groupBy fst
|> Seq.map (snd >> Seq.map snd);;