Clojure - 基本语法

为了理解 Clojure 的基本语法,我们首先看一个简单的 Hello World 程序。

Hello World 作为一个完整的程序

在完整的 Clojure 程序中编写"Hello world"。 下面是一个例子。

示例

(ns clojure.examples.hello
   (:gen-class))
(defn hello-world []
   (println "Hello World"))
(hello-world)

上述程序需要注意以下事项。

  • 该程序将被写入名为 main.clj 的文件中。 扩展名"clj"是 clojure 代码文件的扩展名。 在上面的示例中,文件的名称称为 main.clj。

  • "defn"关键字用于定义函数。 我们将在另一章中详细了解函数。 但现在,我们正在创建一个名为 helloworld 的函数,其中包含我们的主要 Clojure 代码。

  • 在我们的 Clojure 代码中,我们使用"println"语句将"Hello World"打印到控制台输出。

  • 然后我们调用 hello-world 函数,该函数又运行"println"语句。

上面的程序产生以下输出。

输出

Hello World

语句的一般形式

任何语句的一般形式都需要在大括号中进行计算,如下例所示。

(+ 1 2)

在上面的示例中,整个表达式都用大括号括起来。 上述语句的输出是3。 运算符的作用类似于 Clojure 中的函数,用于数字相加。 1 和 2 的值被称为函数的参数

让我们考虑另一个例子。 在此示例中,"str"是用于连接两个字符串的运算符。 字符串"Hello"和"World"用作参数。

(str "Hello" "World")

示例

如果我们结合以上两条语句并编写一个程序,它将如下所示。

(ns clojure.examples.hello
   (:gen-class))
(defn Example []
   (println (str "Hello World"))
   (println (+ 1 2)))
(Example)

输出

上面的程序产生以下输出。

Hello World
3

命名空间

命名空间用于定义 Clojure 中定义的模块之间的逻辑边界。

当前命名空间

这定义了当前 Clojure 代码所在的当前命名空间。

语法

*ns*

示例

在 REPL 命令窗口中运行以下命令。

*ns*

输出

当我们运行上述命令时,输出将根据当前命名空间而延迟。 以下是输出示例。 Clojure 代码的命名空间是 −

clojure.examples.hello

(ns clojure.examples.hello
   (:gen-class))
(defn Example []
   (println (str "Hello World"))
   (println (+ 1 2)))
(Example)

Clojure 中的 Require 语句

Clojure 代码打包在库中。 每个 Clojure 库都属于一个命名空间,类似于 Java 包。 您可以使用"Require"语句加载 Clojure 库。

语法

(require quoted-namespace-symbol)

示例

以下是该语句的用法示例。

(ns clojure.examples.hello
   (:gen-class))
(require ‘clojure.java.io’)
(defn Example []
   (.exists (file "Example.txt")))
(Example)

在上面的代码中,我们使用"require"关键字导入名称空间 clojure.java.io,它具有输入/输出功能所需的所有函数。 由于我们没有所需的库,因此我们可以使用上面代码中的"file"函数。

Clojure 中的注释

注释用于记录您的代码。 单行注释通过使用 ;; 来标识。 在线中的任意位置。 下面是一个例子。

示例

(ns clojure.examples.hello
   (:gen-class))

;; This program displays Hello World
(defn Example []
   (println "Hello World"))
(Example)

分隔符

在 Clojure 中,可以使用弯括号或方括号来分割或分隔语句。

示例

以下是两个示例。

(ns clojure.examples.hello
   (:gen-class))

;; This program displays Hello World
(defn Example []
   (println (+ 1 2 3)))
(Example)

输出

上面的程序产生以下输出。

6

示例

下面是另一个例子。

(ns clojure.examples.hello
   (:gen-class))

;; This program displays Hello World
(defn Example []
   (println [+ 1 2 3]))
(Example)

输出

上面的程序产生以下输出。

[#object[clojure.core$_PLUS_ 0x10f163b "clojure.core$_PLUS_@10f163b"] 1 2 3]

空格

Clojure 中可以使用空格来分割语句的不同组成部分,以提高清晰度。 这可以在逗号 (,) 运算符的帮助下完成。

例如,以下两条语句是等效的,两条语句的输出均为 15。

(+ 1 2 3 4 5)
(+ 1, 2, 3, 4, 5)

尽管 Clojure 会忽略逗号,但它有时会使用它们来使程序员更容易阅读。

例如,如果您有如下所示的哈希映射 (def a-map {:a 1 :b 2 :c 3}) 并在 REPL 窗口中询问其值,Clojure 会将输出打印为 {: a 1,:b 2,:c 3}。

结果更容易阅读,尤其是在您查看大量数据时。

符号

在 Clojure 中,符号相当于其他编程语言中的标识符。 但与其他编程语言不同的是,编译器将符号视为实际的字符串值。 由于符号是一个值,因此符号可以存储在集合中,作为参数传递给函数等,就像任何其他对象一样。

符号只能包含字母数字字符和 '* + ! / . : - _ ?',但不得以数字或冒号开头。

以下是有效的符号示例。

tutorial-point!
TUTORIAL
+tutorial+

Clojure 项目结构

最后我们来谈谈 Clojure 项目的典型项目结构。 由于 Clojure 代码在 Java 虚拟机上运行,因此 Clojure 中的大部分项目结构与 Java 项目中的结构类似。 以下是 Eclipse 中 Clojure 项目的示例项目结构的快照。

基本语法

关于上述程序结构,需要注意以下关键事项。

  • demo_1 − 这是放置 Clojure 代码文件的包。

  • core.clj − 这是主 Clojure 代码文件,其中包含 Clojure 应用程序的代码。

  • Leiningen 文件夹包含运行任何基于 Clojure 的应用程序所需的文件,例如 clojure-1.6.0.jar。

  • pom.properties 文件将包含 Clojure 项目的 groupId、artifactId 和版本等信息。

  • project.clj 文件包含有关 Clojure 应用程序本身的信息。 以下是项目文件内容的示例。

(defproject demo-1 "0.1.0-SNAPSHOT"
   :description "FIXME: write description"
   :url "http://example.com/FIXME"
   :license {
      :name "Eclipse Public License"
      :url "http://www.eclipse.org/legal/epl-v10.html"
   }
   :dependencies [[org.clojure/clojure "1.6.0"]])