что было бы хорошим способом реализовать List.scani на F #? - PullRequest
1 голос
/ 04 августа 2020

У меня ситуация, когда я разбираю файл, и мне нужно знать оба:

  • текущая строка
  • предыдущая строка

до требований предыдущей строки я делал что-то вроде:

myData
|> List.mapi (fun i data -> parse i data)

, но теперь мне нужен доступ к предыдущей строке, поэтому сканирование идеально подходит для этого, но тогда я теряю индекс.

итак, мне нужна функция List.scani :) это что-то, что можно было бы легко создать идиоматическим c способом?

Ответы [ 2 ]

3 голосов
/ 04 августа 2020

Вы можете определить scani следующим образом:

let scani (f:int->'S->'T->'S) (state:'S) (list:'T list) = 
    list
    |>List.scan (fun (i,s) x -> (i+1,f i s x)) (0,state)
    |>List.map snd

Создание кортежа с исходным состоянием и счетчиком, инициализированным с помощью (0,state). С состоянием манипулируют, как обычно, с помощью функции папки f (которая теперь принимает дополнительный параметр i) и счетчика, увеличиваемого на единицу. Наконец, мы удаляем счетчик из состояния, беря второй элемент состояния.

Вы можете использовать его следующим образом, где i - индекс, s - состояние, а x - элемент.

[1;2;3]
|> scani (fun i s x -> s + i*x) 0
|> should equal [0;0;2;8]
3 голосов
/ 04 августа 2020

Возможно, это не самый эффективный способ сделать это, но, похоже, он работает (я назвал его scanl, учитывая, что вам нужен доступ к предыдущему элементу или строке):

let scanl f s l =
    List.scan (fun (acc,elem0) elem1 -> (f acc elem0 elem1),elem1) (s,List.head l) l
    |> List.map fst

Примеры использование:

let l = [1..5]

scanl (fun acc elem0 elem1 -> elem0,elem1) (0,0) l
//result: [(0, 0); (1, 1); (1, 2); (2, 3); (3, 4); (4, 5)]

Обычный List.scan даст следующее:

List.scan (fun acc elem -> elem) 0 l
//result [0; 1; 2; 3; 4; 5]
...