Почему "(def vowel? (set" aeiou "))" работает?

Я смотрю отличный Clojure учебник здесь. В одном из примеров он имеет код Clojure в следующих строках:

(def vowel? (set "aeiou"))

Это заставляет гласные возвращать true для гласных и false для согласных:

(vowel? (first "abc")) ; => true
(vowel? (first "cba")) ; => false

Почему это? Я предполагаю, что это имеет какое-то отношение к знаку вопроса за именем переменной. Не удалось найти ничего в учебнике...


Изменить Я понял: vowel? не возвращает true или false, а сам элемент или nil. См. Мой собственный ответ.

Ответы

Ответ 1

Это совершенно аналогично тому, как работают карты (наиболее естественные объекты в Clojure). Когда карта вызывается как функция, она работает как отображение:

user=> (def ob {:foo "bar", :bar :baz, :qwerty 42})
#'user/ob
user=> (ob :foo)
"bar"

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

user=> (:bar ob)
:baz

и даже то же самое с наборами ключевых слов:

user=> (def vowel-keywords (set [:a :e :i :o :u]))
#'user/vowel-keywords
user=> (:a vowel-keywords)
:a
user=> (:b vowel-keywords)
nil

Но опять же этот последний трюк работает только с ключевыми словами, а не с чем-либо еще, что вы могли бы использовать в качестве ключей в сопоставлении или в членах набора.

Ответ 2

Ага! В итоге я понял это. Он фактически не возвращает true или false, скорее возвращает первое вхождение в наборе, или nil, если это не происходит.

И потому, что вы можете использовать это как условие (nil обрабатывается как false и non-nil как true), это работает как хороший небольшой взлом для проверки, содержит ли строка письмо.

(vowel? (first "abc")) ; => "a"
(vowel? (first "cba")) ; => nil

(if (vowel? (first "abc"))
       (println "yay")
       (println "oops"))  ; => "yay"