Почему F # Seq.windowed возвращает серию массива

Seq.windowed в F # возвращает последовательность, в которой каждое окно внутри является массивом. Есть ли причина, по которой каждое окно возвращается в виде массива (очень конкретный тип), в отличие от другой последовательности или IList<'T>? Например, IList<'T> было бы достаточно, если бы цель состояла в том, чтобы сообщить, что элементы окна могут быть случайным образом доступны, но массив говорит о двух вещах: элементы являются изменяемыми и беспорядочно доступными. Если вы можете рационализировать выбор массива, то как windowed отличается от Seq.groupBy? Почему этот последний (или операторы в том же духе) не также возвращают члены группы в виде массива?

Мне интересно, просто ли это надзор за дизайном или есть более глубокая контрактная причина для массива?

Ответ 1

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

В общем, я считаю, что API-интерфейсы F # используют 'T[], если использование массива является естественной эффективной реализацией или возвращает seq<'T>, когда источник данных может быть бесконечным, ленивым или когда реализация должна будет преобразовать данные к массиву явно (тогда это можно оставить вызывающему).

Для Seq.windowed, я думаю, что массив имеет смысл, потому что вы знаете длину массива, и поэтому вы, вероятно, будете использовать индексирование. Например, если предположить, что prices - это последовательность кортежей с датой (seq<DateTime * float>), вы можете написать:

prices
|> Seq.windowed 5
|> Seq.map (fun win -> fst (win.[2]), Seq.averageBy snd win)

Пример вычисляет значение плавающего среднего и использует индексирование для получения даты посередине.

В целом, у меня нет действительно хорошего объяснения для обоснования дизайна, но я вполне доволен выборами - они, похоже, очень хорошо работают с обычными вариантами использования для функций.

Ответ 2

Несколько мыслей.

Во-первых, знайте, что в их текущей версии, как Seq.windowed, так и Seq.groupBy используют нелазные коллекции в своей реализации. windowed использует массивы и возвращает массивы. groupBy создает Dictionary<'tkey, ResizeArray<'tvalue>>, но сохраняет этот секрет и возвращает значения группы обратно как seq вместо ResizeArray.

Возврат ResizeArray из groupBy не подходит ни к чему другому, так что, очевидно, его нужно скрыть. Другой альтернативой является возвращение данных ToArray(). Для этого потребуется другая копия данных, которые будут созданы, что является недостатком. И на самом деле не так много роста, поскольку вы заранее не знаете, насколько велика ваша группа, так что вы не ожидаете сделать произвольный доступ или какие-либо другие специальные массивы. Так что просто обертка в seq кажется хорошим вариантом.

Для windowed это совсем другая история. Вы хотите получить массив в этом случае. Зачем? Поскольку вы уже знаете, как большой этот массив будет, так что вы можете безопасно делать произвольный доступ или, еще лучше, сопоставление шаблонов. Это большой потенциал. Однако недостаток остается - данные необходимо переписать в новый выделенный массив для каждого окна.

seq{1 .. 100} |> Seq.windowed 3 |> Seq.map (fun [|x; _; y|] -> x + y)

До сих пор остается открытым вопрос: "но не могли ли мы избежать распределения массива/копирования вниз внутренне только с использованием истинных ленивых секций и возвращать их как таковые? Разве это не больше в" духе seq ",?" Это было бы довольно сложно (понадобилось бы какое-то причудливое клонирование счетчиков?), Но конечно, возможно, с некоторой тщательной кодировкой. Тем не менее, есть огромный недостаток. Вам нужно будет кэшировать весь unspooled seq в памяти, чтобы заставить его работать, что отрицает всю цель делать вещи лениво. В отличие от списков или массивов, перечисление seq несколько раз не гарантирует получение одинаковых результатов (например, seq, который возвращает случайные числа), поэтому данные резервного копирования для этих окон seq, которые вы возвращаете, нужно где-то кэшировать. Когда это окно в конечном итоге будет доступно, вы не сможете просто нажать и перечислить исходный исходный код - вы можете получить другие данные, или seq может закончиться в другом месте. Это указывает на другую сторону использования массивов в Seq.windowed - только элементы windowSize должны храниться в памяти сразу.

Ответ 3

Это, конечно, чистое предположение. Я думаю, что это связано с тем, как реализованы обе функции.

Как уже упоминалось, в Seq.groupBy группы имеют переменную длину, а в Seq.windowed они имеют фиксированный размер.

Таким образом, в реализации из Seq.windowed имеет смысл использовать массив фиксированного размера, в отличие от Generic.List, используемого в Seq.groupBy, который btw в F # называется ResizeArray.

Теперь во внешний мир Array хотя mutable широко используется в коде F # и библиотеках, а F # предоставляет синтаксическую поддержку для создания, инициализации и манипуляции массивами, тогда как ResizeArray не так широко используется в коде F # и язык не поддерживает синтаксическую поддержку, кроме псевдонима типа, поэтому я думаю, что почему они решили разоблачить его как Seq.