更優雅實用的解決方案:
let duplicates xs =
Seq.scan (fun xs x -> Set.add x xs) Set.empty xs
|> Seq.zip xs
|> Seq.choose (fun (x, xs) -> if Set.contains x xs then Some x else None)
用途scan
積累套迄今所看到的所有元素。然後使用zip
將每個元素與之前的一組元素進行組合。最後,使用choose
來過濾出在一組先前看到的元素中的元素,即重複。
編輯
其實我原來的答案是完全錯誤的。首先,你不想在你的輸出中重複。其次,你需要表現。
這裏是實現你後的算法純功能的解決方案:
let duplicates xs =
(Map.empty, xs)
||> Seq.scan (fun xs x ->
match Map.tryFind x xs with
| None -> Map.add x false xs
| Some false -> Map.add x true xs
| Some true -> xs)
|> Seq.zip xs
|> Seq.choose (fun (x, xs) ->
match Map.tryFind x xs with
| Some false -> Some x
| None | Some true -> None)
這將使用地圖來追蹤每個元素是否已見過一次或多次,然後發出的元素,如果它被看作是以前只見過一次,即第一次被複制。
這裏是一個更快的當務之急版本:
let duplicates (xs: _ seq) =
seq { let d = System.Collections.Generic.Dictionary(HashIdentity.Structural)
let e = xs.GetEnumerator()
while e.MoveNext() do
let x = e.Current
let mutable seen = false
if d.TryGetValue(x, &seen) then
if not seen then
d.[x] <- true
yield x
else
d.[x] <- false }
這比任何其他的答案,快約2 ×(在寫作的時候)。
使用for x in xs do
循環來列舉在一個序列中的元素是比直接使用GetEnumerator
但生成自己Enumerator
不顯著比使用與yield
的計算表達式快慢得多。
注意的Dictionary
的TryGetValue
成員讓我通過突變堆棧分配的值,而通過F#提供的(並用在他/她的回答KVB)的TryGetValue
擴展成員分配其返回的元組,以避免在內部循環分配。
可能的重複[如何刪除F#序列中的重複項而不使用引用](http://stackoverflow.com/questions/6842466/how-can-i-remove-duplicates-in-an-f-sequence - 沒有使用引用) – gradbot 2012-03-14 19:23:00
實際上,它是相反的。我只想要重複的東西。 – Daniel 2012-03-14 19:24:30
嗯,你想如何存儲你已經訪問過的值?組?字典? – gradbot 2012-03-14 19:28:20