Kiedy używać sekwencji w języku F # zamiast listy?


82

Rozumiem, że lista faktycznie zawiera wartości, a sekwencja jest aliasem IEnumerable<T>. Kiedy w praktycznym programowaniu F # powinienem używać sekwencji, a nie listy?

Oto kilka powodów, dla których widzę, że sekwencja byłaby lepsza:

  • Podczas interakcji z innymi językami lub bibliotekami .NET, które wymagają IEnumerable<T>.
  • Trzeba przedstawić nieskończoną sekwencję (prawdopodobnie niezbyt przydatne w praktyce).
  • Potrzebujesz leniwej oceny.

Czy są jeszcze inni?


3
Uważam, że nieskończone sekwencje są bardzo przydatne i powszechne. Na przykład System.Random.Next () jest już „nieskończoną sekwencją” w przebraniu. Często potrzebuję czegoś, co generuje tyle elementów, ile potrzeba. Niedawno napisałem Tetrisa w języku F # i przedstawiłem generowanie bloków jako nieskończoną sekwencję: utworzy tyle, ile potrzeba, w miarę postępu gry.
Asik

2
@Dr_Asik Zauważ, że seqwygenerowany w ten sposób wygeneruje różne liczby losowe za każdym razem, gdy na to spojrzysz. To oczywiście może być źródłem niedeterministycznych błędów ...
JD

Odpowiedzi:


96

Myślę, że podsumowanie, kiedy wybrać, Seqjest całkiem dobre. Oto kilka dodatkowych punktów:

  • Posługiwać się Seq domyślnie podczas pisania funkcji, ponieważ wtedy działają one z dowolną kolekcją .NET
  • Użyj, Seqjeśli potrzebujesz zaawansowanych funkcji, takich jak Seq.windowedlubSeq.pairwise

Myślę, że wybór Seqdomyślny jest najlepszą opcją, więc kiedy powinienem wybrać inny typ?

  • Użyj, Listgdy potrzebujesz przetwarzania rekurencyjnego przy użyciu head::tailwzorców
    (aby zaimplementować niektóre funkcje, które nie są dostępne w standardowej bibliotece)

  • Użyj, Listgdy potrzebujesz prostej, niezmiennej struktury danych, którą możesz zbudować krok po kroku
    (na przykład, jeśli chcesz przetworzyć listę w jednym wątku - aby wyświetlić statystyki - i jednocześnie kontynuować budowanie listy w innym wątku w miarę otrzymywania więcej wartości, np. z usługi sieciowej)

  • Używaj Listpodczas pracy z krótkimi listami - lista jest najlepszą strukturą danych do użycia, jeśli wartość często reprezentuje pustą listę , ponieważ jest bardzo wydajna w tym scenariuszu

  • Użyj, Arraygdy potrzebujesz dużych kolekcji typów wartości
    (tablice przechowują dane w płaskim bloku pamięci, więc w tym przypadku są bardziej wydajne w pamięci)

  • Użyj, Arraygdy potrzebujesz dostępu swobodnego lub większej wydajności (i lokalizacji pamięci podręcznej)


1
Wielkie dzięki - dokładnie to, o co mi chodziło. Jest to mylące podczas nauki F #, aby dowiedzieć się, dlaczego istnieją te dwa elementy (lista i sekwencja), które zapewniają podobną funkcjonalność.
dodgy_coder

3
„Użyj List[...], gdy potrzebujesz prostej, niezmiennej struktury danych , którą możesz zbudować krok po kroku [...] i jednocześnie kontynuować budowanie listy w innym wątku [...]” Czy możesz rozwinąć swoją znaczenie tutaj / jak to działa? Dzięki.
Narfanar,

2
@Noein Chodzi o to, że zawsze możesz iterować po listach (są one niezmienne), ale możesz tworzyć nowe listy, używając x::xsbez przerywania istniejących pracowników, którzy mogą być w trakcie iteracjixs
Tomas Petricek

29

