在F#中回顾Array.Fold中任意数量的项目的好方法

时间:2010-05-30 05:22:15

标签: f# functional-programming

在Array.Fold操作的文件夹功能中,我想回顾任意数量的项目。

我能想出的唯一方法就是这样。

let aFolder (dataArray, curIdx, result) (dataItem) =
      let N = numItemsToLookBack(result, dataItem) 
      let recentNitems =  dataArray.[curIdx - N .. curIdx - 1]
      let result = aCalc(result, dataItem, recentNitems )

      dataArray, curIdx + 1, result

myDataArray |> Array.fold aFolder (myDataArray, 0, initResult)

如您所见,我将整个dataArray和index传递给文件夹函数以获取“recentNitems”。 但是这种方式允许文件夹功能不仅可以访问前面的数据,还可以访问以下数据。

有没有办法阻止这种期待的事情? 或者,无论这个期待的问题如何,是否有更好的(更多功能或更有效)的方式?

2 个答案:

答案 0 :(得分:2)

如果你需要回顾任意数量的元素,那么直接使用Array.fold可能不是最好的选择,因为该函数是为你可以逐个处理元素的场景而设计的。

在F#中使用直接访问数组没有任何问题 - 只要你不改变它们,你仍然在编写功能代码(因此,使用索引进行递归+直接访问可能适用于你的场景)。 / p>

正如Yin Zhu所指出的,你可以生成数组的数组(使用Seq.windowed),但是创建一个O(n)个小数组可能是一个很大的开销。这是一个你也可以使用的更复杂的技巧:

我看到你需要调用aCalc函数,只将数组的相关部分作为参数。您可以为数组创建一个简单的包装器,它只提供对数组相关部分的访问:

type ArraySlice<'a>(arr:'a[], from, max) = 
  member x.Item
    with get(index) =
      let index = index + from
      if index > max || index < from then failwith "out of range"
      arr.[index]

据我了解您的代码,您需要根据结果生成 lookback 项目的数量,因此您可能需要使用单fold来编写此内容。可以大致这样做:

// If you write this using lambdas or local 'let' binding, the 
// 'dataArray' value will be in scope, so you don't need to keep it as
// part of the state...
(dataArray, (0, initResult)) ||> Array.fold (fun (i, result) item -> 
   let n = numItemsToLookBack (result, item)  
   let recent =  new ArraySlice<_>(dataArray, max 0 (i - n), i - 1)
   // Note: modify aCalc, so that it takes 'ArraySlice'
   let result = aCalc(result, item, recent) 
   i + 1, result)

答案 1 :(得分:1)

如果修复了numItermToLookBack,那么你可以使用Seq.windowed

let arr = [|1;2;3;4;5;6;7;|]
let warr = arr |> Seq.windowed 3 |> Seq.toArray

你得到:

val warr : int [] array =
  [|[|1; 2; 3|]; [|2; 3; 4|]; [|3; 4; 5|]; [|4; 5; 6|]; [|5; 6; 7|]|]

如果数字不固定,我不知道如何更容易地做到这一点。

BTW,在你的代码中

  let recentNitems =  dataArray.[curIdx - n .. curIdx - 1]

并且在我的方法中,每次都会创建一个新的小数组,这可能会损害性能。