2011-10-03 44 views
3

我有一個函數,我想將浮動列表轉換爲另一個元素,其中對於每個元素,我希望元素i的x%溢出到元素i + 1中將不同的函數映射到列表中的第一個元素和最後一個元素

例如:

let p3 = [0.1; 0.2; 0.4; 0.2; 0.1] 

然後p3_s應該是:

[0.05; 0.15; 0.3; 0.3; 0.2] 

要做到這一點,我把每一個元素的一半,並把它添加到下一個元素。

  • 0.1變成了0.05,因爲它給了0.05到下一個,沒有一個元素
  • 0.2變成0.15,因爲它給了0.1到下一個,並從 第一
  • 了0.05和終於0.1成爲0.2,因爲它從之前的 .01。沒有下一個元素。

現在我來到了這裏面,但僅適用於大小爲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 

現在的問題:

1)我如何使它匹配任何長度的列表?現在我在匹配表達式中對4進行了硬編碼,但我希望能夠接受任何長度列表。

我嘗試這樣做:

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 

但這不會把last爲4號,而是成爲i即使last上面已經聲明的變量。

那麼,我怎麼能匹配一個變量,以便我可以以不同的方式對待最後一個elmement?找到第一個很容易,因爲它在0.

2)你會如何做到這一點?我對F#還是很新鮮,但還有許多事情我還不知道。猜測這裏的一般情況是:我如何將一個不同的函數映射到列表的第一個和最後一個元素,並且將其他函數映射到其他元素?

由於提前,

格特 - 揚

回答

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))) 

    // 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 
+0

我去「×當x =最後 - >',是否有任何特殊的意義持續'?或者它只是一個變量的名稱與'在裏面? – gjvdkamp

+2

@gjvdkamp這個名字沒有任何意義,雖然它清楚地表明你只是用它來比較'真正'的最後一個變量 –

5

這裏是一個更實用的解決方案

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) 

而且,這個只需要一個單通過列表,而不是拉蒙的解決方案中的3個,以及較少的臨時存儲。

+0

嗯我喜歡這樣。我已經接受拉蒙的回答,但這似乎是一個更實用的方法。謝謝。實際上,對於我的代碼,我切換到一個數組,並取消了任何中間步驟,因此應該很快。在那裏我使用'when'語法來匹配最後一個元素。 – gjvdkamp

+0

嗨,再次感謝您一直玩弄你的解決方案,事實上,你可以比h :: t更深入地匹配列表,這是非常強大的。今天學到了很多。 – gjvdkamp

+0

您可以在匹配表達式中編寫非常複雜的東西,請參閱http://msdn.microsoft.com/en-us/library/dd547125.aspx –

2

作爲編寫自己的遞歸函數的替代方法,您還可以使用內置函數。使用Seq.windowed可以很容易地解決問題。您還需要最後一個元素的特殊情況,但:

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 
+0

啊..我確實先看了Seq.windowed,但遇到第一種情況。另外我不知道我可以直接使用Seq.window直接對列表,Seq.ofList讓我離開這條路。謝謝,學到了一些! – gjvdkamp

+0

把它變成了這個:|> Seq.mapi(fun i v - > v。[0] * shift + v。[1] *(1.0 - if i = last then 0.0 else shift))。其實最喜歡這個,但我不喜歡從別人那得到答案。下次再等一會兒。 – gjvdkamp

+1

@gjvdkamp沒問題:-)很高興幫助 - 如果你正在學習函數式編程,那麼首先編寫遞歸版本(by _jpalmer_)是學習核心功能概念的好方法。從庫中已有的函數(比如my或_Ramon_的解決方案)編寫解決方案可能是第二步。 –

1

使用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 
+0

花了我一秒鐘才明白,這個也很聰明。我在尋找List.unfold,在那裏我可以推動我想要向前移動的部分,我猜掃描可以做到這一點。謝謝! – gjvdkamp

1

又一個想法,

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 
    |(_,[]) -> [] 
+0

它的工作原理,但我不知道如何...這是相當簡潔;-)當我有時間時,我會選擇它。謝謝! – gjvdkamp

相關問題