2016-12-29 12 views
4

Я хочу сгруппировать последовательность, а затем взять первое вхождение каждого элемента в группе. Когда я попробую этоСохраняет ли Seq.groupBy порядок внутри групп?

Seq.groupBy f inSeq 
|> Seq.map (fun (k,s) -> (k,s|>Seq.take 1|>Seq.exactlyOne)) 

Я нахожу, что иногда я получаю другой элемент от s. Ожидается ли это?

+0

Да, я должен использовать Seq.head во второй строке, но я хотел бы поделиться кодом, с которым я работаю дословно. :) –

+1

Можете ли вы изолировать случаи, когда это происходит? –

ответ

4

Глядя на source of the groupBy implementation - вот соответствующий бит:

// Build the groupings 

seq |> iter (fun v -> 
    let safeKey = keyf v 
    let mutable prev = Unchecked.defaultof<_> 
    match dict.TryGetValue (safeKey, &prev) with 
    | true -> prev.Add v 
    | false -> 
     let prev = ResizeArray() 
     dict.[safeKey] <- prev 
     prev.Add v) 

Он перебирает в исходном массиве и добавляет значения в соответствующий список для ключа. На порядок подпоследовательностей непосредственно влияет порядок входной последовательности. Для той же входной последовательности можно ожидать, что groupBy вернет идентичные выходные последовательности. Вот как кодируются тесты для groupBy.

Если вы видите изменения в результирующих последовательностях, проверьте входную последовательность.

+0

Спасибо, это то, что я ожидал, но для почему-то я получил что-то другое. К сожалению, теперь я не могу воспроизвести проблему. –

+1

@RobertSim Должно быть, это случай «Works on My Machine ™» в обратном порядке. – Asti

4

Да, это ожидается. Не гарантируется pure. Вы можете определить последовательность, которая будет давать разные значения каждый раз, когда вы перебираете их. Если вы дважды вызываете Seq.take 1, вы можете получить разные результаты.

Рассмотрим, в качестве примера, эта последовательность:

open System 

let r = Random() 
let s = seq { yield r.Next(0, 9) } 

Если вы звоните Seq.take 1 на том, что вы можете получить разные результаты:

> s |> Seq.take 1;; 
val it : seq<int> = seq [4] 
> s |> Seq.take 1;; 
val it : seq<int> = seq [1] 

Использование Seq.head не собирается, чтобы помочь вам либо :

> s |> Seq.head;; 
val it : int = 2 
> s |> Seq.head;; 
val it : int = 6 

Если вы хотите, чтобы гарантировать детерминистическое b ehaviour, используйте вместо этого List.

+0

... или в некоторых случаях можно использовать 'Seq.cache' для 'pin'' seq' (но закрывающая строка повторно моделирует вещи, поскольку они должны быть удержаны) –