多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
## Clojure 语法 Lisp方言有一个非常简洁的语法 — 有些人觉得很美的语法。数据和代码的表达形式是一样的,一个列表的列表很自然地在内存里面表达成一个tree。(a b c)表示一个对函数a的调用,而参数是b和c。如果要表示数据,你需要使用 `'(a b c)` o或者 `(quote (a b c))` 。通常情况下就是这样了,除了一些特殊情况 — 到底有多少特殊情况取决于你所使用的方言。 我们把这些特殊情况称为语法糖。语法糖越多代码写起来越简介,但是同时我们也要学习更多的东西以读懂这些代码。这需要找到一个平衡点。很多语法糖都有对应的函数可以调用。到底语法糖是多了还是少了还是你们自己来判断吧。 下面这个表格简要地列举了Clojure里面的一些语法糖, 这些语法糖我们会在后面详细讲解的,所以如果你现在没办法完全理解它们不用担心。 | 作用 | 语法糖 | 对应函数 | | --- | --- | --- | | 注释 | `; _text_`  _单行注释_ | `宏(comment _text_)可以用来写多行注释` | | 字符 (Java `char` 类型) | `\_char_` `\tab` `\newline` `\space` `\u_unicode-hex-value_` | `(char _ascii-code_)` `(char \u_unicode_` ) | | 字符串 (Java `String` 对象) | `"_text_"` | `(str _char1_ _char2_ ...)` 可以把各种东西串成一个字符串 | | 关键字是一个内部字符串; 两个同样的关键字指向同一个对象; 通常被用来作为map的key | `:_name_` | `(keyword "_name_")` | | 当前命名空间的关键字 | `::_name_` | N/A | | 正则表达式 | `#"_pattern_"` | `(re-pattern _pattern_)` | | 逗号被当成空白(通常用在集合里面用来提高代码可读性) | `,` (逗号) | N/A | | 链表(linked list) | `'(_items_)` (不会evaluate每个元素) | `(list _items_)` 会evaluate每个元素 | | vector(和数组类似) | `[_items_]` | `(vector _items_)` | | set | `#{_items_}` 建立一个hash-set | `(hash-set _items_)` `(sorted-set _items_)` | | map | `{_key-value-pairs_}` 建立一个hash-map | `(hash-map _key-value-pairs_)` `(sorted-map _key-value-pairs_)` | | 给symbol或者集合绑定元数据 | `#^{_key-value-pairs_} _object_` 在读入期处理 | `(with-meta _object_ _metadata-map_)` 在运行期处理 | | 获取symbol或者集合的元数据 | `^_object_` | `(meta _object_)` | | 获取一个函数的参数列表(个数不定的) | `& _name_` | N/A | | 函数的不需要的参数的默认名字 | `_` (下划线) | N/A | | 创建一个java对象(注意class-name后面的点) | `(_class-name_. _args_)` | `(new _class-name_ _args_)` | | 调用java方法 | `(. _class-or-instance_ _method-name_ _args_)` 或者 `(._method-name_ _class-or-instance_ _args_)` | N/A | | 串起来调用多个函数,前面一个函数的返回值会作为后面一个函数的第一个参数;你还可以在括号里面指定额外参数;注意前面的两个点 | `(.. _class-or-object_ (_method1 args_) (_method2 args_) ...)` | N/A | | 创建一个匿名函数 | `#(_single-expression_)` 用 `%` (等同于 `%1` ), `%1` , `%2来表示参数` | `(fn [_arg-names_] _expressions_)` | | 获取Ref, Atom 和Agent对应的valuea | `@_ref_` | `(deref _ref_)` | | get `Var` object instead of the value of a symbol (var-quote) | `#'_name_` | `(var _name_)` | | syntax quote (使用在宏里面) | ``` | none | | unquote (使用在宏里面) | `~_value_` | `(unquote _value_)` | | unquote splicing (使用在宏里面) | `~@_value_` | none | | auto-gensym (在宏里面用来产生唯一的symbol名字) | `_prefix_#` | `(gensym _prefix_ )` | 对于二元操作符比如 +和*, Lisp方言使用前置表达式而不是中止表达式,这和一般的语言是不一样的。比如在java里面你可能会写 `a + b + c` , 而在Lisp里面它相当于 `(+ a b c) 。这种表达方式的一个好处是如果操作数有多个,那么操作符只用写一次` . 其它语言里面的二元操作符在lisp里面是函数,所以可以有多个操作数。 Lisp代码比其它语言的代码有更多的小括号的一个原因是Lisp里面不使用其它语言使用的大括号,比如在java里面,方法代码是被包含在大括号里面的,而在lisp代码里面是包含在小括号里面的。 比较下面两段简单的Java和Clojure代码,它们实现相同的功能。它们的输出都是: “edray” 和 “orangeay”. ``` // This is Java code. public class PigLatin { public static String pigLatin(String word) { char firstLetter = word.charAt(0); if ("aeiou".indexOf(firstLetter) != -1) return word + "ay"; return word.substring(1) + firstLetter + "ay"; } public static void main(String args[]) { System.out.println(pigLatin("red")); System.out.println(pigLatin("orange")); } } ``` ``` ; This is Clojure code. ; When a set is used as a function, it returns a boolean ; that indicates whether the argument is in the set. (def vowel? (set "aeiou")) (defn pig-latin [word] ; defines a function ; word is expected to be a string ; which can be treated like a sequence of characters. (let [first-letter (first word)] ; assigns a local binding (if (vowel? first-letter) (str word "ay") ; then part of if (str (subs word 1) first-letter "ay")))) ; else part of if (println (pig-latin "red")) (println (pig-latin "orange")) ``` Clojure支持所有的常见数据类型比如 booleans ( `true` and `false` ), 数字, 高精度浮点数, 字符(上面表格里面提到过 ) 以及字符串. 同时还支持分数 — 不是浮点数,因此在计算的过程中不会损失精度. Symbols是用来给东西命名的. 这些名字是被限制在名字空间里面的,要么是指定的名字空间,要么是当前的名字空间. Symbols的值是它所代表的名字的值. 要使用Symbol的值,你必须把它用引号引起来. 关键字以冒号打头,被用来当作唯一标示符,通常用在map里面 (比如 `:red` , `:green` 和 `:blue` ). 和任何语言一样,你可以写出很难懂的Clojure代码。遵循一些最佳实践可以避免这个。写一些简短的,专注自己功能的函数可以使函数变得容易读,测试以及重复利用。经常使用“抽取方法”的模式来对你的代码进行重构。高度内嵌的函数是非常难懂得,千万不要这么写, 你可以使用let来帮助你。把匿名函数传递给命名函数是非常常见的,但是不要把一个匿名函数传递给另外一个匿名函数, 这样代码就很难懂了。