几分钟内学习Clojure

开发 后端
Clojure是JVM上的一个LISP语言变种。它比Common Lisp更强调纯函数式编程,但提供了一些STM工具以处理它所引入的状态问题。

此处获取源码: test.clj

Clojure是JVM上的一个LISP语言变种。它比Common Lisp更强调纯函数式编程,但提供了一些STM工具以处理它所引入的状态问题。

这个组合使并行处理更加简单,并且通常是自动的。

(你需要Clojure 1.2或以上版本。

  1. ; 分号作为注释的开始  
  2.  
  3. ; Clojure 用一种把元素用括号括起来的像列表一样的方式来书写,元素之间用空格隔开  
  4. ; clojure 解释器会把第一个元素当做是函数或者宏调用,其他的都作为参数  
  5. ; 下面这个函数用于设置当前的命名空间  
  6. (ns test)  
  7.  
  8. ; 更多基本的例子:  
  9.  
  10. ; str 函数会用它所有的参数创造一个字符串  
  11. (str "Hello" " " "World") ; => "Hello World" 
  12.  
  13. ; 数学运算很直观,不过是前缀表达式  
  14. (+ 1 1) ; => 2  
  15. (- 2 1) ; => 1  
  16. (* 1 2) ; => 2  
  17. (/ 2 1) ; => 2  
  18.  
  19. ;  相等比较使用 “=”符号  
  20. (= 1 1) ; => true 
  21. (= 2 1) ; => false 
  22.  
  23. ; 你也不必担心逻辑运算  
  24. (not true) ; => false 
  25.  
  26. ; 嵌套方式正如你预料的那样  
  27. (+ 1 (- 3 2)) ; = 1 + (3 - 2) => 2  
  28.  
  29. ; 类型系统  
  30. ;;;;;;;;;;;;;  
  31.  
  32. ; Clojure 使用java对象类型来表示 布尔值、字符串和数字  
  33. ; 使用 `class`函数来检测它们.  
  34. (class 1) ; 整形字面值默认是java中的Long类型  
  35. (class 1.); 浮点字面值对应着java中的Double类型  
  36. (class ""); 字符串总是用双引号括起来,并且对应着java中的Sring类型  
  37. (class false) ;布尔值对应着java中的Boolean类型  
  38. (class nil); null值被称为 nil(英语含义:无、零点)  
  39.  
  40. ; 如果你想创建一列数据字面值, 使用一个单引号 ' 来防表达式被解析执行  
  41. '(+ 1 2) ; => (+ 1 2) ;这里没有返回3  
  42. ; (上面表达式和(quote (+ 1 2)) 等价,不过更简洁  
  43.  
  44. ; 你可以运算一个引用列表  
  45. (eval '(+ 1 2)) ; => 3  
  46.  
  47. ; 集合和序列  
  48. ;;;;;;;;;;;;;;;;;;;  
  49.  
  50. ; 向量和列表也是java类哦!!  
  51. (class [1 2 3]); => clojure.lang.PersistentVector  
  52. (class '(1 2 3)); => clojure.lang.PersistentList  
  53.  
  54. ;书写一个列表形如(1 2 3)一样简单, 但是我们不得不把它“引”(前面加个单引号)起来  
  55. ;这样就能防止解释器把它当做一个函数来解析  
  56. ;另外,(list 1 2 3) 和 '(1 2 3) 等价  
  57.  
  58. ;列表和向量都是集合:  
  59. (coll? '(1 2 3)) ; => true 
  60. (coll? [1 2 3]) ; => true 
  61.  
  62. ; 只有列表是序列.(序列是有顺序的)  
  63. (seq? '(1 2 3)) ; => true 
  64. (seq? [1 2 3]) ; => false 
  65.  
  66. ; 序列是列表一种逻辑上的接口,可以懒加载.  
  67. "懒" 意味着可以定义无穷序列,就像下面一样:  
  68. (range 4) ; => (0 1 2 3)  
  69. (range) ; => (0 1 2 3 4 ...) (一个无穷序列)  
  70. (take 4 (range)) ;  (0 1 2 3)  
  71.  
  72. ; 使用cons 来追加一个元素到列表或者向量的头部  
  73. (cons 4 [1 2 3]) ; => (4 1 2 3)  
  74. (cons 4 '(1 2 3)) ; => (4 1 2 3)  
  75.  
  76. ; 使用conj追加一个元素到列表的头部,或者向量的尾部,  
  77. (conj [1 2 3] 4) ; => [1 2 3 4]  
  78. (conj '(1 2 3) 4) ; => (4 1 2 3)  
  79.  
  80. ; 使用concat来连接列表和向量  
  81. (concat [1 2] '(3 4)) ; => (1 2 3 4)  
  82.  
  83. ; 使用filter, map 来进行列表计算  
  84. (map inc [1 2 3]) ; => (2 3 4)  
  85. (filter even? [1 2 3]) ; => (2)  
  86.  
  87. ; 使用reduce 来进行化繁为简  (map/reduce 思想就来自于lisp)  
  88. (reduce + [1 2 3 4])  
  89. ; = (+ (+ (+ 1 2) 3) 4)  
  90. ; => 10  
  91.  
  92. ; Reduce 可以使用一个初始值  
  93. (reduce conj [] '(3 2 1))  
  94. ; = (conj (conj (conj [] 3) 2) 1)  
  95. ; => [3 2 1]  
  96.  
  97. ; 函数  
  98. ;;;;;;;;;;;;;;;;;;;;;  
  99.  
  100. ; 使用fn来创建一个函数。所有的函数都有返回值,就是它的最后一个表达式  
  101. (fn [] "Hello World") ; => fn  
  102.  
  103. ; (你需要额外的括号去调用它)  
  104. ((fn [] "Hello World")) ; => "Hello World" 
  105.  
  106. ;你可以使用def来创建变量  
  107. (def x 1)  
  108. x ; => 1  
  109.  
  110. ; 将函数赋值给一个变量  
  111. (def hello-world (fn [] "Hello World"))  
  112. (hello-world) ; => "Hello World" 
  113.  
  114. ; 你可以使用defn来简化定义过程  
  115. (defn hello-world [] "Hello World")  
  116.  
  117. ;[] 是函数的参数列表  
  118. (defn hello [name]  
  119.   (str "Hello " name))  
  120. (hello "Steve") ; => "Hello Steve" 
  121.  
  122. ; 你也可以使用下面这种简写方式  
  123. (def hello2 #(str "Hello " %1))  
  124. (hello2 "Fanny") ; => "Hello Fanny" 
  125.  
  126. ; 你可以创建拥有可变参数的函数  
  127. (defn hello3  
  128.   ([] "Hello World")  
  129.   ([name] (str "Hello " name)))  
  130. (hello3 "Jake") ; => "Hello Jake" 
  131. (hello3) ; => "Hello World" 
  132.  
  133. ; 函数允许将参数打包成列表 (有点类似python中的*)  
  134. (defn count-args [& args]  
  135.   (str "You passed " (count args) " args: " args))  
  136. (count-args 1 2 3) ; => "You passed 3 args: (1 2 3)" 
  137.  
  138. ; 你可以将普通参数和列表参数混合使用  
  139. (defn hello-count [name & args]  
  140.   (str "Hello " name ", you passed " (count args) " extra args"))  
  141. (hello-count "Finn" 1 2 3)  
  142. ; => "Hello Finn, you passed 3 extra args" 
  143.  
  144.  
  145. ; 哈希表  
  146. ;;;;;;;;;;  
  147.  
  148. (class {:a 1 :b 2 :c 3}) ; => clojure.lang.PersistentArrayMap  
  149.  
  150. ; 关键字类似字符串,但是做了一些性能上的优化  
  151. (class :a) ; => clojure.lang.Keyword  
  152.  
  153. ; Maps 的键可以是任意类型,但是通常推荐使用keywords  
  154. (def stringmap (hash-map "a" 1, "b" 2, "c" 3))  
  155. stringmap  ; => {"a" 1, "b" 2, "c" 3}  
  156.  
  157. (def keymap (hash-map :a 1 :b 2 :c 3))  
  158. keymap ; => {:a 1, :c 3, :b 2} (不保证顺序)  
  159.  
  160. ; 顺便说一下, 逗号只是为了看着更清晰,其他都和空格一样,什么都不做.  
  161.  
  162. ; 从一个map中检索一个值,可以直接把这个map当做函数调用(这个NB)  
  163. (stringmap "a") ; => 1  
  164. (keymap :a) ; => 1  
  165.  
  166. ; 关键字也可以当做函数来调用,从一个map中检索值(这个更NB)  
  167. (:b keymap) ; => 2  
  168.  
  169. ; stings 可没有这个功能,所以下面会抛出异常。(这也是为什么推荐使用keywords)  
  170. ;("a" stringmap)  
  171. ; => Exception: java.lang.String cannot be cast to clojure.lang.IFn  
  172.  
  173. ; 检索一个不存在的值会返回nil 
  174. (stringmap "d") ; => nil 
  175.  
  176. ; 使用assoc 向一个map中添加新的键值对。  
  177. (assoc keymap :d 4) ; => {:a 1, :b 2, :c 3, :d 4}  
  178.  
  179. ; 请记住, clojure 类型是不可变的!  
  180. keymap ; => {:a 1, :b 2, :c 3}  
  181.  
  182. ; 使用dissoc 来删除key(可以删除多个)  
  183. (dissoc keymap :a :b) ; => {:c 3}  
  184.  
  185. ; 集合  
  186. ;;;;;;  
  187.  
  188. (class #{1 2 3}) ; => clojure.lang.PersistentHashSet  
  189. (set [1 2 3 1 2 3 3 2 1 3 2 1]) ; => #{1 2 3}  
  190.  
  191. ; 使用con来添加新值  
  192. (conj #{1 2 3} 4) ; => #{1 2 3 4}  
  193.  
  194. ; 使用disj删除原有值  
  195. (disj #{1 2 3} 1) ; => #{2 3}  
  196.  
  197. ; 直接将set当做函数来测试是否包含某个值(NB)  
  198. (#{1 2 3} 1) ; => 1  (有就返回原有的值)  
  199. (#{1 2 3} 4) ; => nil (没有就返回nil)  
  200.  
  201. ; clojure.sets 命名空间包含更多的函数  
  202.  
  203. ; 一些有用的形式  
  204. ;;;;;;;;;;;;;;;;;  
  205.  
  206. ; clojure中的逻辑结构都是宏, 看起来也没什么不同  
  207. (if false "a" "b") ; => "b" 
  208. (if false "a") ; => nil 
  209.  
  210. ; 使用let 来创建临时绑定  
  211. (let [a 1 b 2]  
  212.   (> a b)) ; => false 
  213.  
  214. ; 执行多条语句,返回最后一条语句  
  215. (do 
  216.   (print "Hello")  
  217.   "World") ; => "World" (prints "Hello")  
  218.  
  219. ; 所有的函数都包含一个隐式的do 
  220. (defn print-and-say-hello [name]  
  221.   (print "Saying hello to " name)  
  222.   (str "Hello " name))  
  223. (print-and-say-hello "Jeff") ;=> "Hello Jeff" (prints "Saying hello to Jeff")  
  224.  
  225. ; let绑定也是哦  
  226. (let [name "Urkel"]  
  227.   (print "Saying hello to " name)  
  228.   (str "Hello " name)) ; => "Hello Urkel" (prints "Saying hello to Urkel")  
  229.  
  230. ; 模块  
  231. ;;;;;;;;;;;;;;;  
  232.  
  233. ; 使用“use”来获得一个模块中所有的函数  
  234. (use 'clojure.set)  
  235.  
  236. ; 现在我们可以使用集合操作  
  237. (intersection #{1 2 3} #{2 3 4}) ; => #{2 3}  求交集  
  238. (difference #{1 2 3} #{2 3 4}) ; => #{1}   求差集  
  239.  
  240. ; 你可以只导入一个函数子集(例如下面只包含交集函数)  
  241. (use '[clojure.set :only [intersection]])  
  242.  
  243. ; 使用reqire来导入一个模块  
  244. (require 'clojure.string)  
  245.  
  246. ; 使用/从一个模块中调用函数  
  247. (clojure.string/blank? "") ; => true 
  248.  
  249. ; 你可以在导入模块的时候自定义名称  
  250. (require '[clojure.string :as str])    
  251. (str/replace "This is a test." #"[a-o]" str/upper-case) ; => "THIs Is A tEst."  
  252. ; (#"" denotes a regular expression literal)  
  253.  
  254. ; 你可以使用":require" 从一个命名空间中引入模块(use也可以,但是别这么做)  
  255. ; 如果你使用:require的话,就没必要把模块“引”(前面加个单引号)起来了.  
  256. (ns test  
  257.   (:require 
  258.     [clojure.string :as str]  
  259.     [clojure.set :as set]))  
  260.  
  261. ; Java  
  262. ;;;;;;;;;;;;;;;;;  
  263.  
  264. ; java 拥有一个庞大的各种用途的标准库,你一定迫不及待想学习如何在clojure中使用这些库  
  265.  
  266. ; 使用import类引入java模块(这个还好没变化)  
  267. (import java.util.Date)  
  268.  
  269. ; 你也可以从一个命名空间中引入  
  270. (ns test  
  271.   (:import java.util.Date  
  272.            java.util.Calendar))  
  273.  
  274. ; 类名字后加个”."用来创建一个对象  
  275. (Date.) ; <a date object>  
  276.  
  277. ; 使用. 来调用方法. 或者使用“.方法名"简写的方式  
  278. (. (Date.) getTime) ; <a timestamp>  
  279. (.getTime (Date.)) ; 和上面一样哦  
  280.  
  281. ; 使用/ 来调用静态方法  
  282. (System/currentTimeMillis) ; <a timestamp> (system is always present)  
  283.  
  284. ; 使用 doto 来处理可变的类<span style="font-family:宋体;">,所有的函数始终用最初的那个对象值,最后还是返回最初的那个对象</span> &nbsp;(import java.util.Calendar)  
  285. (doto (Calendar/getInstance)  
  286.   (.set 2000 1 1 0 0 0)  
  287.   .getTime) ; => A Date. set to 2000-01-01 00:00:00 

英文原文:Learn Clojure in minutes

译文链接:http://www.oschina.net/translate/learn-clojure-in-minutes

责任编辑:林师授 来源: OSCHINA编译
相关推荐

2016-09-30 15:13:01

Python代码

2009-12-29 09:01:46

ADSL断线

2020-04-30 21:46:46

机器学习病毒数据

2010-02-23 14:19:30

Python目录操作

2010-01-21 09:46:24

C++程序

2010-01-06 15:35:06

JSON对象

2024-12-03 15:38:14

2010-03-01 18:13:22

Python程序

2010-03-04 10:01:01

Android基本组件

2009-12-21 13:51:54

ADO.NET处理

2017-08-03 17:30:26

戴尔IoT设备

2009-11-25 17:22:12

路由器安装

2024-04-19 09:37:49

Ollama大语言模型开源

2021-03-23 13:49:21

人工智能机器学习

2021-02-16 09:17:40

VimLinux编辑器

2010-01-26 14:53:43

C++

2022-04-06 09:28:04

漏洞SQL注入

2010-01-21 14:51:02

C++函数指针

2010-01-06 13:39:51

扩展Jquery的Js

2022-04-12 08:46:30

for 循环遍历字符串
点赞
收藏

51CTO技术栈公众号