Clojure小教程
2016-05-30 00:07
225 查看
什么是函数式编程?
Clojure的简易语法
特点1:函数是一等公民,即与其他基本类型处于等价地位,可以被返回,可以被赋值也可以作为参数.python js clojure都支持这点. 特点2:更多的表达式,减少过程.即每个语句尽量都是计算并返回计算结果.所有的函数都要有返回值(可以为nil). 特点3:没有副作用(side-effect),即函数就是单纯的执行计算,不改变外部变量.(改变外部变量最简单的就是修改一个全局变量,但这在函数式编程是不允许的) 特点4:不修改变量.即不改变参数与全局变量,这也意味着函数的运行状态不能用变量(对象)保存.java可以通过传递值引用,c可以通过指针 来改变函数参数实际调用值的状态,这在函数式编程中是不允许的,函数式编程使用参数来保存状态(参数传递所有的运行参数),最好的例子就是递归. (defn sum [x s] (if (pos? x) (recur (dec x) (+ s x)) s));每次运行时都把当前状态传递进去,作为函数的必要运行信息. 特点5:引用透明(Referential transparency),指的是函数的运行不依赖于外部变量或"状态",只依赖于输入的参数,任何时候只要参数相同,引用函数所得到的返回值总是相同的。 优势1:函数式编程不需要考虑"死锁"(deadlock),因为它不修改变量,所以根本不存在"锁"线程的问题。 优势2:函数式编程没有副作用,只要保证接口不变,内部实现是外部无关的。所以,可以在运行状态下直接升级代码.即函数的独立性高.
Clojure的简易语法
* 基本类型:整型,浮点,有理数(分数),字符串,字符(\a \A \u2014),符号,关键字 * 集合类型: list --> (1 2 3),一般第一个元素为函数或者宏,如果第一个为函数,则往后每个元素(可能为另外的list)依次计算后将值传给函数处理.可以用(list 1 2 3)生成. vector --> [1 2 3] map --> {"key1" 1,"key2" 2} set --> #{1 2 3 4} * 函数定义 1.普通的匿名形式 1.1(fn inner_add [x y] (+ x y)) 其中inner_add为内部别名不是真正的函数名称,可以不要. 1.2(fn inner_add ([x] x) ([x y] (+ x y)) 重载一个函数为多种实现,即将每个实现的参数(vector)与方法体(list)拿出来放到一个list中,实际调用时根据参数个数调用. 1.3(fn mul [& x] #{x}) 支持变长参数,&+"space"后的参数名为变长参数,将作为一个list传入函数定义中. eg:((fn mul [& x] #{x}) 1 2 3) --> 返回 #{(1 2 3)} 2.#()匿名函数形式,直接在#(),实现函数,参数应用分别为 %1 %2... eg (#(+ %1 %2) 2 3) --> 返回5 3.defn直接给函数取外部别名,直接替换上面的1中的fn即可. (defn outer_add [x y] (+ x y)) (outer_add 1 2) --> 3 4.def一般用于给一个值取别名(symbol),也可以用于匿名函数. (def my_add (fn [x y] (+ x y))) || (def my_add #(+ %1 %2)) * 符号赋值 (def x 1) (def y x) * 序列块:使用do将一些操作序列组成一个块,只有最后一个元素的值被返回 (do 1 (+ 1 2) (- 9 2) 3) --> 3 * 设定全局不变量(let [] ()) (let [x 10 y 100] (do (println x) (println y))); -->则xy的值在()中可以被使用,但不能被改变. * if与when (if (cond) (thenclause) (elseclause));cond必须返回bool值,若为true则执行thenclause,否则执行elseclause (when (cond) (sta1) (sta2) (sta3)) ;当cond为true时,执行所有的语句,默认支持do语句(sta1 ... sta3),但不存在分支else语句. * 使用recur尾递归实现循(recur只能进行尾递归) ;不用loop的写法,相当于在内部匿名函数后直接给出参数值. (defn px [line] ((fn [cur] (when (<= cur line) ((fn [col] (if (<= col cur) (do (print "*") (recur (inc col))) (println ""))) 1) (recur (inc cur))) ) 1)) ;loop写法,可以在loop的第一个参数给定循环的初始值 --> eg:(loop [x 1 y 2] (...)) ;loop循环等价于一个有初始参数的匿名函数调用 ;(loop [x 10] (when (pos? x) (println x) (recur (dec x)))) <==>((fn [x] (when (pos? x) (println x) (recur (dec x)))) 10) (defn py [line] (loop [cur 1] (when (<= cur line) (loop [col 1] (if (<= col cur) (do (print "*") (recur (inc col))) (println)) ) (recur (inc cur)) ))) * 非尾递归可以直接使用函数名调用 ;eg 汉诺塔,可以看到x的值在某个特定函数调用上,保持不变 (defn hanoi [x] (+ (+ (hanoi (dec x)) 1) (hanoi (dec x)))) * quote,用于保证一个list及其子list不被解析,()空list不用加quote. ;eg (quote (+ 1 (+ 2 3))) -->返回 (+ 1 (+ 2 3)) ;eg (quote (1 2)) --> 返回(1 2)不报错. 也可以使用'(1 2) '(+ 1 2) . 语法quote --> 使用` `(+ 1 2) 与quote不同,其会自动展开 (map even? [1 2 3]) ;=> (clojure.core/map clojure.core/even? [1 2 3]) * unquote(~),quote递归使得子list不解析,可以unquote某个子表达式,让它计算. `(+ 1 ~(* 2 3)) => (clojure.core/+ 1 6) 反quote拼接: (let [x '(2 3)] `(1 ~@x)) ;=> (1 2 3) ~@里的@,它告诉 Clojure,不要解开序列 x,将它拼装到最终的 list 里,而不是作为嵌套 list 插入。 * 与java互操作 1.静态域与静态方法ClassName/static_method_or_field eg:(println Math/PI) (println (Math/sqrt 9)) 2.创建实例(ClassName. ) eg:(java.util.HashMap. ["x" 1 "y" 2])或(new java.util.HashMap ["x" 1 "y" 2]) 3.调用对象方法或实例域(.method_or_field) eg: (.divide (java.math.BigDecimal. "42") 2M) (.x (java.awt.Point. 10 0)) --> 返回10 4.设置实例属性,如果未提供setter可以使用set! eg: (def point (java.awt.Point 10 10)) (defn setX [po xval] (set! (.x po) xval)) (setX point 15) (.x point) --> 返回15 5.链式调用支持,使用..宏 eg:new java.util.Date().toString().endsWith("2016")可以表示为 (.. (java.util.Date) toString (endsWith "2016")) 6.doto宏,用于设置一个java对象的多个实例域. eg: Date time = new Date(); time.setYear(1); time.setMonth(2); time.setDate(3);等价于 (doto (java.util.Date.) (.setYear 1) (.setMonth 2) (.setDate 3)) * 异常处理 1.抛出异常(throw (Exception. "I done throwed")) 2.异常捕获 eg: (defn throw-catch [f] (try (f) (catch ArithmeticException e "No dividing by zero!") (catch Exception e (str "You are so bad " (.getMessage e))) (finally (println "returning... ")))) 调用(throw-catch #(/ 10 2))将传递一个匿名函数作为throw-catch的参数. * 命名空间 1.Clojure命名空间的创建 (ns my.clojure) 2.引用其他命名空间(获得函数/宏定义),使用:require (ns my.core (:require my.clojure)) 3.只引用其他命名空间的部分函数使用:use eg: (ns my.util (:use [my.core :only [myfunc1 myfunc2]]));只导入myfunc1与myfunc2 (ns my.util (:use [my.core :exclude [myfunc1 myfunc2]]));只有myfunc1与myfunc2不导入 4.导入java类库.(java.lang.*默认导入) (import java.util.*)
相关文章推荐
- 14.python中的集合
- Problem-R
- 如何使用hugo搭建个人博客(一)
- Leetcode 81. Search in Rotated Sorted Array II 旋转数组查询2 解题报告
- C++类型转换函数(类型转换运算符函数)
- min,max和abs函数不同编译器不同头文件
- MySQL优化GROUP BY(松散索引扫描与紧凑索引扫描)
- Microsoft .Net Remoting系列教程之一:.Net Remoting基础篇
- Microsoft .Net Remoting系列教程之二:Marshal、Disconnect与生命周期以及跟踪服务
- Microsoft .Net Remoting系列教程之三:Remoting事件处理全接触
- ASP.NET中Webservice安全 实现访问权限控制
- ASP.NET中基于soaphead的webservice安全机制
- php实现的一段简单概率相关代码
- smarty中改进truncate使其支持中文的方法
- php求圆周率的简单实现方法 原创
- Yii2中如何使用modal弹窗(基本使用)
- php while循环控制的简单实例
- PHP如何实现跨域
- PHP将页面中点击数量高的链接进行高亮显示的方法
- 实战模拟监控MySQL服务shell脚本小结