1. Functions
1.1. Calling Functions
(+ 1 2 3 4)
(* 1 2 3 4)
(first [1 2 3 4])
Clojure表达式语法:开括号,运算符,操作数,右括号。 “函数调用”只是另一个术语表达式,其中的运算符是一个函数表达式。函数表达式只是返回一个函数的表达式。 它可能不是很明显,但是这可以让你写出可爱有趣的代码。这里有一个函数表达式返回的+(加)函数:
((or + -) 1 2 3)
; => 6
e.g. valid function calls which return 6:
;; Return value of "and" is first falsey value or last truthy value.
;; + is the last truthy value
((and (= 1 1) +) 1 2 3)
;; Return value of "first" is the first element in a sequence
((first [+ 0]) 1 2 3)
these aren't valid function calls:;; Numbers aren't functions
(1 2 3 4)
;; Neither are strings
("test" 1 2 3)
1.2. Function Calls, Macro Calls, and Special Forms
在上一节中,了解到,函数调用是其中有一个函数表达式的运算符的表达式。还有另外两种类型的表达式:macro 调用和 special forms。例如(def failed-movie-titles ["Gone With the Moving Air" "Swellfellas"])
(if (= severity :mild)
(def error-message (str error-message "MILDLY INCONVENIENCED!"))
(def error-message (str error-message "DOOOOOOOMED!")))
这使得special forms的“special”的是,他们不像函数调用那样,他并不总是evaluate所有的操作数。
例如,if的一般结构
(if boolean-form
then-form
optional-else-form)
加入有个这样的if语句
(if good-mood
(tweet walking-on-sunshine-lyrics)
(tweet mopey-country-song-lyrics))
13. Defining Functions
5个部分
[1] (defn name 函数名
[2] "description" 函数描述 (可选)
[3] {metadata} 元数据 (可选)
[4] [arguments] 参数列表
[5] body-of-expressions...) 函数体
例子:
(defn too-enthusiastic
"Return a cheer that might be a bit too enthusiastic"
[name]
(str "OH. MY. GOD! " name " YOU ARE MOST DEFINITELY LIKE THE BEST "
"MAN SLASH WOMAN EVER I LOVE YOU AND WE SHOULD RUN AWAY TO SOMEWHERE"))
(too-enthusiastic "Zelda")
; => "OH. MY. GOD! Zelda YOU ARE MOST DEFINITELY LIKE THE BEST MAN SLASH WOMAN EVER I LO
1.3.1. The Docstring
您可以在REPL中(doc fn-name)
e.g. (doc map)
查看该文档字符串。如果使用工具来生成代码文档的话文档字符串也被利用上面的例子中, "Return a cheer that might be a bit too enthusiastic"
就是文档字符串
3.3.2. Parameters
可以有一个,多个或零个参数
(defn no-params
[]
"I take no parameters!")
(defn one-param
[x]
(str "I take one param: " x " It'd better be a string!"))
(defn two-params
[x y]
(str "Two parameters! That's nothing! Pah! I will smoosh them "
"together to spite you! " x y))
function可以由arity重载,这意味着不同的函数体将取决于参数传递给函数的数目运行。 下面有一个多元数函数定义的一般形式。请注意,每个arity的定义是用括号括起来,并有一个参数列表:
(defn multi-arity
;; 3-arity arguments and body
([first-arg second-arg third-arg]
(do-things first-arg second-arg third-arg))
;; 2-arity arguments and body
([first-arg second-arg]
(do-things first-arg second-arg))
;; 1-arity arguments and body
([first-arg]
(do-things first-arg)))
元数arity重载是为参数提供默认值的其中一个方法。在这种情况下,"karate"
是chop-type
参数的默认参数:
(defn x-chop
"Describe the kind of chop you're inflicting on someone"
([name chop-type]
(str "I " chop-type " chop " name "! Take that!"))
([name]
(x-chop name "karate")))
如果用两个参数调用 x-chop
, 那么该函数就像它不是一个多元数函数一样工作:
(x-chop "Kanye West" "slap")
; => "I slap chop Kanye West! Take that!"
如果是用一个参数调用x-chop,那么函数实际上将调用自身的第二个参数“karate”:
(x-chop "Kanye East")
; => "I karate chop Kanye East! Take that!"
1.3.3. Destructuring解构
destructuring的基本思想是在集合里可以清楚的 bind symbols to values.例如:
;; Return the first element of a collection
(defn my-first
[[first-thing]] ; Notice that first-thing is within a vector
first-thing)
(my-first ["oven" "bike" "waraxe"])
; => "oven"
若没有解构的话,就要这样做:
(defn my-other-first
[collection]
(first collection))
(my-other-first ["nickel" "hair"])
; => "nickel"
当解构一个vector or list,可以you can name as many elements as you want and also use rest params:(defn chooser
[[first-choice second-choice & unimportant-choices]]
(println (str "Your first choice is: " first-choice))
(println (str "Your second choice is: " second-choice))
(println (str "We're ignoring the rest of your choices. "
"Here they are in case you need to cry over them: "
(clojure.string/join ", " unimportant-choices))))
(chooser ["Marmalade", "Handsome Jack", "Pigpen", "Aquaman"])
; =>
; Your first choice is: Marmalade
; Your second choice is: Handsome Jack
; We're ignoring the rest of your choices. Here they are in case \
; you need to cry over them: Pigpen, Aquaman
结构一个maps
(defn announce-treasure-location
[{lat :lat lng :lng}]
(println (str "Treasure lat: " lat))
(println (str "Treasure lng: " lng)))
(announce-treasure-location {:lat 28.22 :lng 81.33})
; =>
; Treasure lat: 28.22
; Treasure lng: 81.33
更简洁的写法是:
;; Works the same as above.
(defn announce-treasure-location
[{:keys [lat lng]}]
(println (str "Treasure lat: " lat))
(println (str "Treasure lng: " lng)))
你可以保留访问原始map参数使用 :as
关键字。在下面的例子中, 原始map的key用 treasure-location
:
;; Works the same as above.
(defn receive-treasure-location
[{:keys [lat lng] :as treasure-location}]
(println (str "Treasure lat: " lat))
(println (str "Treasure lng: " lng))
;; One would assume that this would put in new coordinates for your ship
(steer-ship! treasure-location))
1.3.4. Function body
函数体可以包含任何形式。 Clojure的自动返回最后计算的形式:
(defn illustrative-function
[]
(+ 1 304)
30
"joe")
(illustrative-function)
; => "joe"
(defn number-comment
[x]
(if (> x 6)
"Oh my gosh! What a big number!"
"That number's OK, I guess"))
(number-comment 5)
; => "That number's OK, I guess"
(number-comment 7)
; => "Oh my gosh! What a big number!"
1.3.5. All Functions are Created Equal
即所有都是函数
函数式头等公民
1.4. Anonymous Functions匿名函数
在Clojure中,你的函数不必有名称。事实上,使用匿名函数就会找到自己。
两种方法来创建匿名函数。第一种方法是使用fn形式:
;; This looks a lot like defn, doesn't it?
(fn [param-list]
function body)
;; Example
(map (fn [name] (str "Hi, " name))
["Darth Vader" "Mr. Magoo"])
; => ("Hi, Darth Vader" "Hi, Mr. Magoo")
;; Another example
((fn [x] (* x 3)) 8)
; => 24
(def my-special-multiplier (fn [x] (* x 3)))
(my-special-multiplier 12)
; => 36
还有另一种更简洁的方式来创建匿名函数:;; Whoa this looks weird.
#(* % 3)
;; Apply this weird looking thing
(#(* % 3) 8)
; => 24
;; Another example
(map #(str "Hi, " %)
["Darth Vader" "Mr. Magoo"])
; => ("Hi, Darth Vader" "Hi, Mr. Magoo")
但是,这种方式 最好别用
接下来
%,可以表示传递给函数的参数。如果您的匿名函数有多个参数,则可以区分它们是这样的:%1,%2,%3,等等。%相当于%1:
(#(str %1 " and " %2) "corn bread" "butter beans")
; => "corn bread and butter beans"
(#(identity %&) 1 "blarg" :yip)
; => (1 "blarg" :yip)
这种形式和fn之间的主要区别是,这种形式很不可读,最适合用于短功能。1.5. Returning Functions
函数可以返回其他函数。返回的函数都是闭包,这意味着它们可以访问所有的都在创建函数时范围内的变量。
下面是一个标准的例子:;; inc-by is in scope, so the returned function has access to it even
;; when the returned function is used outside inc-maker
(defn inc-maker
"Create a custom incrementor"
[inc-by]
#(+ % inc-by))
(def inc3 (inc-maker 3))
(inc3 7)
; => 10