Một giải pháp chức năng thanh lịch hơn:
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)
Sử dụng scan
để tích lũy bộ của tất cả các yếu tố thấy cho đến nay. Sau đó, sử dụng zip
để kết hợp từng phần tử với tập hợp các phần tử trước đó. Cuối cùng, sử dụng choose
để lọc ra các phần tử nằm trong tập hợp các phần tử đã xem trước đó, tức là các bản sao.
EDIT
Thực ra câu trả lời ban đầu của tôi là hoàn toàn sai. Thứ nhất, bạn không muốn bản sao trong kết quả đầu ra của bạn. Thứ hai, bạn muốn hiệu suất.
Dưới đây là một giải pháp hoàn toàn chức năng mà thực hiện các thuật toán bạn đang sau:
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)
này sử dụng một bản đồ để theo dõi xem mỗi yếu tố đã được thấy trước đây một lần hoặc nhiều lần và sau đó phát ra nguyên tố này nếu nó được nhìn thấy chỉ được nhìn thấy một lần trước đây, tức là lần đầu tiên nó được nhân đôi.
Đây là một phiên bản cấp bách nhanh hơn:
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 }
Đây là khoảng 2 × nhanh hơn so với bất kỳ câu trả lời khác của bạn (ở thời điểm viết bài).
Sử dụng một vòng lặp for x in xs do
để liệt kê các yếu tố trong một chuỗi là chậm hơn đáng kể so với sử dụng GetEnumerator
trực tiếp nhưng tạo Enumerator
riêng của bạn không phải là đáng kể nhanh hơn so với sử dụng một biểu thức tính toán với yield
.
Lưu ý rằng TryGetValue
viên của Dictionary
cho phép tôi để tránh phân bổ trong vòng lặp bên trong bằng cách biến đổi một giá trị stack giao trong khi các thành viên TryGetValue
phần mở rộng được cung cấp bởi F # (và được sử dụng bởi kvb trong/câu trả lời của mình) phân bổ tuple trở lại của mình.
bản sao có thể có của [Làm cách nào tôi có thể xóa các bản trùng lặp trong chuỗi F # mà không sử dụng tham chiếu] (http://stackoverflow.com/questions/6842466/how-can-i-remove-duplicates-in-an-f-sequence -Không có tham chiếu-sử dụng) – gradbot
Thực ra, đó là nghịch đảo. Tôi chỉ muốn các bản sao. – Daniel
Hmm, làm cách nào bạn muốn lưu trữ các giá trị bạn đã truy cập? Bộ? Từ điển? – gradbot