Коли використовувати послідовність у F # на відміну від списку?


82

Я розумію, що список насправді містить значення, а послідовність є псевдонімом для IEnumerable<T>. Коли в практичній розробці F #, коли я повинен використовувати послідовність на відміну від списку?

Ось кілька причин, якими я бачу, коли послідовність буде кращою:

  • При взаємодії з іншими мовами .NET або бібліотеками, які вимагають IEnumerable<T>.
  • Потрібно представляти нескінченну послідовність (ймовірно, не дуже корисну на практиці).
  • Потрібна ледача оцінка.

Чи є інші?


3
Я вважаю нескінченні послідовності дуже корисними та поширеними. Наприклад, System.Random.Next () - це вже "маскувана нескінченна послідовність". Часто я хочу щось, що генерує стільки елементів, скільки потрібно. Нещодавно я написав тетріс у F # і представив генерацію блоків як нескінченну послідовність: він створить стільки, скільки потрібно, скільки триває гра.
Асік

2
@Dr_Asik Зверніть увагу, що seqгенерований таким чином буде створювати різні випадкові числа кожного разу, коли ви дивитесь на нього. Очевидно, це може бути джерелом недетермінованих помилок ...
JD

Відповіді:


96

Я думаю, що ваш підсумок, коли вибрати, Seqє досить хорошим. Ось кілька додаткових моментів:

  • Використовуйте Seqза замовчуванням під час написання функцій, оскільки тоді вони працюють з будь-якою колекцією .NET
  • Використовуйте, Seqякщо вам потрібні розширені функції, такі як Seq.windowedабоSeq.pairwise

Я думаю, що вибір Seqза замовчуванням є найкращим варіантом, тож коли я вибрав би інший тип?

  • Використовуйте, Listколи вам потрібна рекурсивна обробка за допомогою head::tailшаблонів
    (для реалізації деяких функціональних можливостей, недоступних у стандартній бібліотеці)

  • Використовуйте, Listколи вам потрібна проста незмінна структура даних, яку ви можете будувати поетапно
    (наприклад, якщо вам потрібно обробити список в одному потоці - показати деякі статистичні дані - і одночасно продовжувати будувати список в іншому потоці, коли ви отримуєте більше значень, тобто від послуги мережі)

  • Використовуйте Listпід час роботи з короткими списками - список є найкращою структурою даних для використання, якщо значення часто представляє порожній список , оскільки він дуже ефективний у цьому сценарії

  • Використовуйте, Arrayколи вам потрібні великі колекції типів значень
    (масиви зберігають дані у плоскому блоці пам'яті, щоб вони в цьому випадку були більш ефективними)

  • Використовуйте, Arrayколи вам потрібен довільний доступ або більша продуктивність (і місцезнаходження кешу)


1
Велике спасибі - саме те, що я шукав. Це заплутано під час вивчення F #, щоб з’ясувати, чому існують ці два елементи (list & seq), які надають вам подібну функціональність.
dodgy_coder

3
"Використовуйте List[...], коли вам потрібна проста незмінна структура даних, яку ви можете будувати поетапно [...] і одночасно продовжувати будувати список в іншому потоці [...]" Чи можете ви, детальніше, розповісти про ваш значення тут / як це працює? Дякую.
Нарфанар

2
@Noein Ідея полягає в тому, що ви завжди можете переглядати списки (вони незмінні), але ви можете створювати нові списки, використовуючи, x::xsне порушуючи жодних існуючих працівників, які можуть перебувати в процесі ітераціїxs
Томаш Петрічек

29

Також віддайте перевагу, seqколи:

  • Ви не хочете одночасно зберігати всі елементи в пам'яті.

  • Продуктивність не важлива.

  • Вам потрібно зробити щось до і після переліку, наприклад, підключитися до бази даних і закрити зв’язок.

  • Ви не об'єднуєтесь (повторення Seq.appendпризведе до переповнення стека).

Віддайте перевагу, listколи:

  • Елементів мало.

  • Ви будете багато передувати і обезголовити.

Ні паралелізм seqні listдобре, але це не означає, що вони погані. Наприклад, ви можете використовувати будь-яку для представлення невеликої групи окремих робіт, які потрібно виконувати паралельно.


"Ні послідовності, ні перелік не підходять для паралелізму": чи можете ви розширити інформацію про те, чому seq не підходить для паралелізму? Що тоді добре для паралелізму, лише масив?
Асік

5
Масиви @Dr_Asik найкращі, тому що ви можете їх рекурсивно розподілити та зберегти належну локальність посилання. Дерева - найкращі, тому що ви можете їх також розділити, але місце розташування не так добре. Списки та послідовності погані, тому що ви не можете їх поділити. Якщо ви виробляєте альтернативні елементи, тоді ви отримуєте найгірший можливий регіон посилання. Гай Стіл обговорював лінійні колекції, що перешкоджають паралелізму, хоча він враховує лише роботу та глибину, а не місцевість (вона ж складність кешу ). labs.oracle.com/projects/plrg/Publications/…
JD

12

Тільки один невеликий момент: Seqі Arrayкраще, ніж Listдля паралелізму.

У вас є кілька варіантів: PSeq від F # PowerPack, модуль Array.Parallel та Async.Parallel (асинхронне обчислення). Список є жахливим для паралельного виконання через його послідовний характер ( head::tailсклад).


Це хороший момент - я мав на увазі сценарій, коли вам потрібно побудувати колекцію з одного потоку (тобто, коли ви отримуєте значення від якоїсь служби) і використовувати її з іншого потоку (тобто для обчислення статистики та її відображення). Я згоден з тим, що для паралельної обробки (коли у вас вже є всі дані в пам'яті) наявність Arrayабо PSeqє набагато кращими.
Томаш Петрічек

1
Чому ви кажете, що seqце краще, ніж listдля паралелізму? seqтакож жахливі для паралельного виконання через їх послідовний характер ...
JD

7

список більш функціональний, зручний для математики. коли кожен елемент рівний, 2 списки рівні.

послідовність не є.

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)

введіть тут опис зображення


5

Ви завжди повинні виставляти Seqсвої загальнодоступні API. Використовуйте Listта Arrayу своїх внутрішніх реалізаціях.


Це тому, що вони чудово працюють з іншими мовами .NET? тобто тому, що а Seqрозглядається як IEnumerable<T>?
dodgy_coder

Ні, це через добру практику дизайну. Розкривайте стільки інформації, скільки потрібно, не більше.
Aleš Roubíček

Гаразд, справедливий коментар, це хороша практика і для коду C # - тобто функцію найкраще визначати як IEnumerable <T>, а не як важчий List <T>, наприклад.
dodgy_coder

1
Я погоджуюся в контексті змінних структур повернення (наприклад, ця вада дизайну в .NET: msdn.microsoft.com/en-us/library/afadtey7.aspx ) або API, які можуть використовуватися з інших мов. погодьтесь загалом, частково тому, що seqвони так погані для паралелізму.
JD
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.