gjvdkamp
gjvdkamp

Reputation: 10516

Map different function to first and last element in list

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

Answers (5)

Jizugu
Jizugu

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

Ankur
Ankur

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

Tomas Petricek
Tomas Petricek

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

John Palmer
John Palmer

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

Ramon Snir
Ramon Snir

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

Related Questions