何谓函数式编程:
- 操作不可变的值
- 使用简单抽象的不可变数据结构,而不是有可变状态的数据结构。
- 把函数本身当做值的一种,从而可以使用高阶函数
- 对数据进行声明式的处理,而不是命令式的控制,遍历
- 对函数进行递增式的组合,使用高阶函数以及不可变数据结构,在更高的抽象级别来解决复杂的问题。
Java某些使用了函数式编程的例子:google的guava,Function Java类库等。
REPL定义:
- 读入(Read)
- 求值(Eval)
- 输出(Print)
- 循环(Loop)
Clojure表达式调用规则
- 列表里面的第一个值是操作符,其余的都是这个操作符的参数。
apply函数
- 它以提供给他的集合参数来调用传给它的参数;比如(apply + [a b c])调用和(+ a b c) 是一样的。
Clojure命名空间
- 当前命名空间 两个冒号开头(::)
- 特定命名空间 两个冒号开头同时包含/ 。 (::alias/key)
命名空间定义与查询
- ns关键字
ns 查询当前命名空间 (ns foo) 创建命名空间 - 每个命名空间都会默认引入Clojure核心库clojure.core中所有的var。
符号(关键字)
- 符号不能以数字开头,但是可以为特殊字符。
注释
- 单行注释以分号开头
- 形式级别的注释 #宏 优点是可以加入调试信息,告诉reader忽略下一个Clojure形式(表达式). 类似于comment函数,但是comment函数回存在始终为nil的返回值,可能抛出不必要的异常。
同像性
- 代码是由Clojure自身的数据结构:原子值(字符串、数字等)和集合的字面量来表示的。“代码即数据”
值的重要性
- 个人理解
类似于避免引用传递修改值,避免修改堆内存。基于栈。Clojure提供的数据结构是不可变的,而且是高效的。不用担心使用复杂的锁机制而使代码变得复杂而低效。
什么叫做函数
- 函数其实就是包号了一些数据的Clojure列表。
- 函数的本身就是值(高阶函数),这样就可以将它作为参数传递给其它函数,同时也可以把函数作为返回值。
- 函数在Clojure里面是头等公民。
在Java中,代码只能存放于方法里面,方法必须存放于类里面,而且由于Java的反射机制,方法本身不能被当做一个变量或者值来直接引用。
;举个例子
;实现一个函数,要求可以调用其它函数两次,可以传入任何函数,任何参数
;Clojure实现
user=> (defn call-twice [f x]
#_=> (f x)
#_=> (f x))
#'user/call-twice
user=> (call-twice println 123)
123
123
介绍几个基本函数
- map函数 > 把一个或多个顺序集合转换成另外一个顺序集合的最基本的高阶函数。 > 同时map返回的序列是惰性的。
user=> (map clojure.string/lower-case ["Clojure" "Java"])
("clojure" "java")
user=> (map + [1 2 3] [4 5 6])
(5 7 9)
- reduce函数 > 应用一个函数把一个集合里的值转换成单个值的过程,叫做归约。
user=> (reduce max [0 -3 10 48])
48
;相当于
user=> (max (max (max 0 -3) 10) 48)
48
- 函数应用apply
函数应用是不同于普通的函数的调用,给定一个函数, 以及要传给这个函数的参数序列,然后来调用这个函数的过程叫做函数调用。 支持调用的函数和参数是运行时才能确定的。
;给一个确定参数,和不定参数的例子
;apply函数: apply 把给定的集合里面的所有元素一次性地给指定的函数作为参数调用,
;然后返回这个函数的返回值。可以把apply看作是SQL里面的聚合函数,如下:
user=> (def args [1 2 3 4])
#'user/args
user=> (apply * 10 2 args)
480
- 偏函数应用 Partial
局部套用的定义:通过将参数封装到闭包中来把一个函数的参数变少的过程。允许我们创建新的、自定义 的函数而不需要显示的写出定义。
把函数fa的一部分参数传给一个函数fa,这样创建一个新的函数fb, 这个函数fb需要的参数就是你没有传给那个函数fa的那些剩余参数。 相当于给fa固定了一些参数,剩下的参数往fb传即可。
在Clojure中,任何函数通过partial都可以被局部调用,partial以函数作为第一个参数,后面还可以跟任意个额外的参数。 它的功能和第一个参数函数的功能类似,但是却只需要更少的参数。它将partial的额外参数也作为自己的参数。
;偏函数partial: 形如 ((partial f arg1 arg2 .. argn) arga argb .. argz)
;就是执行: (f arg1 arg2 .. argn arga argb .. argz)
;注意:偏函数的第一个参数是一个函数,后面至少有1个其他参数
;partial函数称为“偏函数”或者“部分完整函数”,因为它是不完整的,定义也用def而不是defn。
user=> (defn f [n] (* n 10));正常函数
#'user/f
user=> (f 2)
20
user=> (def fp (partial * 10));偏函数
#'user/fp
user=> (fp 2)
20
- 函数组合 comp
对于给定的任意个数的函数,他们会被组成一个新的函数,所有传给这个新函数的参数会用来调用每个函数, 每个函数的返回值会作为参数来调用下一个函数,调用顺序通常与传入的顺序相反。
;组合函数comp: 形如 ((comp f1 f2 .. fn) arg1 arg2 .. argn)
;就是对参数从右到左组合执行所有函数,可以转变为: (f1 (f2 (.. (fn arg1 arg2 .. argn))))
;举例如下:
;例如以下函数
user=> (defn negated-sum-str [& numbers]
(str (- (apply + num )))
(str (- (apply + numbers ))))
#'user/negated-sum-str
user=> (negated-sum-str 10 20 30)
"-60"
;comp实现
(def negated-sum-str (comp str - +))
- constantly函数
;constantly函数: constantly函数接受一个参数x,
;并返回一个变参函数,该变参函数无论参数是什么,都返回这个x值。
user=> (def consf (constantly "a"))
#'user/consf
user=> (consf 1 2 3)
"a"
user=> (consf "a")
"a"
user=> (consf [1 2 3])
"a"
- 高阶函数
把函数作为返回值或者接受函数作为参数。
(defn adder
[n]
(fn [x] (+ n x))) ;返回值是一个函数
((adder 5) 18)
;=23
日志系统库tools.logging库
纯函数
不依赖于外部的数据源,也不会改变任何外部的环境状态,对于相同的参数,始终返回相同的结果。
- 纯函数更容易理解。
- 纯函数更容易测试。
- 纯函数的结果是可以被缓存的,并且很容易并行化。(内存化)
原文链接:[Clojure学习笔记]Clojure基础知识,转载请注明来源!