Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Map different function to first and last element in list

Tags:

list

f#

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.

  • 0.1 became 0.05 because it gave 0.05 to the next, there is no previous element
  • 0.2 became 0.15 because it gave 0.1 to the next and got 0.05 from the first
  • etc
  • and finally 0.1 became 0.2 because it .01 from the previous. There is no 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

like image 499
gjvdkamp Avatar asked Dec 12 '25 15:12

gjvdkamp


2 Answers

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.

like image 102
John Palmer Avatar answered Dec 14 '25 11:12

John Palmer


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
like image 40
Tomas Petricek Avatar answered Dec 14 '25 12:12

Tomas Petricek



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!