Почему в качестве предиката лучше предпочесть seq непустому?

Строка документации для empty? гласит: «Пожалуйста, используйте идиому (seq x) вместо (not (пусто? x))». MisterMetaphor указывает, что использование seq в качестве предиката может иметь смысл при использовании в if-let:

(if-let [s (seq might-be-empty)]
   (fn-for-non-empty-seq s)
   (fn-for-empty-seq))

Должен ли я действительно использовать seq для проверки непустоты в целом? seq может преобразовать свой аргумент в другую форму. Например:

user=> (class (lazy-seq '(1 2 3)))
clojure.lang.LazySeq
user=> (class (seq (lazy-seq '(1 2 3))))
clojure.lang.PersistentList
user=> (class (map inc [1 2 3]))
clojure.lang.LazySeq
user=> (class (seq (map inc [1 2 3])))
clojure.lang.ChunkedCons

Это кажется пустой тратой циклов, если вы просто хотите проверить непустоту и не нуждаетесь в новой привязке или если мне не нужно преобразование перед привязкой. Разве not-empty не будет лучшим выбором в таких случаях? Он возвращает nil, если его аргумент пуст, и его аргумент остается неизменным, если он не пуст.

(if (not-empty [])
  "more to do"
  "all done")

person Mars    schedule 06.02.2014    source источник


Ответы (1)


Во-первых, проверьте определение empty?:

(defn empty?
  "Returns true if coll has no items - same as (not (seq coll)).
  Please use the idiom (seq x) rather than (not (empty? x))"
  {:added "1.0"
  :static true}
  [coll] (not (seq coll)))

Итак, empty? равно (not (seq coll)). Вот почему не рекомендуется дополнять empty?, потому что вы выполняете двойное отрицание seq.

Теперь см. not-empty

(defn not-empty
  "If coll is empty, returns nil, else coll"
  {:added "1.0"
   :static true}
  [coll] (when (seq coll) coll))

Удивительно, но и seq используется как тест на непустоту. Это not-empty полезно, если тип имеет значение — nth производительность для векторов, conj поведение и т. д., — потому что он возвращает непустые столбцы как есть. Однако, если использовать его только как предикат, это будет только обертка seq.

Однако не беспокойтесь о том, что seq будет деспотичным. Он делает немного больше, чем возвращает итератор. На самом деле он не преобразует всю структуру данных.

person A. Webb    schedule 06.02.2014
comment
Спасибо. Удивительно и удивительно, что любые дополнительные затраты (возврат итератора) должны быть оплачены за операцию, используемую так часто. Или, скорее, должно быть так, что на самом деле это вообще ничего не стоит, что опять же завораживает. - person Mars; 06.02.2014
comment
Я очень надеюсь, что у JIT-компилятора нет проблем с оптимизацией двойного отрицания. - person remcycles; 19.06.2018
comment
@ remicles2 Я не могу представить, чтобы оптимизация в динамически типизированных языках с ленивыми фрагментами была настолько горячей. Есть ли документ, в котором говорится, сколько работы может выполнить JIT, прежде чем он столкнется с каким-либо серьезным препятствием? - person David Tonhofer; 18.05.2019