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.
Если набор непрактичен, ваше решение не плохое. Пара возможных улучшений:
"Не пусто" немного неловко. Простого использования seq достаточно, чтобы получить значение, равное нулю или истине, которое ваши пользователи могут использовать в if. Вы можете заключить это в логическое значение, если хотите true или false.
Так как вы заботитесь только о первом совпадении, вы можете использовать некоторые вместо filter и seq.
Удобный способ написать предикат равенства - это буквальный набор, такой как #{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
если ключ не найден.