Подтвердить что ты не робот

Какая разница между последовательностью и коллекцией в Clojure

Я программист на Java и новичок в Clojure. Из разных мест я видел, как последовательность и коллекция используются в разных случаях. Тем не менее, я понятия не имею, какова точная разница между ними.

Для некоторых примеров:

1) В документации Clojure для Sequence:

The Seq interface
(first coll)
  Returns the first item in the collection. 
  Calls seq on its argument. If coll is nil, returns nil.
(rest coll)
  Returns a sequence of the items after the first. Calls seq on its argument. 
  If there are no more items, returns a logical sequence for which seq returns nil.
(cons item seq)
  Returns a new seq where item is the first element and seq is the rest.

Как вы можете видеть, при описании интерфейса Seq первые две функции (first/rest) используют coll что указывает на то, что это коллекция, а функция cons использует seq которая указывает на то, что это последовательность.

2) Есть функции, называемые coll? и как seq? это может использоваться, чтобы проверить, является ли значение коллекцией или последовательностью. Это явно сбор и последовательность разные.

3) В документации Clojure о " Коллекциях " сказано:

Поскольку коллекции поддерживают функцию seq, все функции последовательности могут использоваться с любой коллекцией.

Означает ли это, что все коллекции являются последовательностями?

(coll? [1 2 3]) ; => true 
(seq? [1 2 3]) ; => false

Приведенный выше код говорит мне, что это не тот случай, потому что [1 2 3] - это коллекция, но не последовательность.

Я думаю, что это довольно простой вопрос для Clojure, но я не могу найти место, объясняющее это ясно, в чем их различие и какой из них мне следует использовать в разных случаях. Любой комментарий приветствуется.

4b9b3361

Ответ 1

Каждая последовательность является коллекцией, но не каждая коллекция является последовательностью.

Функция seq позволяет конвертировать коллекцию в последовательность. Например, для карты вы получите список ее записей. Этот список записей отличается от самой карты.

Ответ 2

Любой объект, поддерживающий основные функции first и rest, является sequence.

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

Итак:

user> (seq [1 2 3])
    (1 2 3)

И вы можете создать объект последовательности из map тоже

user> (seq {:a 1 :b 2})
    ([:a 1] [:b 2])

Для этого вы можете использовать filter, map, for и т.д. на maps sets и т.д.

Таким образом, вы можете обрабатывать многие объекты, подобные коллекциям, в виде последовательностей.

