In F#, given
game: (int*int) list
I'd like to compute minx, maxx, miny, maxy the min and max values for each tuple dimension.
This code works but seems a bit clumsy:
let minX (game: (int*int) list) = game |> List.map (fun (x,y) -> x) |> Seq.min
let maxX (game: (int*int) list) = game |> List.map (fun (x,y) -> x) |> Seq.max
let minY (game: (int*int) list) = game |> List.map (fun (x,y) -> y) |> Seq.min
let maxY (game: (int*int) list) = game |> List.map (fun (x,y) -> y) |> Seq.max
Any hint for improvement?
let minX game = List.minBy fst game |> fst
let maxX game = List.maxBy fst game |> fst
let minY game = List.minBy snd game |> snd
let maxY game = List.maxBy snd game |> snd
Like John's, but easier to read:
let game = [(1,4);(2,1)]
let minx, miny, maxx, maxy =
let folder (mx,my,Mx,My) (ax,ay) = min mx ax, min my ay, max Mx ax, max My ay
((Int32.MaxValue, Int32.MaxValue, Int32.MinValue, Int32.MinValue), game) ||> List.fold folder
There are a few small changes you can make to improve what you have:
use Seq.map instead of List.map to avoid creating new lists and therefore keep memory usage constant
use the built-in fst/snd functions instead of lambdas
since game is the only argument you can use function composition to make your code more concise
You end up with:
let minX = Seq.map fst >> Seq.min
let maxX = Seq.map fst >> Seq.max
let minY = Seq.map snd >> Seq.min
let maxY = Seq.map snd >> Seq.max
Interestingly, I found this to be quite a bit faster than pad's solution: 0.28 vs 1.75 sec for 10M elements.
The fold version of pad's answer (only 1 list traversal)
let minx,miny,maxx,maxy =game |> List.fold (fun (mx,my,Mx,My) (ax,ay) ->
let nmx,nMx = if ax<mx then ax,Mx else if ax > Mx then mx,ax else mx,Mx
let nmy,nMy = if ay<my then ay,My else if ay > My then my,ay else my,My
nmx,nmy,nMx,nMy) (Int32.MaxValue,Int32.MaxValue,Int32.MinValue,Int32.MinValue)
Related
I am currently learning functional programming and F#, and I want to do a loop control until n-2. For example:
Given a list of doubles, find the pairwise average,
e.g. pairwiseAverage [1.0; 2.0; 3.0; 4.0; 5.0] will give [1.5; 2.5; 3.5; 4.5]
After doing some experimenting and searching, I have a few ways to do it:
Method 1:
let pairwiseAverage (data: List<double>) =
[for j in 0 .. data.Length-2 do
yield (data.[j]+data.[j+1])/2.0]
Method 2:
let pairwiseAverage (data: List<double>) =
let averageWithNone acc next =
match acc with
| (_,None) -> ([],Some(next))
| (result,Some prev) -> ((prev+next)/2.0)::result,Some(next))
let resultTuple = List.fold averageWithNone ([],None) data
match resultTuple with
| (x,_) -> List.rev x
Method 3:
let pairwiseAverage (data: List<double>) =
// Get elements from 1 .. n-1
let after = List.tail data
// Get elements from 0 .. n-2
let before =
data |> List.rev
|> List.tail
|> List.rev
List.map2 (fun x y -> (x+y)/2.0) before after
I just like to know if there are other ways to approach this problem. Thank you.
Using only built-ins:
list |> Seq.windowed 2 |> Seq.map Array.average
Seq.windowed n gives you sliding windows of n elements each.
One simple other way is to use Seq.pairwise
something like
list |> Seq.pairwise |> Seq.map (fun (a,b) -> (a+b)/2.0)
The approaches suggested above are appropriate for short windows, like the one in the question. For windows with a length greater than 2 one cannot use pairwise. The answer by hlo generalizes to wider windows and is a clean and fast approach if window length is not too large. For very wide windows the code below runs faster, as it only adds one number and subtracts another one from the value obtained for the previous window. Notice that Seq.map2 (and Seq.map) automatically deal with sequences of different lengths.
let movingAverage (n: int) (xs: float List) =
let init = xs |> (Seq.take n) |> Seq.sum
let additions = Seq.map2 (fun x y -> x - y) (Seq.skip n xs) xs
Seq.fold (fun m x -> ((List.head m) + x)::m) [init] additions
|> List.rev
|> List.map (fun (x: float) -> x/(float n))
xs = [1.0..1000000.0]
movingAverage 1000 xs
// Real: 00:00:00.265, CPU: 00:00:00.265, GC gen0: 10, gen1: 10, gen2: 0
For comparison, the function above performs the calculation above about 60 times faster than the windowed equivalent:
let windowedAverage (n: int) (xs: float List) =
xs
|> Seq.windowed n
|> Seq.map Array.average
|> Seq.toList
windowedAverage 1000 xs
// Real: 00:00:15.634, CPU: 00:00:15.500, GC gen0: 74, gen1: 74, gen2: 71
I tried to eliminate List.rev using foldBack but did not succeed.
A point-free approach:
let pairwiseAverage = List.pairwise >> List.map ((<||) (+) >> (*) 0.5)
Online Demo
Usually not a better way, but another way regardless... ;-]
I'm making a function that takes a Map and generates all the possible swaps of values.
let mutations setup =
let setupList = setup |> Map.toList
[for x in 0..(setupList.Length-1) do
for y in x..(setupList.Length-1) do
yield (x, y)]
|> Seq.map (fun (x,y) ->
setupList
|> swapSndAt x y
|> Map.ofList)
|> Seq.distinct
Here is the code for swapSndAt:
let swapSndAt i1 i2 xs =
let f1,s1 = xs |> List.item i1
let f2,s2 = xs |> List.item i2
xs |> List.mapi (fun i x ->
if i = i1 then f1,s2
else if i = i2 then f2,s1
else x)
When I run this, I get an System.ArgumentException: The index was outside the range of elements in the list. at the point where I am calling List.item in swapSndAt.
How can that be, since the List comprehension makes sure to watch the bounds?
let highs = [| 2; 4; 6 |]
let lows = [| 1; 5; 10 |]
I want to get 2 arrays from the above: if the element in highs is smaller than the corresponding element in lows, then swap them. So, I can get the final 2 arrays:
let trueHighs = [| 2; 5; 10 |]
let trueLows = [| 1; 4; 6 |]
How do I do this?
Similar with JaredPar's answer but simpler:
let trueHighs, trueLows =
Array.zip highs lows
|> Array.map (fun (x, y) -> if x >= y then (x, y) else (y, x))
|> Array.unzip
Another more concise version:
let trueHighs, trueLows =
(highs, lows)
||> Array.map2 (fun x y -> if x >= y then (x, y) else (y, x))
|> Array.unzip
Here is the code you should use:
let n = highs.Length
let trueHighs = Array.init n (fun i -> max highs.[i] lows.[i])
let trueLows = Array.init n (fun i -> min highs.[i] lows.[i])
If performance is uber-critical, you're probably better off with an imperative approach.
let n = highs.Length
let trueHighs = Array.zeroCreate n
let trueLows = Array.zeroCreate n
for i = 0 to n-1 do
let hi = highs.[i]
let lo = lows.[i]
if hi > lo then
trueHighs.[i] <- hi
trueLows.[i] <- lo
else
trueHighs.[i] <- lo
trueLows.[i] <- hi
Try the following
let trueHighs, trueLows =
let zipped =
highs
|> Seq.ofArray
|> Seq.zip (lows |> Seq.ofArray)
|> Seq.map (fun (x, y) -> min x y, max x y)
let lows = zipped |> Seq.map fst |> Array.ofSeq
let highs = zipped |> Seq.map snd |> Array.ofSeq
highs, lows
Is piping parameter into line is working only for functions that accept one parameter?
If we look at the example at Chris Smiths' page,
// Using the Pipe-Forward operator (|>)
let photosInMB_pipeforward =
#"C:\Users\chrsmith\Pictures\"
|> filesUnderFolder
|> Seq.map fileInfo
|> Seq.map fileSize
|> Seq.fold (+) 0L
|> bytesToMB
where his filesUnderFolder function was expecting only rootFolder parameter,
what if the function was expecting two parameters, i.e.
let filesUnderFolder size rootFolder
Then this does not work:
// Using the Pipe-Forward operator (|>)
let size= 4
let photosInMB_pipeforward =
#"C:\Users\chrsmith\Pictures\"
|> filesUnderFolder size
|> Seq.map fileInfo
|> Seq.map fileSize
|> Seq.fold (+) 0L
|> bytesToMB
Since I can define
let inline (>>) f g x y = g(f x y)
I think I should be able to use pipeline operator with functions having multiple input parameters, right? What am I missing?
When mixing pipeline operators and curried arguments be aware of the order you pass arguments with.
let size = 4
let photosInMB_pipeforward =
size, #"C:\Users\chrsmith\Pictures\"
||> filesUnderFolder
|> Seq.map fileInfo
|> Seq.map fileSize
|> Seq.fold (+) 0L
|> bytesToMB
Think about it as if the compiler is putting parentheses around the function and its parameters like this.
#"C:\Users\chrsmith\Pictures\" |> filesUnderFolder size
becomes
#"C:\Users\chrsmith\Pictures\" |> (filesUnderFolder size)
or
(filesUnderFolder size) #"C:\Users\chrsmith\Pictures\"
Out of order example
let print2 x y = printfn "%A - %A" x y;;
(1, 2) ||> print2;;
1 - 2
1 |> print2 2;;
2 - 1
With three arguments
let print3 x y z = printfn "%A - %A - %A" x y z;;
(1, 2, 3) |||> print3;;
1 - 2 - 3
(2, 3) ||> print3 1;;
1 - 2 - 3
3 |> print3 1 2;;
1 - 2 - 3
Definitions
let inline (|>) x f = f x
let inline (||>) (x1,x2) f = f x1 x2
let inline (|||>) (x1,x2,x3) f = f x1 x2 x3
The example you suggested should work fine, a la
let add x y = x + y
41
|> add 1
|> printfn "%d"
If filesUnderFolder takes two curried args, and you partially apply it to one arg, you can use it in the pipeline for the other.
(Note also the lesser known pipeline operator ||>
(41,1)
||> add
|> printfn "%d"
which takes a 2-tuple and feed them sequentially into what follows.)
It may be bad style (?), but you can add additional parameters to the pipeline 'from the right side'
let h x y z = x + y - z
let sub x y = x - y
let sqr x = x * x
3 |> h <| 2 <| 7
|> sub <| 23
|> sqr
// is the same as
sqr (sub (h 3 2 7) 23)
I'm a bit stuck on the last step of getting the solution to problem 2 on Project Euler. This is the source I've gotten so far.
#light
module pe2 (* Project Euler Problem 2 solution *)
open System
let Phi = 1.6180339887;;
let invPhi = 1.0/Phi;;
let rootOfFive = 2.236067977;;
let maxFib = 4000000.0;
let Fib n =
System.Math.Round((Phi**n - invPhi**n)/rootOfFive);;
let FibIndices = Seq.unfold(fun i -> Some(i, i+3.0)) 3.0;;
let FibNos = FibIndices |> Seq.map(fun index -> Fib(index));;
let setAllowedFibNos = FibNos |> Seq.filter(fun fn -> (fn <= maxFib));;
// let answer = setAllowedFibNos |> Seq.fold (+) 0.0;
When I uncomment the last line, the process never seems to finish. So I was hoping that someone could give me a gentle nudge in the right direction. I did look at setAllowedFibNos and it looks right but it's also an infinite sequence so I only see the first three terms.
Also, could someone point me to the right way to chain the various sequences together? I tried something like this:
let answer = Seq.unfold(fun i-> Some(i, i + 3.0)) 3.0
|> Seq.map (fun index -> Fib(index))
|> Seq.filter(fun fn -> (fn <= maxFib))
|> Seq.fold (+) 0.0;;
But that didn't work. As you can probably guess I'm just learning F# so please go gentle and if this sort of question has been asked and answered before, please post a link to the answer and I'll withdraw this one.
let rec Fib(n) =
if (n < 2) then
1
else
Fib(n-2) + Fib(n-1)
Seq.initInfinite Fib
|> Seq.takeWhile (fun a -> a <= 4000000)
|> Seq.filter (fun a -> (a % 2) = 0)
|> Seq.fold (+) 0
'setAllowedFibNos' is indeed an infinite seq computation; 'fold' needs the whole sequence, so the 'filter' will run forever looking for another number <= maxFib.
Take a look at takeWhile:
http://research.microsoft.com/en-us/um/cambridge/projects/fsharp/manual/FSharp.Core/Microsoft.FSharp.Collections.Seq.html
I think it is what you want instead of filter.
Also note that you can use 'sqrt 5.0'.
I'm still trying to get used to the Seq approach. But, here is my solution without it.
#light
let rec fib n =
match n with
|0|1 -> n
|_ -> fib(n-1) + fib(n-2)
let maxFib = 4000000
let phi = (1.0 + sqrt(5.0)) / 2.0
let upperBound = 1 + int( log10((float(maxFib) - 0.5) * sqrt(5.0)) / log10(phi))
[1..upperBound] |> List.filter (fun x-> x%3=0) |> List.map fib |> List.filter (fun x -> x%2 = 0) |> List.filter (fun x -> x List.sum |> printfn "%d"
My solution is:
Seq.unfold (fun state ->
if (fst state + snd state > 4000000) then None
else Some(fst state + snd state, (snd state, fst state + snd state))) (0,1)
|> Seq.filter (fun x -> x % 2 = 0)
|> Seq.sum;;