Clojure: идиоматический способ вызова содержит? на ленивой последовательности

Есть ли идиоматический способ определения, содержит ли LazySeq элемент? По состоянию на Clojure 1.5 вызов contains? создает исключение IllegalArgumentException:

IllegalArgumentException contains? not supported on type: clojure.lang.LazySeq      
clojure.lang.RT.contains (RT.java:724)

До 1.5, насколько я знаю, всегда возвращалось false.

Я знаю это призвание contains? на LazySeq может никогда не вернуться, поскольку он может быть бесконечным. Но что, если я знаю, что это не так, и мне все равно, оценит ли это с нетерпением?

Я придумал:

(defn lazy-contains? [col key]
  (not (empty? (filter #(= key %) col))))

Но это не совсем правильно. Есть ли способ лучше?

2 ответа

Решение

Во-первых, ленивые seqs не эффективны для проверки членства. Попробуйте использовать набор вместо ленивых seq.

Если набор непрактичен, ваше решение не плохое. Пара возможных улучшений:

  1. "Не пусто" немного неловко. Простого использования seq достаточно, чтобы получить значение, равное нулю или истине, которое ваши пользователи могут использовать в if. Вы можете заключить это в логическое значение, если хотите true или false.

  2. Так как вы заботитесь только о первом совпадении, вы можете использовать некоторые вместо filter и seq.

  3. Удобный способ написать предикат равенства - это буквальный набор, такой как #{key}, хотя, если key равен nil, он всегда будет возвращать nil, если найден nil, а не наш.

Все вместе, что дает вам:

(defn lazy-contains? [col key]
  (some #{key} col))

Если вы используете some вместо filter как в вашем примере, вы получите немедленный возврат, как только будет найдено значение, вместо принудительной оценки всей последовательности.

(defn lazy-contains? [coll key]
  (boolean (some #(= % key) coll)))

Изменить: Если вы не приведете результат к логическому значению, обратите внимание, что вы получите nil вместо false если ключ не найден.

Другие вопросы по тегам