跳到主要内容

【翻译-不推荐】学习 Clojure - 语法

原文来自于官网:https://www.clojure.org/guides/learn/syntax

字面含义

下面是一些Clojure中常见原始的的字面表示法的例子。所有这些字面符号都是有效的Clojure表达式。

;创建一个注释到行尾。有时会使用多个分号来表示标题注释部分,但这只是一种惯例。

数值类型

42        ; integer
-1.5 ; 浮点型
22/7 ; 比值

当整数在范围内时,会作为固定精度的64位整数来读取,否则就是任意精度。后面的N可以用来强制使用任意精度。Clojure还支持八进制(前缀0)、十六进制(前缀0x)和任意弧度(前缀为base,然后为r)整数的Java语法。比值是作为他们自己的类型提供的,结合了分子和分母。

浮点值可作为双精度64位浮点,或任意精度的M后缀来读取。指数符号也被支持。特殊符号值##Inf##-Inf##NaN分别代表正无穷大、负无穷大和 "非数字 "值。

字符类型

"hello"         ; 字符串
\e ; character
#"[0-9]+" ; 正则表达式

字符串包含在双引号中,可以跨越多行。单个字符用前面的反斜杠表示。有一些特殊的命名字符。\newline (换行), \spec , \tab 等。Unicode字符可以用 \uNNNN 表示,或者用八进制的 \oNNNN 表示。

字面正则表达式是带有#号的字符串。这些被编译成java.util.regex.Pattern对象。

符号和标识

map             ; symbol
+ ; symbol - most punctuation allowed
clojure.core/+ ; namespaced symbol
nil ; null value
true false ; booleans
:alpha ; keyword
:release/alpha ; 带命名空间的关键字

符号由字母、数字和其他标点符号组成,用来指代其他东西,如函数、值、命名空间等。符号可以选择有一个命名空间,用正斜杠与名称分开。

有三个特殊的符号被当作不同的类型来读 - nil是空值,truefalse是布尔值。

关键词以前面的冒号开始,并且总是对自己进行评估。它们经常被用作Clojure中的枚举值或属性名称。

集合

Clojure 还包括四种集合类型的文字语法:

'(1 2 3)     ; list
[1 2 3] ; vector
#{1 2 3} ; set
{:a 1, :b 2} ; map

稍后我们将更详细地讨论这些 - 现在知道这四种数据结构可用于创建复合数据就足够了。

计算

接下来我们将考虑 Clojure 如何读取和评估表达式。

传统的计算(Java)

Java evaluation

在Java中,源代码(.java文件)被编译器(javac)作为字符读取,产生字节码(.class文件),可由JVM加载。

Clojure计算

Clojure evaluation

在Clojure中,源代码是由阅读器作为字符来读取的。读取器可以从.clj文件中读取源码,也可以以交互方式给出一系列表达式。读取器产生Clojure数据。然后,Clojure编译器为JVM产生字节码。

这里有两个重要的观点:

  1. 源代码的单位是 Clojure 表达式,而不是 Clojure 源文件。 源文件被读取为一系列表达式,就像您在 REPL 中以交互方式键入这些表达式一样。
  2. 宏是特殊的函数,它接受代码(作为数据),并发出代码(作为数据)。你能看出在评估模型中可以插入一个用于宏扩展的循环吗?

结构与语义

考虑一个Clojure表达式。

Structure and semantics

这张图说明了绿色的语法(Reader产生的Clojure数据结构)和蓝色的语义(Clojure运行时如何理解这些数据)之间的区别。

除了符号和列表之外,大多数Clojure的字面形式都会对自己进行评估。符号是用来指代其他东西的,当被评估时,返回它们所指代的东西。列表(如图)是作为调用来评估的。

在图中,(+ 3 4)被解读为一个包含符号(+)和两个数字(3和4)的列表。第一个元素(找到+的地方)可以被称为 "函数位置",也就是说,找到要调用的东西的地方。虽然函数是一个明显的可调用的东西,但也有一些运行时已知的特殊操作符、宏和其他一些可调用的东西。

考虑到上述表达式的计算:

  • 3 and 4 evaluate to themselves (longs)
  • + evaluates to a function that implements +
  • 计算该列表将调用+函数,参数为3和4。

许多语言都有语句和表达式,其中语句有一些有状态的效果,但不返回一个值。在Clojure中,所有的东西都是一个表达式,它可以赋值为一个值。一些表达式(但不是大多数)也有副作用。

现在让我们考虑如何在Clojure中交互式地赋值表达式。

为引用延迟求值

有时暂停赋值是很有用的,特别是对于符号和列表。有时,一个符号就应该是一个符号,而不需要去查它指的是什么。

user=> 'x
x

而有时一个列表应该只是一个数据值的列表(而不是要赋值的代码)。

user=> '(1 2 3)
(1 2 3)

你可能会看到一个令人困惑的错误,那就是不小心试图把一个数据列表当作代码来赋值的结果。

user=> (1 2 3)
Execution error (ClassCastException) at user/eval156 (REPL:1).
class java.lang.Long cannot be cast to class clojure.lang.IFn

现在,不要太担心引用,但在这些原型中你会偶尔看到它,以避免对符号或列表的赋值。