2011-10-03 86 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

相关问题