Clojure是一门函数式编程语言,function具有非常重要的地位。

Function Call

函数调用很简单,我们已经见过了很多例子:

(+ 1 2 3)
(* 1 2 3)

我们也可以将函数返回让外面使用:

user=> ((or + -) 1 2 3)
6

如果我们调用了非法的函数,会报错:

user=> (1 2 3)

ClassCastException java.lang.Long cannot be cast to clojure.lang.IFn  user/eval1491 (form-init7840101683239336203.clj:1)

这里,1并不是一个合法的operator。

Function Define

一个函数,通常由几个部分组成:

  • defn
  • 函数名称
  • 函数说明(可选)
  • 参数列表,使用[]封装
  • 函数body

一个例子:

user=> (defn fun1
  #_=> "a function example"
  #_=> [name]
  #_=> (str "hello " name))
#'user/fun1
user=> (fun1 "world")
"hello world"
user=> (doc fun1)
-------------------------
user/fun1
([name])
  a function example
nil

Overloading

我们也可以进行函数重载,如下:

user=> (defn func-multi
  #_=> ([](str "no arg"))
  #_=> ([name1](str "arg " name1))
  #_=> ([name1 name2](str "arg " name1 " " name2)))
user=> (func-multi)
"no arg"
user=> (func-multi "1")
"arg 1"
user=> (func-multi "1" "2")
"arg 1 2"

Variable arguments

我们使用&来支持可变参数

user=> (defn func-args
  #_=> [name & names]
  #_=> (str name " " (clojure.string/join " " names)))
#'user/func-args
user=> (func-args "a")
"a "
user=> (func-args "a" "b")
"a b"
user=> (func-args "a" "b" "c")
"a b c"

Destructuring

我们可以在函数参数里面通过特定的name来获取对应的collection的数据,譬如:

user=> (defn f-vec1 [[a]] [a])
#'user/f-vec1
user=> (f-vec1 [1 2 3])
[1]
user=> (defn f-vec2 [[a b]] [a b])
#'user/f-vec2
user=> (f-vec2 [1 2 3])
[1 2]

在上面的例子里面,我们的参数是一个vector,然后[a]以及[a b]表示,我们需要获取这个vector里面的第一个以及第二个数据,并且使用变量a,b存储。

我们也可以使用map,譬如:

user=> (defn f-map [{a :a b :b}] [a b])
#'user/f-map
user=> (f-map {:a 1 :b 2})
[1 2]
user=> (f-map {:a 1 :c 2})
[1 nil]

上面这个例子我们可以用:keys来简化,

user=> (defn f-map-2 [{:keys [a b]}] [a b])
#'user/f-map-2
user=> (f-map-2 {:a 1 :b 2 :c 3})
[1 2]

我们可以通过:as来获取原始的map:

user=> (defn f-map-3 [{:keys [a b] :as m}] [a b (:c m)])
#'user/f-map-3
user=> (f-map-3 {:a 1 :b 2 :c 3})
[1 2 3]

Body

Function的body里面可以包括多个form,Clojure会将最后一个form执行的值作为该函数的返回值:

user=> (defn func-body []
  #_=> (+ 1 2)
  #_=> (+ 2 3))
#'user/func-body
user=> (func-body)
5

匿名函数

我们可以通过fn来声明一个匿名函数

user=> ((fn [name]  (str "hello " name)) "world")
"hello world"

我们也可以通过def来给一个匿名函数设置名称:

user=> (def a (fn [name] (str "hello " name)))
#'user/a
user=> (a "world")
"hello world"

当然,我们更加简化匿名函数,如下:

user=> #(str "hello " %)
#object[user$eval1584$fn__1585 0x72445715 "user$eval1584$fn__1585@72445715"]
user=> (#(str "hello " %) "world")
"hello world"

我们使用%来表明匿名函数的参数,如果有多个参数,则使用%1%2来获取,使用%&来获取可变参数。

user=> (#(str "hello " %) "world")
"hello world"
user=> (#(str "hello " %1 " " %2) "world" "a ha")
"hello world a ha"

user=> (#(str "hello " %1 " " (clojure.string/join " " %&)) "world" "ha" "ha")
"hello world ha ha"