Reputation: 10516
I have a function where I want to transform a list of floats into another one, where for each element I want to have x percent of element i spill over into element i + 1
example:
let p3 = [0.1; 0.2; 0.4; 0.2; 0.1]
then p3_s should be:
[0.05; 0.15; 0.3; 0.3; 0.2]
To do this I took half of each element and added it to the next element.
Now I came up with this which works but only for list of size 5:
// create list
let p3 = [0.1; 0.2; 0.4; 0.2; 0.1]
let shiftList orgList shift =
// chop list up in tuples of what stays and what moves
let ms = orgList |> List.map (fun p-> (p * shift, p * (1.0-shift)))
// map new list
ms |> List.mapi (fun i (move, stay) ->
match i with
| 0 -> stay
| 4 -> stay + fst ms.[i-1] + move // note hardcoded 4
| _ -> stay + fst ms.[i-1])
// get shifted list
shiftList p3 0.5
Now for the questions:
1) How do I make it match on any length list? Now I hardcoded the 4 in the match expression but I'd like to be able to accept any lenght list.
I tried this:
let shiftList orgList shift =
// chop list up in tuples of what stays and what moves
let ms = orgList |> List.map (fun p-> (p * shift, p * (1.0-shift)))
// find length
let last = orgList.Length - 1
// map new list
ms |> List.mapi (fun i (move, stay) ->
match i with
| 0 -> stay
| last -> stay + fst ms.[i-1] + move
| _ -> stay + fst ms.[i-1]) // now this one will never be matched
But this will not treat last
as the number 4, instead it becomes a variable for i
even though last
is already declared above.
So how could I match on a variable, so that I can treat the last elmement differently? Finding the first one is easy because it's at 0.
2) How would you do this? I'm still pretty fresh to F# there are many things I don't know about yet. Guess the general case here is: how do I map a different function to the first and last element of a list, and a general one to the others?
Thanks in advance,
Gert-Jan
Upvotes: 3
Views: 892
Reputation: 790
Just another idea,
let bleh p3 =
match Seq.fold (fun (give,acc) i -> i*0.5,((i*0.5 + give) :: acc)) (0.0,[]) p3 with
|(give,h::t) -> give+h :: t |> List.rev
|(_,[]) -> []
Upvotes: 1
Reputation: 33637
Using List.scan:
let lst = [0.1; 0.2; 0.4; 0.2; 0.1]
let len = (lst.Length-1)
lst
|> List.mapi (fun i e -> (i,e))
|> List.scan (fun (c,_) (i,e) -> if i = len then (0.0,e+c) else ((e/2.0),(e/2.0)+c)) (0.0,0.0) |> List.tail
|> List.map snd
Upvotes: 1
Reputation: 243051
As an alternative to writing your own recursive function, you can also use built-in functions. The problem can be solved quite easily using Seq.windowed
. You still need a special case for the last element though:
let p3 = [0.1; 0.2; 0.4; 0.2; 0.1]
// Prefix zero before the list, pre-calculate the length
let p3' = (0.0 :: p3)
let l = p3.Length
// Perform the transformation
p3'
|> Seq.windowed 2
|> Seq.mapi (fun i ar ->
(if i = l - 1 then ar.[1] else ar.[1] / 2.0) + ar.[0] / 2.0)
|> List.ofSeq
Upvotes: 2
Reputation: 25516
Here is a more functional solution
let func (input:float list) =
let rec middle_end input_ =
match input_ with
|h::t::[] -> ((h/2.0)+t)::[]
|h::t::tt ->((h+t)/2.0)::(middle_end (t::tt))
| _ -> [] //fix short lists
let fst = input.Head/2.0
fst::middle_end(input)
Also, this only requires a single pass through the list, rather than the 3 in Ramon's solution, as well as less temporary storage.
Upvotes: 5
Reputation: 7560
You want to do:
let shiftList orgList shift =
// chop list up in tuples of what stays and what moves
let ms = orgList |> List.map (fun p-> (p * shift, p * (1.0-shift)))
// find length
let last = orgList.Length - 1
// map new list
ms |> List.mapi (fun i (move, stay) ->
match i with
| 0 -> stay
| last' when last' = last -> stay + fst ms.[i-1] + move
| _ -> stay + fst ms.[i-1]) // now this one will never be matched
Upvotes: 1