匿名函数用 fn 创建匿名函数。
● 这是一个很简短且不言自明的函数,如果给它取名字的话,不会令可读性增强,反而使得代码更难以阅读。
● 这是一个仅在别的函数内部使用的函数,需要的是局部名称,而非顶级绑定。
● 这个函数是在别的函数中被创建的,其目的是为了隐藏某些数据。
用作过滤器的函数总是简短且不言自明的。
src/examples/exploring.clj
(defn indexable-word? [word]
(> (count word) 2))
(require '[clojure.string :as str])
(filter indexable-word? (str/split "A fine day it is" #"\W+"))
-> ("fine" "day")
匿名函数能让你仅用一行代码就做到相同的事情。
最简单的匿名函数形式fn。
(fn [params*] body)
通过这种形式,能在调用filter时直接插入indexable-word?的实现。
(filter (fn [w] (> (count w) 2)) (str/split "A fine day" #"\W+"))
-> ("fine" "day")
还有一种采用隐式参数名称,也更加简短的匿名函数语法。其参数被命名为%1、%2,以此类推。对于第一个参数,你也可以使用%表示。
#(body)
可以使用这种更简短的匿名形式来重新调用filter。
(filter #(> (count %) 2) (str/split "A fine day it is" #"\W+"))
-> ("fine" "day")
使用匿名函数的第二个动机是,确定想要一个具名函数,但该函数仅在其他函数的作用域内使用。
src/examples/exploring.clj
(defn indexable-words [text]
(let [indexable-word? (fn [w] (> (count w) 2))]
(filter indexable-word? (str/split text #"\W+"))))
let将那个匿名函数与名称indexable-word?绑定在了一起,但这次它被限定在了indexable-words的词法作用域内。
(indexable-words "a fine day it is")
-> ("fine" "day")
采用这种let和匿名函数的组合,相当于对代码的读者说:“函数indexable-word?有足够的理由拥有一个名称,但仅限于在indexable-words中。”
使用匿名函数的第三个原因是,需要在运行期动态创建一个函数。
src/examples/exploring.clj
(defn make-greeter [greeting-prefix]
(fn [username] (str greeting-prefix ", " username)))
为一个通过 fn 得到的函数命名是没有意义的,因为每次调用make-greeter都会创建一个不同的函数。
可以使用 def 对 make-greeter创建的函数进行命名。
(def hello-greeting (make-greeter "Hello"))
-> #'user/hello-greeting
(def aloha-greeting (make-greeter "Aloha"))
-> #'user/aloha-greeting
可以像调用其它函数那样调用这些函数了。
(hello-greeting "world")
-> "Hello, world"
(aloha-greeting "world")
-> "Aloha, world"
没有必要为每个问候函数都命名。可以简单地创建一个问候函数,并将它放置到列表形式的第一个(函数)槽(slot)中。
((make-greeter "Howdy") "pardner")
-> "Howdy, pardner"
不同的问候函数会记住创建它们时的那个greeting-prefix值。用更为正式的说法,这些问候函数对greeting-prefix的值构成了闭包(closures)。
何时使用匿名函数匿名函数只是一种选择,而非必须。只有令代码更具可读性时,才应该使用这样的匿名形式。