(+ 1 2 3 4) (* 1 2 3 4) (first [1 2 3 4])
Clojure表达式语法:开括号,运算符,操作数,右括号。 “函数调用”只是另一个术语表达式,其中的运算符是一个函数表达式。函数表达式只是返回一个函数的表达式。 ((or + -) 1 2 3) ; => 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)
(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 boolean-form then-form optional-else-form)
加入有个这样的if语句
(if good-mood (tweet walking-on-sunshine-lyrics) (tweet mopey-country-song-lyrics))
13. Defining Functions [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
您可以在REPL中(doc fn-name)
e.g. (doc map)
查看该文档字符串。如果使用工具来生成代码文档的话文档字符串也被利用上面的例子中, "Return a cheer that might be a bit too enthusiastic"
就是文档字符串
可以有一个,多个或零个参数
(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重载,这意味着不同的函数体将取决于参数传递给函数的数目运行。 (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!"
;; 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))
(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!"
在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")
但是,这种方式 最好别用
(#(str %1 " and " %2) "corn bread" "butter beans") ; => "corn bread and butter beans"
(#(identity %&) 1 "blarg" :yip) ; => (1 "blarg" :yip)
这种形式和fn之间的主要区别是,这种形式很不可读,最适合用于短功能。函数可以返回其他函数。返回的函数都是闭包,这意味着它们可以访问所有的都在创建函数时范围内的变量。
下面是一个标准的例子:;; 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