为了理解复杂的表达式和对它的操做,一个首要的前提就是理解”前缀表达式“。这可能会花费你一点时间来习惯它。不过我相信你会很快的爱上这种规则的。你想一想,若是你要对多个值进行同一种运算,你只用写一个运算符在第一个值的最前面,而不是写多个运算符在中间。不信就看下面的例子: python
普通: 1 + 2 + 3 + 4 + 5 + 6 + 8 + 9 前缀: + 1 2 3 4 5 6 7 8 9抛开前缀表示法不说,一个复杂表达式能够看出是一个单独的操做,或者是一组操做。它们既能够接收参数,也能向外输出,在整个表达式计算完成的时候返回计算结果。一句话,复杂的表达式能够作任何你想让它作的事情。让咱们看一个很是简单的例子:
(+ 1 2)
在对这个式子求值前,咱们先去看一看clojure在求值期间会作哪些工做: shell
首先,clojure会遇到左括号,代表这是一个列表(表达式的形式)的开始。这个列表包含了它要处理的全部东西。clojure永远会把左括号的第一个元素当作是一个操做符(这也是采用前缀表达式的缘由),这种方式使得lisp方言语法异常的简单。在咱们的例子中,操做符指的就是一个函数。(函数其实能够看出是针对其参数须要作哪些操做的说明)。当clojure遇到一个右括号,代表这个列表的结束。这种解析是递归的,由于列表中的元素依然多是列表。这种表达方式还有另外一个名字:S表达式。(lisp 的含义就是 list processor 即列表处理)。 app
S表达式看起来可能很直观,但对它的理解是很是很是重要的。这是学习一切lisp方言的基础。Clojure在执行函数(例如+)以前,首先会对其全部的参数进行顺序(从左到右)求值并返回本身的结果。而后函数会针对这些参数的返回结果进行相应的求值运算。返回最终的结果。 函数
上面例子中,”+"是函数,参数都是数字字面值。咱们说过字面值求值后返回本身。因此整个操做就是将“+”运用到1和2之上。获得的最终结果就是3。再考虑一下下面这个例子 学习
(+ 2 (- 8 3))
上面例子稍微复杂了一点,由于第二个参数不是单纯的字面值。这个求值也很是简单,clojure在对第二个参数(- 8 3)进行求值时采起的依然是以前的策略,获得结果5。最终 "+"会做用在2和5之上,最终结果返回7。因此咱们说这种解析方式是递归的。S表达式规则虽然简单,可是真的是变化多端啊。 spa
再来看一个例子(检测对前缀表达式的理解): code
=>(- 8 3 2 1 -6 34 12 4 2 6 4 -23 12 4) -47
了解完上面,接下来作点啥呢?运行点例子也许是个好主意,可是还有更好的方法。让咱们看一看内置函数的源码来了解一下。查看某个函数的源码很简单,仍是调用函数来作。咱们会使用一个名为“source”的函数:(source 【函数名】) 递归
让咱们查看函数'-'(减号)的源码 源码
=>(source -) ;;依然是S表达式 (defn - "If no ys are supplied, returns the negation of x, else subtracts the ys from x and returns the result." {:inline (fn [& args] `(. clojure.lang.Numbers (minus ~ @args))) :inline-arities #{1 2} :added "1.0"} ([x] (. clojure.lang.Numbers (minus x))) ([x y] (. clojure.lang.Numbers (minus x y))) ([x y & more] (reduce - (- x y) more))) nil这个比预想的要多的多了,一个减号竟然有这么多代码量。如今不用担忧不理解上面的代码。由于这里面包含了不少未知的知识。咱们只用知道一点是,咱们的”-“能够操做任意个参数的能力,而这一切归功一个叫作”reduce“的函数。咱们不用去调用”reduce“的源码,只用看看相关描述便可。咱们可使用”doc“函数:
=>(doc reduce) ------------------------- clojure.core/reduce ([f coll] [f val coll]) f should be a function of 2 arguments. If val is not supplied, returns the result of applying f to the first 2 items in coll, then applying f to that result and the 3rd item, etc. If coll contains no items, f must accept no arguments as well, and reduce returns the result of calling f with no arguments. If coll has only 1 item, it is returned and f is not called. If val is supplied, returns the result of applying f to val and the first item in coll, then applying f to that result and the 2nd item, etc. If coll contains no items, returns val and f is not called. nil这里的reduce和python中的reduce函数含义是同样。