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))))
Но это не совсем правильно. Есть ли лучший способ?
Ответы
Ответ 1
Во-первых, ленивые секвенции не эффективны для проверки членства. Рассмотрите возможность использования набора вместо ленивого seq.
Если набор непрактичен, ваше решение неплохое. Несколько возможных улучшений:
-
"Не пустой" немного неудобно. Просто использовать seq достаточно, чтобы получить значение nil-or-truthy, которое ваши пользователи могут использовать в if.You может обернуть это в boolean, если вы хотите true или false.
-
Поскольку вы только заботитесь о первом совпадении, вы можете использовать некоторые вместо фильтра и seq.
-
Удобный способ написать предикат равенства - это литеральный набор, такой как # {key}, хотя если ключ равен нулю, он всегда будет возвращать нуль, независимо от того, найден ли nil наш.
Все вместе, что дает вам:
(defn lazy-contains? [col key]
(some #{key} col))
Ответ 2
Если вы используете some
вместо filter
, как в вашем примере, вы получите немедленное возвращение, как только будет найдено значение, а не для принудительной оценки всей последовательности.
(defn lazy-contains? [coll key]
(boolean (some #(= % key) coll)))
Изменить. Если вы не принуждаете результат к логическому, обратите внимание, что вместо false
вы получите nil
, если ключ не найден.