Вот почему многие функции обработки последовательности, такие как filter вызывают seq на входе:

 (defn filter
  "Returns a lazy sequence of the items in coll for which
  (pred item) returns true. pred must be free of side-effects."
  {:added "1.0"
   :static true}
  ([pred coll]
   (lazy-seq
      (when-let [s (seq coll)]

Если вы вызываете (filter pred 5)

  Don't know how to create ISeq from: java.lang.Long
                  RT.java:505 clojure.lang.RT.seqFrom
                  RT.java:486 clojure.lang.RT.seq
                 core.clj:133 clojure.core/seq
                core.clj:2523 clojure.core/filter[fn]

Вы видите, что вызов seq - это объект проверки последовательности.

Большая часть этого материала находится в Радость Clojure главы 5, если вы хотите углубиться.

Ответ 3

В Clojure для храбрых и истинных автор суммирует его по-настоящему понятным образом:

Абстракция коллекции тесно связана с последовательностью абстракция. Все Clojure основные структуры данных - векторы, карты, списки и наборы - участвовать в обеих абстракциях.

Абстракции отличаются тем, что абстракция последовательности "около" работая на членах индивидуально, в то время как абстракция коллекции "о" структуре данных в целом ". Например, коллекция функции count, empty? и every? не относятся ни к одному человеку элемент; они о целом.

Ответ 4

Вот несколько моментов, которые помогут понять разницу между коллекцией и последовательностью.

  • "Коллекция" и "Последовательность" - это абстракции, а не свойство, которое может быть определено из заданного значения.

  • Коллекции представляют собой мешки с ценностями.

  • Последовательность представляет собой структуру данных (подмножество коллекции), к которой, как ожидается, будут доступны последовательный (линейный) способ.

На рисунке ниже лучше всего описывается соотношение между ними:

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

Подробнее об этом можно узнать .

Ответ 5

Для seq?:

Возвращает true, если x реализует ISeq

Для coll?:

Возвращает true, если x реализует IPersistentCollection

И я нашел интерфейс ISeq, простирается от IPersistentCollection в исходном коде Clojure, так как Rörd сказал, что каждая последовательность представляет собой коллекцию.

Ответ 6

Я только что прочитал главу 5 - "Типы коллекций" "Радости Clojure", которая немного сбивает с толку (то есть следующая версия этой книги нуждается в рецензировании). В главе 5 на странице 86 есть таблица, которой я не полностью доволен:

Table 5.1 from the Joy of Clojure, 2nd ed.

Итак, вот мое мнение (полностью обновлено после возвращения к этому после месяца размышлений).

коллекция

Это "вещь", коллекция других вещей.

Это основано на функции coll?.

  • Функция coll? может использоваться для проверки этого.
  • И наоборот, все, для чего coll? возвращает true, является коллекцией.

Строка документации coll? гласит:

Возвращает true, если x реализует IPersistentCollection

Вещи, которые являются коллекциями, сгруппированными в три отдельных класса. Вещи в разных классах никогда не равны.

  • Тест карт с использованием (map? foo)
    • Карта (две фактические реализации с немного отличающимся поведением)
    • Сортированная карта. Примечание: (sequential? (sorted-map :a 1); => false
  • Устанавливает тест с использованием (set? foo)
    • Установить
    • Сортированный набор. Примечание: (sequential? (sorted-set :a :b)); => false
  • Тест последовательных коллекций с использованием (sequential? foo)
    • Список
    • Вектор
    • Очередь
    • Seq: (sequential? (seq [1 2 3])); => true
    • Lazy-Seq: (sequential? (lazy-seq (seq [1 2 3]))); => true

Взаимодействие с Java за пределами этого:

  • (coll? (to-array [1 2 3])); => false
  • (map? (doto (new java.util.HashMap) (.put "a" 1) (.put "b" 2))); => false

последовательный сбор ("цепочка")

Это "вещь", коллекция, содержащая другие вещи в соответствии с определенным, стабильным порядком.

Это основано на функции sequential?.

  • Функция sequential? может использоваться для проверки этого.
  • И наоборот, все, для чего sequential? возвращает true, является последовательной коллекцией.

Строка документации sequential? гласит:

Возвращает true, если coll реализует Sequential

Примечание: "последовательный" является прилагательным! В "Радости Clojure" прилагательное используется как существительное, и это очень, очень, очень запутанно:

"Clojure классифицирует каждый тип данных коллекции на один из трех логические категории или разделы: последовательности, карты и наборы. "

Вместо "последовательной" следует использовать "последовательную вещь" или "последовательную коллекцию" (как использовано выше). С другой стороны, в математике уже существуют следующие слова: "цепочка", "полностью упорядоченное множество", "просто упорядоченное множество", "линейно упорядоченное множество". "цепь" звучит отлично, но никто не использует это слово. Позор!

"Радость Clojure" также говорит следующее:

Beware type-based predicates!

Clojure включает несколько предикатов с именами, такими как слова просто определены. Хотя они не часто используются, кажется, стоит упоминая, что они могут не иметь в виду именно то, что определения здесь может предложить. Например, каждый объект для которого последовательный? возвращается true - это последовательная коллекция, но для некоторых она возвращает false также последовательны [лучше: "это можно считать последовательным Коллекции "]. Это из-за деталей реализации, которые могут быть улучшена в будущей версии Clojure [и, возможно, это уже было сделано?]

последовательность (также "абстракция последовательности")

Это скорее концепция, чем вещь: серия значений (упорядоченных таким образом), которые могут существовать или не существовать (то есть поток). Если вы говорите, что вещь - это последовательность, является ли эта вещь обязательно коллекцией Clojure, даже последовательной коллекцией? Я так полагаю.

Эта последовательная коллекция может быть полностью вычислена и быть полностью доступной. Или это может быть "машина" для генерации значений по необходимости (путем вычислений - вероятно, "чистым" способом - или путем запроса внешних "нечистых", "оракулярных" источников: клавиатура, базы данных)

сл

Это вещь: то, что может быть обработано функциями first, rest, next, cons (и, возможно, другие?), То есть что-то, что подчиняется протоколу protocol clojure.lang.ISeq (что примерно соответствует концепции "обеспечения реализации для интерфейс "в Java), то есть система зарегистрировала реализации функций для пары (вещь, имя-функции) [Я очень надеюсь, что я понимаю это правильно...]

Это основано на функции seq?.

  • Функция seq? может использоваться для проверки этого
  • И наоборот, seq - это все, для чего seq? возвращает true.

Строка документации для seq?:

Вернуть true, если x реализует ISeq

Строка документации для first:

Возвращает первый элемент в коллекции. Вызывает seq на свой аргумент. Если coll - ноль, возвращает ноль.

Строка документации для rest:

Возвращает возможно пустую последовательность элементов после первого. Вызовы seq на его аргумент.

Строка документации для next:

Возвращает последовательность пунктов после первого. Вызывает seq на свой аргумент. Если товаров больше нет, возвращается ноль.

Вы вызываете next в seq для генерации следующего элемента и нового seq. Повторяйте до тех пор, пока nil не будет получено.

Радость Clojure называет это "простым API для навигации по коллекциям" и говорит, что "seq - это любой объект, который реализует API seq" - что правильно, если "API" является ансамблем "вещи" (определенного типа). и функции, которые работают на эту вещь. Это зависит от подходящего сдвига в концепции API.

Примечание по особому случаю пустой последовательности:

(def empty-seq (rest (seq [:x])))

(type? empty-seq)                 ;=> clojure.lang.PersistentList$EmptyList

(nil? empty-seq)                  ;=> false ... empty seq is not nil
(some? empty-seq)                 ;=> true ("true if x is not nil, false otherwise.")

(first empty-seq)                 ;=> nil   ... first of empty seq is nil ("does not exist"); beware confusing this with a nil in a nonempty list!
(next empty-seq)                  ;=> nil   ... "next" of empty seq is nil
(rest empty-seq)                  ;=> ()    ... "rest" of empty seq is the empty seq
   (type (rest empty-seq))        ;=> clojure.lang.PersistentList$EmptyList
   (seq? (rest empty-seq))        ;=> true
   (= (rest empty-seq) empty-seq) ;=> true

(count empty-seq)                 ;=> 0
(empty? empty-seq)                ;=> true

Addenda

Функция seq

Если вы примените функцию seq к тому, для чего это имеет смысл (обычно это последовательная коллекция), вы получите последовательность, представляющую/генерирующую элементы этой коллекции.

Строка документа говорит:

Возвращает последовательность в коллекции. Если коллекция пуста, возвращает ноль. (seq nil) возвращает ноль. seq также работает на Strings, родной Java массивы (ссылочных типов) и любые объекты, которые реализуют Iterable. Обратите внимание, что значения кэша seqs, таким образом, seq не должен использоваться ни на каких Итерируемый, итератор которого многократно возвращает один и тот же изменяемый объект.

После применения seq вы можете получить объекты различных реальных классов:

  • clojure.lang.Cons - попробуйте (class (seq (map #(* % 2) '( 1 2 3))))
  • clojure.lang.PersistentList
  • clojure.lang.APersistentMap$KeySeq
  • clojure.lang.PersistentList$EmptyList
  • clojure.lang.PersistentHashMap$NodeSeq
  • clojure.lang.PersistentQueue$Seq
  • clojure.lang.PersistentVector$ChunkedSeq

Если вы примените seq к последовательности, фактический класс возвращаемой вещи может отличаться от фактического класса передаваемой вещи. Это все равно будет последовательность.

Каковы "элементы" в последовательности, зависит. Например, для карт они представляют собой пары ключ-значение, которые выглядят как 2-элементные vector (но их фактический класс не является вектором).

Функция lazy-seq

Создает объект для ленивого создания большего количества объектов (приостановленный компьютер, приостановленный поток, thunk)

Строка документа говорит:

Принимает тело выражений, которое возвращает ISeq или nil, и возвращает Seqable объект, который будет вызывать тело только в первый раз, когда seq вызывается, и кеширует результат и возвращает его на всех последующих звонки. Смотрите также - поняли? "

Заметка о "функциях" и "вещах"... и "объектах"

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

В чем разница между функцией и вещью?

Это жидкость! Некоторые вещи - это чистые функции, некоторые вещи - чистые вещи, некоторые между ними (могут использоваться как функции и имеют атрибуты вещей)

В частности, Clojure допускает контексты, в которых ключевые слова (вещи) рассматриваются как функции (для поиска значений в картах) или когда интерпретируются карты (вещи) как функции, или сокращение для функций (которые берут ключ и возвращают значение, связанное с этим. введите карту)

Очевидно, что функции - это вещи, поскольку они являются "первоклассными гражданами".

Это также контекстно! В некоторых случаях функция становится вещью, или вещь становится функцией.

Есть неприятные упоминания об объектах... это осколки, выскакивающие из базовой вселенной Java.

В целях презентации, схема коллекций

Collections in Clojure