Skip to main content
 首页 » 操作系统 » linux系统

Clojure读取器宏和函数匿名函数

读取器宏读取器负责读入Clojure形式,并将其从文本转换为Clojure数据结构。

除了那些基本的形式,Clojure 读取器还能识别一组专门的读取器宏。

读取器宏,是指由宏字符前缀触发的特殊读取器行为。

触发注释的宏字符是分号(;),其对应的特殊读取器行为是“忽略后面的所有内容直至本行结束”。

引号(')可以阻止求值。

Clojure读取器宏和函数匿名函数  Clojure 第1张

'(1 2)与较长的(quote (1 2))等价。

Clojure读取器宏和函数匿名函数  Clojure 第2张

Clojure读取器宏和函数匿名函数  Clojure 第3张

Clojure不允许程序定义新的读取器宏。在灵活性上做出的这个妥协,能给予Clojure一个更加稳定的内核。如果允许自定义读取器宏,很可能会降低Clojure程序的互操作性,并且难以阅读。

函数函数调用,在Clojure中,只不过是一个列表的起始元素可以被解析成函数而已。

Clojure读取器宏和函数匿名函数  Clojure 第4张

函数名通常表明了其单复数,例如clear-agent-errors。如果函数是一个谓词,那么按照惯例,它的名称应该以一个问号结束。

Clojure读取器宏和函数匿名函数  Clojure 第5张

defn定义函数

Clojure读取器宏和函数匿名函数  Clojure 第6张

attr-map表示关联到函数对象上的元数据。

Clojure读取器宏和函数匿名函数  Clojure 第7张

Clojure读取器宏和函数匿名函数  Clojure 第8张

Clojure读取器宏和函数匿名函数  Clojure 第9张

函数调用漏掉参数

Clojure读取器宏和函数匿名函数  Clojure 第10张

Clojure 函数强调元数(arity),也就是它们期望获得的参数数量。如果调用函数时传入的参数数目不正确,Clojure 会抛出一个 ArityException。如果希望当调用者遗漏了username参数时,greeting函数也能表达通用的问候,那么可以使用defn的另外一种形式,它允许函数接受多组参数列表和函数主体。

Clojure读取器宏和函数匿名函数  Clojure 第11张

同一个函数的不同元数之间能够彼此相互调用,可以很容易地创建一个没有参数的greeting,然后将功能委托给那个单参数的greeting,并传入一个默认的username。

Clojure读取器宏和函数匿名函数  Clojure 第12张

Clojure读取器宏和函数匿名函数  Clojure 第13张

在参数列表中包含一个&号,就能创建一个具有可变元数的函数。Clojure会把所有剩余的参数都放进一个序列中,并绑定到&号后面的那个名称上。

Clojure读取器宏和函数匿名函数  Clojure 第14张

defn意在命名空间的顶层定义函数。但如果希望能够通过函数来创建函数,那就应该使用匿名函数加以替代。

匿名函数除了能用 defn 来创建具名函数以外,还能用 fn 创建匿名函数。采用匿名函数至少有以下3个原因。

● 这是一个很简短且不言自明的函数,如果给它取名字的话,不会令可读性增强,反而使得代码更难以阅读。

● 这是一个仅在别的函数内部使用的函数,需要的是局部名称,而非顶级绑定。

● 这个函数是在别的函数中被创建的,其目的是为了隐藏某些数据。

用作过滤器的函数总是简短且不言自明的。

Clojure读取器宏和函数匿名函数  Clojure 第15张

使用indexable-word?从句子中提取那些可索引的单词。

Clojure读取器宏和函数匿名函数  Clojure 第16张

调用split,将句子分解为单词,然后filter对每个单词调用indexable-word?,并返回那些被indexable-word?判定为true的单词。匿名函数能仅用一行代码就做到相同的事情。

Clojure读取器宏和函数匿名函数  Clojure 第17张

Clojure读取器宏和函数匿名函数  Clojure 第18张

隐式参数名称被命名为%1、%2,以此类推。对于第一个参数,可以使用%表示。

Clojure读取器宏和函数匿名函数  Clojure 第19张

Clojure读取器宏和函数匿名函数  Clojure 第20张

使用匿名函数的第二个动机是,确定想要一个具名函数,但该函数仅在其他函数的作用域内使用。

Clojure读取器宏和函数匿名函数  Clojure 第21张

let将匿名函数与名称indexable-word?绑定在了一起,但这次它被限定在了indexable-words的词法作用域内。

Clojure读取器宏和函数匿名函数  Clojure 第22张

采用这种let和匿名函数的组合,相当于对代码的读者说:“函数indexable-word?有足够的理由拥有一个名称,但仅限于在indexable-words中。”

使用匿名函数的第三个原因是,有时需要在运行期动态创建一个函数。

Clojure读取器宏和函数匿名函数  Clojure 第23张

为一个通过 fn 得到的函数命名是没有意义的,因为每次调用make-greeter都会创建一个不同的函数。然而,在某次调用了make-greeter之后,也许会想为那个特殊的具体结果命名。如果真是这样,可以使用 def 对 make-greeter创建的函数进行命名。

Clojure读取器宏和函数匿名函数  Clojure 第24张

Clojure读取器宏和函数匿名函数  Clojure 第25张

没有必要为每个问候函数都命名。可以简单地创建一个问候函数,并将它放置到列表形式的第一个(函数)槽(slot)中。

Clojure读取器宏和函数匿名函数  Clojure 第26张

不同的问候函数会记住创建它们时的那个greeting-prefix值。

用更为正式的说法,这些问候函数对greeting-prefix的值构成了闭包(closures)。

何时使用匿名函数匿名函数那极度简洁的语法并不总是恰当的。

匿名函数只是一种选择,而非必须。

只有当发现它们可以令你的代码更具可读性时,才应该使用这样的匿名形式。

评论列表暂无评论
发表评论
微信