Również preferuj, seqgdy:

  • Nie chcesz jednocześnie przechowywać w pamięci wszystkich elementów.

  • Wydajność nie jest ważna.

  • Musisz coś zrobić przed i po wyliczeniu, np. Połączyć się z bazą danych i zamknąć połączenie.

  • Nie łączysz się (powtórzone Seq.appendprzepełnienie stosu).

Preferuj listkiedy:

  • Jest kilka elementów.

  • Będziesz dużo udawać i dekapitować.

Ani seqnie listsą dobre dla paralelizmu, ale to niekoniecznie oznacza, że ​​są one również złe. Na przykład, możesz użyć albo do przedstawienia małej grupy oddzielnych zadań do wykonania równolegle.


„Ani sekwencja, ani lista nie są dobre dla równoległości”: czy mógłbyś rozszerzyć, dlaczego sekwencja nie jest dobra dla równoległości? Co zatem jest dobre dla równoległości, tylko macierz?
Asik

5
Tablice @Dr_Asik są najlepsze, ponieważ można je rekurencyjnie podzielić i zachować dobrą lokalizację odniesienia. Drzewa są następne w kolejności, ponieważ można je również podzielić, ale lokalizacja odniesienia nie jest tak dobra. Listy i sekwencje są złe, ponieważ nie można ich podzielić. Jeśli zdobędziesz alternatywne elementy, otrzymasz najgorszą możliwą lokalizację odniesienia. Guy Steele omówił zbiory liniowe utrudniające paralelizm, chociaż bierze pod uwagę tylko pracę i głębię, a nie lokalność (inaczej złożoność pamięci podręcznej ). labs.oracle.com/projects/plrg/Publications/ ...
JD

12

Tylko jeden mały punkt: Seqi Arraysą lepsze niż w Listprzypadku równoległości.

Masz kilka opcji: PSeq z F # PowerPack, Array.Parallel modułu i Async.Parallel (asynchroniczny obliczeń). Lista jest okropna do równoległego wykonywania ze względu na jej sekwencyjny charakter ( head::tailskład).


To dobra uwaga - scenariusz, o którym myślałem, był taki, kiedy trzeba było zbudować kolekcję na jednym wątku (tj. Jak otrzymujesz wartości z jakiejś usługi) i używać jej z innego wątku (czyli obliczać statystyki i wyświetlać je). Zgadzam się, że do przetwarzania równoległego (gdy masz już wszystkie dane w pamięci), posiadanie Arraylub PSeqjest znacznie lepsze.
Tomas Petricek

1
Dlaczego mówisz, że seqjest to lepsze niż w listprzypadku równoległości? seqsą również okropne do równoległego wykonywania ze względu na ich sekwencyjną naturę ...
JD

7

lista jest bardziej funkcjonalna, przyjazna matematyce. gdy każdy element jest równy, 2 listy są równe.

sekwencja nie jest.

let list1 =  [1..3]
let list2 =  [1..3]
printfn "equal lists? %b" (list1=list2)

let seq1 = seq {1..3}
let seq2 = seq {1..3}
printfn "equal seqs? %b" (seq1=seq2)

wprowadź opis obrazu tutaj


5

Zawsze powinieneś ujawniać Seqw swoich publicznych interfejsach API. Użyj Listi Arrayw swoich wewnętrznych wdrożeniach.


Czy to dlatego, że dobrze współpracują z innymi językami .NET? tj. ponieważ a Seqjest postrzegane jako IEnumerable<T>?
dodgy_coder

Nie z powodu dobrych praktyk projektowych. Udostępniaj tyle informacji, ile potrzeba, nie więcej.
Aleš Roubíček

Ok, uczciwy komentarz, jest to również dobra praktyka dla kodu C # - tj. Funkcja może być najlepiej zdefiniowana jako IEnumerable <T>, a nie na przykład cięższa List <T>.
dodgy_coder

1
Zgadzam się w kontekście mutowalnych struktur zwrotnych (np. Ten błąd projektowy w .NET: msdn.microsoft.com/en-us/library/afadtey7.aspx ) lub API, które mogą być używane z innych języków .NET, ale nie zgadzam się ogólnie, częściowo dlatego, że seqsą tak złe dla paralelizmu.
JD
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.