Хороший способ просмотреть произвольное количество элементов в Array.Fold в F # - PullRequest
1 голос
/ 30 мая 2010

В функции папки операции 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 в функцию folder, чтобы получить "latestNitems". Но этот способ позволяет функции папок получать доступ не только к предыдущим, но и к следующим данным.

Есть ли способ предотвратить эту перспективу? Или есть ли лучший (более функциональный или более эффективный) способ, независимо от этой ожидаемой проблемы?

Ответы [ 2 ]

2 голосов
/ 30 мая 2010

Если вам нужно оглянуться назад на произвольное количество элементов, то использование Array.fold напрямую может быть не лучшим вариантом, так как функция предназначена для сценариев, в которых вы можете обрабатывать элементы один за другим.

Нет ничего плохого в использовании прямого доступа к массивам в F # - если вы не изменяете их, вы все еще пишете функциональный код (поэтому, возможно, рекурсия + прямой доступ с использованием индексов будет работать в вашем сценарии).

Как указывает Инь Чжу, вы можете сгенерировать массив частей массива (используя 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]

Насколько я понимаю ваш код, вам нужно сгенерировать количество просмотров элементов на основе результата, поэтому вам, вероятно, нужно будет написать это, используя один 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 голос
/ 30 мая 2010

Если зафиксирован 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|]|]

Если номер не фиксированный, я не знаю способа сделать это проще, чем ваш.

Кстати, в вашем коде

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

а также в моем методе каждый раз создается новый маленький массив, который может немного снизить производительность.

...