группы, пока не будет изменена последовательность

У меня есть большой файл Excel, который я читаю с помощью Excel Provider в F #. Строки должны быть сгруппированы по одному столбцу. Обработка сбоев с OutOfMemoryException . Не уверен, является Seq.groupBy вызов Seq.groupBy виновным или поставщиком типа excel. Чтобы упростить это, я использую 3D Point здесь как строку.

 type Point = { x : float; y: float; z: float; } let points = seq { for x in 1 .. 1000 do for y in 1 .. 1000 do for z in 1 .. 1000 -> {x = float x; y = float y; z = float z} } let groups = points |> Seq.groupBy (fun point -> point.x) 

Строки уже упорядочены сгруппированным столбцом, например 10 точек с x = 10, затем 20 точек с x = 20 и т. Д. Вместо того, чтобы группировать их, мне нужно просто разбить строки в кусках до тех пор, пока они не будут изменены. Есть ли способ перечислить последовательность только один раз и получить последовательность строк, разделенных, не сгруппированных, некоторым значением столбца или некоторым значением f (строки)?

Если строки уже упорядочены, то эта функция chunkify вернет seq <'список>. Каждый список будет содержать все точки с одинаковым значением x.

 let chunkify pred s = seq { let values = ref [] for x in s do match !values with |h::t -> if pred hx then values := x::!values else yield !values values := [x] |[] -> values := [x] yield !values } let chunked = points |> chunkify (fun xy -> xx = yx) 

Здесь chunked имеет тип

 seq<Point list> 

Другое решение, в том же направлении, что и у Кевина

 module Seq = let chunkBy f src = seq { let chunk = ResizeArray() let mutable key = Unchecked.defaultof<_> for x in src do let newKey = fx if (chunk.Count <> 0) && (newKey <> key) then yield chunk.ToArray() chunk.Clear() key <- newKey chunk.Add(x) } // returns 2 arrays, each with 1000 elements points |> Seq.chunkBy (fun pt -> pt.y) |> Seq.take 2 

Вот чисто функциональный подход, который, безусловно, медленнее и гораздо сложнее понять.

 module Seq = let chunkByFold f src = src |> Seq.scan (fun (chunk, (key, carry)) x -> let chunk = defaultArg carry chunk let newKey = fx if List.isEmpty chunk then [x], (newKey, None) elif newKey = key then x :: chunk, (key, None) else chunk, (newKey, Some([x]))) ([], (Unchecked.defaultof<_>, None)) |> Seq.filter (snd >> snd >> Option.isSome) |> Seq.map fst 

Давайте начнем с ввода

 let count = 1000 type Point = { x : float; y: float; z: float; } let points = seq { for x in 1 .. count do for y in 1 .. count do for z in 1 .. count -> {x = float x; y = float y; z = float z} } val count : int = 1000 type Point = {x: float; y: float; z: float;} val points : seq<Point> 

Если мы попытаемся оценить точки, мы получим исключение OutOfMemoryException:

 points |> Seq.toList System.OutOfMemoryException: Exception of type 'System.OutOfMemoryException' was thrown. at Microsoft.FSharp.Collections.FSharpList`1.Cons(T head, FSharpList`1 tail) at Microsoft.FSharp.Collections.SeqModule.ToList[T](IEnumerable`1 source) at <StartupCode$FSI_0011>[email protected]() Stopped due to error 

Это может быть одна и та же причина, что groupBy терпит неудачу, но я не уверен. Но он говорит нам, что мы должны использовать seq и yield для возвращения групп. Таким образом, мы получаем эту реализацию:

 let group groupBy points = let mutable lst = [ ] seq { for p in points do match lst with | [] -> lst <- [p] | p'::lst' when groupBy p' p -> lst <- p::lst | lst' -> lst <- [p]; yield lst' } val group : groupBy:('a -> 'a -> bool) -> points:seq<'a> -> seq<'a list> 

Это не самый читаемый код. Он берет каждую точку из последовательности точек и добавляет ее в список аккумуляторов, пока выполняется функция groupBy. Если функция groupBy не выполняется, генерируется новый список накопителей, а старый -. Обратите внимание, что порядок списка аккумуляторов отменен.

Тестирование функции:

 for g in group (fun p' p -> p'.x = px ) points do printfn "%f %i" g.[0].x g.Length 

Прекращается красиво (через некоторое время).

Другая реализация с исправлением ошибок и лучшим форматированием.

 let group (groupBy : 'a -> 'b when 'b : equality) points = let mutable lst = [] seq { yield! seq { for p in points do match lst with | [] -> lst <- [ p ] | p' :: lst' when (groupBy p') = (groupBy p) -> lst <- p :: lst | lst' -> lst <- [ p ] yield (groupBy lst'.Head, lst') } yield (groupBy lst.Head, lst) } 

Кажется, нет ни одного строкового чисто функционального решения, либо уже определенного метода Seq, который я наблюдал.

Поэтому в качестве альтернативы здесь мое собственное императивное решение. Сопоставимый с ответом @ Кевина, но на самом деле удовлетворяет больше моей потребности. В ячейке ref содержится:

  • Ключ группы, который вычисляется только один раз для каждой строки
  • Текущий список блоков (может быть seq, чтобы соответствовать Seq.groupBy ), который содержит элементы в порядке ввода, для которых f(x) равен сохраненному групповому ключу (требуется равенство).

,

 let splitByChanged f xs = let acc = ref (None,[]) seq { for x in xs do match !acc with | None,_ -> acc := Some (fx),[x] | Some key, chunk when key = fx -> acc := Some key, x::chunk | Some key, chunk -> let group = chunk |> Seq.toList |> List.rev yield key, group acc := Some (fx),[x] match !acc with | None,_ -> () | Some key,chunk -> let group = chunk |> Seq.toList |> List.rev yield key, group } points |> splitByChanged (fun point -> point.x) 

Функция имеет следующую подпись:

  val splitByChanged : f:('a -> 'b) -> xs:seq<'a> -> seq<'b * 'a list> when 'b : equality 

Приветствуются исправления и даже лучшие решения

  • F # не распознает interop.office.Excel.dll
  • F # или C # на основе Excel надстройка на OSX
  • Взаимодействие и расширение ApplicationClass
  • Импорт пользовательских функций, написанных в F # Использование Excel DNA в подпрограммах VBA
  • Как создать COM видимую DLL в F #
  • F # Преобразование валюты из Excel
  • F # вставить строку в Excel динамически
  • Тип FSHarpx Excel-тип зависает
  • Как закрыть открытый Exceldocument в F #?
  • F # Excel - ошибка на новом ApplicationClass
  • Принудительно использовать mutable для чтения Excel ThisWorkbook.Names?
  • Давайте будем гением компьютера.