Suppose I have a seq and I want to return the largest if there are any elements or None otherwise. F# does not appear to have this built-in.
Here is my attempt:
let tryMax xs =
if Seq.isEmpty xs
then
None
else
Seq.max xs |> Some
let tryMin xs =
if Seq.isEmpty xs
then
None
else
Seq.min xs |> Some
Are there any problems with this approach?
Is there a built-in solution for this?
I think your approach is generally good. There was an answer that is now deleted that suggested to use try/with to prevent double-evaluation of the first item by catching the error for empty sequences, but that too can be expensive.
If you want to prevent double evaluation, you can use Seq.cache, or not use Seq at all (use List or Array instead). Or use fold, which iterates only once:
module Seq =
let tryMin sq =
sq
|> Seq.fold(fun x y ->
match x with None -> Some y | Some x -> Some(min x y)) None
Usage:
> Seq.tryMin Seq.empty<int>;;
val it : int option = None
> Seq.tryMin (Seq.singleton 2L);;
val it : int64 option = Some 2L
> Seq.tryMin (seq { 2; 3});;
val it : int option = Some 2
> Seq.tryMin (seq { 2; -3});;
val it : int option = Some -3
A potentially faster method (I didn't time it), is to prevent the creation of option on each min- or max-calculation result, and at the same time preventing multiple iterations of the first item.
This should have much less GC pressure too ;).
module Seq =
let tryMin (sq: seq<_>) =
use e = sq.GetEnumerator()
// this returns false if there is no first item
if e.MoveNext() then
let mutable result = e.Current
while e.MoveNext() do
result <- min e.Current result
Some result
else
None
Usage:
> Seq.tryMin Seq.empty<int>;;
val it : int option = None
> Seq.tryMin (Seq.singleton 2L);;
val it : int64 option = Some 2L
> Seq.tryMin (seq { 2; 3});;
val it : int option = Some 2
> Seq.tryMin (seq { 2; -3});;
val it : int option = Some -3
FWIW, here's tryMinBy as well:
let tryMinBy projection (items : seq<_>) =
use e = items.GetEnumerator()
if e.MoveNext() then
let mutable minItem = e.Current
let mutable minValue = projection minItem
while e.MoveNext() do
let value = projection e.Current
if value < minValue then
minItem <- e.Current
minValue <- value
Some minItem
else
None
The full suite:
module Seq
let tryMinBy projection (items : seq<_>) =
use e = items.GetEnumerator ()
if e.MoveNext ()
then
let mutable minItem = e.Current
let mutable minValue = projection minItem
while e.MoveNext () do
let value = projection e.Current
if value < minValue
then
minItem <- e.Current
minValue <- value
Some minItem
else
None
let tryMaxBy projection (items : seq<_>) =
use e = items.GetEnumerator ()
if e.MoveNext ()
then
let mutable maxItem = e.Current
let mutable maxValue = projection maxItem
while e.MoveNext () do
let value = projection e.Current
if value > maxValue
then
maxItem <- e.Current
maxValue <- value
Some maxItem
else
None
let tryMin (items : seq<_>) =
use e = items.GetEnumerator ()
if e.MoveNext ()
then
let mutable minItem = e.Current
while e.MoveNext () do
if e.Current < minItem
then
minItem <- e.Current
Some minItem
else
None
let tryMax (items : seq<_>) =
use e = items.GetEnumerator ()
if e.MoveNext ()
then
let mutable maxItem = e.Current
while e.MoveNext () do
if e.Current > maxItem
then
maxItem <- e.Current
Some maxItem
else
None
Related
I'm trying to write a function that takes a list for example
let list = [5;23;29;1]
let x = max list // This will return 2 because 29 will be the max value and it's "indexed" at position 2
I'm not sure about how to go about writing the max function
Since my list will only contain four elements I currently have some code like this
let list = (1, newMap1 |> getScore) :: (2, newMap2 |> getScore) :: (3, newMap3 |> getScore) :: (4, newMap4 |> getScore) :: []
I consider this a terrible approach but I'm still stuck on how to return (x, _) after I find the max of (_, y). I'm very confident with imperative approaches but I'm stumped on how to do this functionally
There is a couple of ways to do this. At the low-level, you can write a recursive function to iterate and pattern match over a list. This is good exercise if you are learning F#.
Similarly, you can implement this using the fold function. Here, the idea is that we keep some state, consisting of the "best value" and the index of the best value. At each step, we either keep the original information, or update it:
let _, maxValue, maxIndex =
list |> List.fold (fun (index, maxSoFar, maxIndex) v ->
if v > maxSoFar then (index+1, v, index+1)
else (index+1, maxSoFar, maxIndex)) (-1, System.Int32.MinValue, -1)
Finally, the shortest option I can think of is to use mapi and maxBy functions:
list
|> Seq.mapi (fun i v -> i, v)
|> Seq.maxBy snd
Here's an answer only using pattern matching and recursion.
let list = [5;23;29;1]
let rec findIndexOfMaxValue (maxValue:int) indexOfMaxValue currentIndex aList =
match aList with
| [] -> indexOfMaxValue
| head::tail -> match head with
| head when head > maxValue -> findIndexOfMaxValue head currentIndex (currentIndex + 1) tail
| _ -> findIndexOfMaxValue maxValue indexOfMaxValue (currentIndex + 1) tail
[<EntryPoint>]
let main argv =
let indexOfMaxValue = findIndexOfMaxValue 0 0 0 list
printfn "The index of the maximum value is %A." indexOfMaxValue
//The index of the maximum value is 2.
0
Out of interest, I made a timing script comparing my algorithm with the other ones provided:
open System.Diagnostics
let n = 5000
let random = System.Random 543252
let randomlists =
[for i in [1..n] -> [ for i in [1..n] -> random.Next (0, n*n)]]
let stopWatch =
let sw = Stopwatch ()
sw.Start ()
sw
let timeIt (name : string) (a : int list -> 'T) : unit =
let t = stopWatch.ElapsedMilliseconds
let v = a (randomlists.[0])
for i = 1 to (n - 1) do
a randomlists.[i] |> ignore
let d = stopWatch.ElapsedMilliseconds - t
printfn "%s, elapsed %d ms, result %A" name d v
let rec findIndexOfMaxValue (maxValue:int) indexOfMaxValue currentIndex aList =
match aList with
| [] -> indexOfMaxValue
| head::tail -> match head with
| head when head > maxValue -> findIndexOfMaxValue head currentIndex (currentIndex + 1) tail
| _ -> findIndexOfMaxValue maxValue indexOfMaxValue (currentIndex + 1) tail
let findIndexOfMaxValueFoldAlg list =
let _, maxValue, maxIndex =
list |> List.fold (fun (index, maxSoFar, maxIndex) v ->
if v > maxSoFar then (index+1, v, index+1)
else (index+1, maxSoFar, maxIndex)) (-1, System.Int32.MinValue, -1)
maxIndex
let findIndexOfMaxValueSimpleSeq list = list
|> Seq.mapi (fun i v -> i, v)
|> Seq.maxBy snd
|> fst
let findIndexOfMaxValueSimpleList list =
list
|> List.mapi (fun i x -> i, x)
|> List.maxBy snd
|> fst
[<EntryPoint>]
let main argv =
timeIt "recursiveOnly" (findIndexOfMaxValue 0 0 0)
timeIt "simpleSeq" findIndexOfMaxValueSimpleSeq
timeIt "simpleList" findIndexOfMaxValueSimpleList
0
The results I get are:
recursiveOnly, elapsed 356ms, result 3562
foldAlgorithm, elapsed 1602ms, result 3562
simpleSeq, elapsed 4504ms, result 3562
simpleList, elapsed 4395ms, result 3562
I have these functions in my helper library:
module List =
let maxIndexBy projection list =
list
|> List.mapi (fun i x -> i, projection x)
|> List.maxBy snd
|> fst
let maxIndex list = maxIndexBy id list
Returns the index of the max element, optionally using a given projection function. You can write the same functions for the Seq and Array modules easily by replacing the "List" part and renaming the arguments.
What would the time complexity be of these two algorithms?
let rec fol f a = function
| [] -> a
| x::xs -> fol f (f a x) xs;;
let mergelist xs = List.fol (#) [] xs
and
let rec folB f xs a =
match xs with
| [] -> a
| y::ys -> f y (folB f ys a);;
let mergelist2 xs = List.folB (#) xs []
and how would i be able to test it my self?
Should return something like
mergelist [[1;2];[];[3];[4;5;6]];;
val it : int list = [1; 2; 3; 4; 5; 6]
Here is a quick&dirty snippet of how you can compare the two operations with n lists of length 3 each:
let rec fol f a = function
| [] -> a
| x::xs -> fol f (f a x) xs;;
let rec folB f xs a =
match xs with
| [] -> a
| y::ys -> f y (folB f ys a);;
let compareThemFor n =
let testList = List.replicate n [1;2;3]
let count = ref 0
let myCons x xs =
incr count
x :: xs
let myApp ys =
List.foldBack myCons ys
let mergelist = fol myApp []
mergelist testList |> ignore
let countA = !count
count := 0
let mergelist2 xs = folB myApp xs []
mergelist2 testList |> ignore
let countB = !count
(countA, countB)
and this is what you will get:
> compareThemFor 2;;
val it : int * int = (3, 6)
> compareThemFor 3;;
val it : int * int = (9, 9)
> compareThemFor 4;;
val it : int * int = (18, 12)
> compareThemFor 5;;
val it : int * int = (30, 15)
> compareThemFor 6;;
val it : int * int = (45, 18)
as you can see the second is far better and I hope the comments above helps you understand why.
Just in case here is the n=3 version for mergelist:
mergelist [[1;2;3];[3;4;5];[6;7;8]]
{ second case in `fol` with `x=[1;2;3]` and `xs=[[3;4;5];[6;7;8]]` }
= fol (#) ([] # [1;2;3]) [[3;4;5];[6;7;8]] // one # of 0 elements = 0 operations
{ second case in `fol` with `x=[3;4;5]` and `xs=[[6;7;8]]` }
= fol (#) ([1;2;3] # [3;4;5]) [[6;7;8]] // one # of 3 elements = 3 operations
{ second case in `fol` with `x=[6;7;8]` and `xs=[]` }
= fol (#) ([1;2;3;3;4;5] # [6;7;8]) [] // one # of 6 elements = 6 operations
{ first case }
= [1;2;3;3;4;5;6;7;8] // 0+3+(3+3)=9 Operations Total
please note that you prepend [1,2,3] multiple times ...
I am seeking help, mainly because I am very new to F# environment. I need to use F# stream to generate an infinite stream of Armstrong Numbers. Can any one help with this one. I have done some mambo jumbo but I have no clue where I'm going.
type 'a stream = | Cons of 'a * (unit -> 'a stream)
let rec take n (Cons(x, xsf)) =
if n = 0 then []
else x :: take (n-1) (xsf());;
//to test if two integers are equal
let test x y =
match (x,y) with
| (x,y) when x < y -> false
| (x,y) when x > y -> false
| _ -> true
//to check for armstrong number
let check n =
let mutable m = n
let mutable r = 0
let mutable s = 0
while m <> 0 do
r <- m%10
s <- s+r*r*r
m <- m/10
if (test n s) then true else false
let rec armstrong n =
Cons (n, fun () -> if check (n+1) then armstrong (n+1) else armstrong (n+2))
let pos = armstrong 0
take 5 pos
To be honest your code seems a bit like a mess.
The most basic version I could think of is this:
let isArmstrong (a,b,c) =
a*a*a + b*b*b + c*c*c = (a*100+b*10+c)
let armstrongs =
seq {
for a in [0..9] do
for b in [0..9] do
for c in [0..9] do
if isArmstrong (a,b,c) then yield (a*100+b*10+c)
}
of course assuming a armstrong number is a 3-digit number where the sum of the cubes of the digits is the number itself
this will yield you:
> Seq.toList armstrongs;;
val it : int list = [0; 1; 153; 370; 371; 407]
but it should be easy to add a wider range or remove the one-digit numbers (think about it).
general case
the problem seems so interesting that I choose to implement the general case (see here) too:
let numbers =
let rec create n =
if n = 0 then [(0,[])] else
[
for x in [0..9] do
for (_,xs) in create (n-1) do
yield (n, x::xs)
]
Seq.initInfinite create |> Seq.concat
let toNumber (ds : int list) =
ds |> List.fold (fun s d -> s*10I + bigint d) 0I
let armstrong (m : int, ds : int list) =
ds |> List.map (fun d -> bigint d ** m) |> List.sum
let leadingZero =
function
| 0::_ -> true
| _ -> false
let isArmstrong (m : int, ds : int list) =
if leadingZero ds then false else
let left = armstrong (m, ds)
let right = toNumber ds
left = right
let armstrongs =
numbers
|> Seq.filter isArmstrong
|> Seq.map (snd >> toNumber)
but the numbers get really sparse quickly and using this will soon get you out-of-memory but the
first 20 are:
> Seq.take 20 armstrongs |> Seq.map string |> Seq.toList;;
val it : string list =
["0"; "1"; "2"; "3"; "4"; "5"; "6"; "7"; "8"; "9"; "153"; "370"; "371";
"407"; "1634"; "8208"; "9474"; "54748"; "92727"; "93084"]
remark/disclaimer
this is the most basic version - you can get big speed/performance if you just enumerate all numbers and use basic math to get and exponentiate the digits ;) ... sure you can figure it out
Ok, this looks like it should be easy, but I'm just not getting it. If I have a sequence of numbers, how do I generate a new sequence made up of the running totals? eg for a sequence [1;2;3;4], I want to map it to [1;3;6;10]. In a suitably functional way.
Use List.scan:
let runningTotal = List.scan (+) 0 >> List.tail
[1; 2; 3; 4]
|> runningTotal
|> printfn "%A"
Seq.scan-based implementation:
let runningTotal seq' = (Seq.head seq', Seq.skip 1 seq') ||> Seq.scan (+)
{ 1..4 }
|> runningTotal
|> printfn "%A"
Another variation using Seq.scan (Seq.skip 1 gets rid of the leading zero):
> {1..4} |> Seq.scan (+) 0 |> Seq.skip 1;;
val it : seq<int> = seq [1; 3; 6; 10]
> Seq.scan (fun acc n -> acc + n) 0 [1;2;3;4];;
val it : seq<int> = seq [0; 1; 3; 6; ...]
With lists:
> [1;2;3;4] |> List.scan (fun acc n -> acc + n) 0 |> List.tail;;
val it : int list = [1; 3; 6; 10]
Edit: Another way with sequences:
let sum s = seq {
let x = ref 0
for i in s do
x := !x + i
yield !x
}
Yes, there's a mutable variable, but I find it more readable (if you want to get rid of the leading 0).
Figured it was worthwhile to share how to do this with Record Types in case that's also what you came here looking for.
Below is a fictitious example demonstrating the concept using runner laps around a track.
type Split = double
type Lap = { Num : int; Split : Split }
type RunnerLap = { Lap : Lap; TotalTime : double }
let lap1 = { Num = 1; Split = 1.23 }
let lap2 = { Num = 2; Split = 1.13 }
let lap3 = { Num = 3; Split = 1.03 }
let laps = [lap1;lap2;lap3]
let runnerLapsAccumulator =
Seq.scan
(fun rl l -> { rl with Lap = l; TotalTime = rl.TotalTime + l.Split }) // acumulator
{ Lap = { Num = 0; Split = 0.0 }; TotalTime = 0.0 } // initial state
let runnerLaps = laps |> runnerLapsAccumulator
printfn "%A" runnerLaps
Not sure this is the best way but it should do the trick
let input = [1; 2; 3; 4]
let runningTotal =
(input, 0)
|> Seq.unfold (fun (list, total) ->
match list with
| [] ->
None
| h::t ->
let total = total + h
total, (t, total) |> Some)
|> List.ofSeq
a simple question I cannot find an answer to: how to add an element to a sequence? Eg I have a seq and a newElem XElement I'd like to append to it.
Thanks
Seq.append:
> let x = { 1 .. 5 };;
val x : seq<int>
> let y = Seq.append x [9];; // [9] is a single-element list literal
val y : seq<int>
> y |> Seq.toList;;
val it : int list = [1; 2; 3; 4; 5; 9]
You can also use
let newSeq = Seq.append oldSeq (Seq.singleton newElem)
Which is a slight modification of the first answer but appends sequences instead of a list to a sequence.
given the following code
let startSeq = seq {1..100}
let AppendTest = Seq.append startSeq [101] |> List.ofSeq
let AppendTest2 = Seq.append startSeq (Seq.singleton 101) |> List.ofSeq
let AppendTest3 = seq { yield! startSeq; yield 101 } |> List.ofSeq
looped 10000 executions the run times are
Elapsed 00:00:00.0001399
Elapsed 00:00:00.0000942
Elapsed 00:00:00.0000821
Take from that what you will.
There's also an imperative solution...
> let x = seq {1..5}
> let y = seq { yield! x; yield 9 } // Flatten the list,then append your element
> Seq.to_list y;;
val it : int list = [1; 2; 3; 4; 5; 9]
This may be better if the underlying problem is an imperative one, and it is most natural to use a yield statement in a loop.
let mySeq = seq { for i in 1..10 do yield i };;