Clojure - 快速指南


Clojure - 概述

Clojure 是一种高级动态函数式编程语言。Clojure 基于 LISP 编程语言设计,并具有编译器,使其可以在 Java 和 .Net 运行时环境上运行。

在讨论 Clojure 之前,我们先简单介绍一下 LISP 编程语言。LISP 具有很小的语言核心,几乎没有语法,并且具有强大的宏功能。借助这些功能,您可以弯曲 LISP 以满足您的设计,而不是相反。LISP 已经存在很长时间了,可以追溯到 1958 年。

Common LISP 读入一个表达式,对其求值,然后打印出结果。例如,如果您想计算 4+6 的简单数学表达式的值,则可以输入。

USER(1) (+ 4 6)

Clojure 作为一种编程语言具有以下高级关键目标。

  • 它基于LISP编程语言,使其代码语句比传统编程语言更小。

  • 它是一种函数式编程语言。

  • 它侧重于不变性,这基本上是一个概念,即您不应该对就地创建的对象进行任何更改。

  • 它可以为程序员管理应用程序的状态。

  • 它支持并发。

  • 它包含现有的编程语言。例如,Clojure可以利用整个Java生态系统通过JVM来管理代码的运行。

Clojure 的官方网站是https://clojure.org/

Clojure 概述

Clojure - 环境

使用 Clojure 作为编程语言有多种方法。我们将了解两种使用 Clojure 编程的方法。

  • Leiningen - Leiningen 是创建、构建和自动化 Clojure 项目的重要工具。

  • Eclipse 插件- 有一个名为 CounterClockwise 的插件,可用于 Eclipse 在 Eclipse IDE 中进行 Clojure 开发。

莱宁根装置

在继续安装之前,请确保满足以下系统要求。

系统要求

JDK JDK 1.7 或更高版本
记忆 2 GB 内存(推荐)

步骤 1 - 下载二进制安装。转至链接http://leiningen-wininstaller获取 Windows Installer。单击该选项开始下载 Groovy 安装程序。

步骤 2 - 启动安装程序并单击“下一步”按钮。

启动安装程序

步骤 3 - 指定安装位置,然后单击“下一步”按钮。

地点安装

步骤 4 - 安装程序将检测现有 Java 安装的位置。单击“下一步”按钮继续。

Java安装

步骤 5 - 单击“安装”按钮开始安装。

开始安装

安装完成后,您将可以选择打开 Clojure REPL,这是一个可用于创建和测试 Clojure 程序的环境。

Clojure 程序

Eclipse 安装

在继续安装之前,请确保满足以下系统要求。

系统要求

JDK JDK 1.7 或更高版本
日食 4.5(火星)

步骤 1 - 打开 Eclipse 并单击菜单项。单击帮助 → Eclipse Marketplace。

日食市场

步骤 2 - 在出现的对话框中输入关键字 Clojure,然后点击“Go”按钮。将出现逆时针选项,单击安装按钮开始安装此插件。

Clojure 对话框

步骤 3 - 在下一个对话框中,单击“确认”按钮开始安装。

Clojure 确认按钮

步骤 4 - 在下一个对话框中,系统将要求您接受许可协议。接受许可协议并单击完成按钮继续安装。

接受许可协议

安装将开始,完成后,它将提示您重新启动 Eclipse。

重新启动 Eclipse 后,您将在 Eclipse 中看到创建新 Clojure 项目的选项。

Clojure 项目

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"]])

Clojure-REPL

REPL(读取-评估-打印循环)是一个用于试验 Clojure 代码的工具。它允许您与正在运行的程序交互并快速尝试事情是否按预期进行。它通过向您显示可以输入代码的提示来实现此目的。然后,它读取您的输入,对其进行评估,打印结果,然后循环,再次向您显示提示。

此过程实现了快速反馈周期,这在大多数其他语言中是不可能的。

启动 REPL 会话

通过在命令行中键入以下命令,可以在 Leiningen 中启动 REPL 会话。

lein repl

这将启动以下 REPL 窗口。

REPL窗口

然后,您可以根据需要开始在 REPL 窗口中评估 Clojure 命令。

要在 Eclipse 中启动 REPL 会话,请单击“菜单”选项,转到“运行方式”→“Clojure 应用程序”。

REPL 会话

这将在一个单独的窗口中启动一个新的 REPL 会话以及控制台输出。

REPL 控制台输出

从概念上讲,REPL 类似于 Secure Shell (SSH)。与使用 SSH 与远程服务器交互的方式相同,Clojure REPL 允许您与正在运行的 Clojure 进程交互。此功能非常强大,因为您甚至可以将 REPL 附加到实时生产应用程序并在程序运行时对其进行修改。

REPL 中的特殊变量

REPL 包含一些有用的变量,其中广泛使用的是特殊变量*1、*2 和*3。它们用于评估三个最近表达式的结果。

以下示例显示了如何使用这些变量。

user => "Hello"
Hello
user => "World"
World
user => (str *2 *1)
HelloWorld

在上面的示例中,前两个字符串分别作为“Hello”和“World”发送到 REPL 输出窗口。然后 *2 和 *1 变量用于调用最后 2 个计算的表达式。

Clojure - 数据类型

Clojure 提供了多种内置数据类型。

内置数据类型

以下是 Clojure 中定义的数据类型列表。

  • 整数- 以下是 Clojure 中可用的整数表示。

    • 十进制整数(短整型、长整型和整型) - 这些用于表示整数。例如,1234。

    • 八进制数- 这些用于表示八进制表示的数字。例如,012。

    • 十六进制数字- 这些用于表示表示中的数字。例如,0xff。

    • 基数- 这些用于表示基数表示中的数字。例如,2r1111,其中基数是 2 到 36 之间的整数(含 2 和 36)。

  • 浮点

    • 默认用于表示 32 位浮点数。例如,12.34。

    • 另一种表示方法是科学记数法。例如,1.35e-12。

  • char - 这定义了单个字符文字。字符是用反冲符号定义的。例如,/e。

  • Boolean - 这表示一个布尔值,可以是 true 或 false。

  • 字符串- 这些是以字符链的形式表示的文本文字。例如,“你好世界”。

  • Nil - 这用于在 Clojure 中表示 NULL 值。

  • Atom - Atomics提供了一种管理共享、同步、独立状态的方法。它们是像 refs 和 vars 一样的引用类型。

约束值

由于 Clojure 中的所有数据类型都是从 Java 继承的,因此有界值与 Java 编程语言中的相同。下表显示了数字和十进制文字的最大允许值。

文字 范围
短的 -32,768 至 32,767
整数 -2,147,483,648 至 2,147,483,647
长的 -9,223,372,036,854,775,808 至 +9,223,372,036,854,775,807
漂浮 1.40129846432481707e-45 至 3.40282346638528860e+38
双倍的 4.94065645841246544e-324d 至 1.79769313486231570e+308d

类数字类型

除了基本类型之外,还允许使用以下对象类型(有时称为包装类型)。

姓名
java.lang.Byte
java.lang.Short
java.lang.Integer
java.lang.Long
java.lang.Float
java.lang.Double

例子

以下程序显示了一个整合的 clojure 代码,以演示 Clojure 中的数据类型。

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

;; This program displays Hello World
(defn Example []
   ;; The below code declares a integer variable
   (def x 1)
   
   ;; The below code declares a float variable
   (def y 1.25)
   
   ;; The below code declares a string variable
   (def str1 "Hello")
   (println x)
   (println y)
   (println str1))
(Example)

输出

上述程序产生以下输出。

1
1.25
Hello

Clojure - 变量

在 Clojure 中,变量由“def”关键字定义。它有点不同,其中变量的概念更多地与绑定有关。在 Clojure 中,值绑定到变量。Clojure 中需要注意的一件关键事情是变量是不可变的,这意味着为了更改变量的值,需要将其销毁并再次重新创建。

以下是 Clojure 中变量的基本类型。

  • - 用于表示短数字。例如,10。

  • int - 用于表示整数。例如,1234。

  • long - 用于表示长数字。例如,10000090。

  • float - 用于表示 32 位浮点数。例如,12.34。

  • char - 这定义了单个字符文字。例如,“/a”。

  • Boolean - 这表示一个布尔值,可以是 true 或 false。

  • 字符串- 这些是以字符链的形式表示的文本文字。例如,“你好世界”。

变量声明

以下是定义变量的一般语法。

句法

(def var-name var-value)

其中“var-name”是变量的名称,“var-value”是绑定到变量的值。

例子

以下是变量声明的示例。

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

;; This program displays Hello World
(defn Example []
   ;; The below code declares a integer variable
   (def x 1)
   
   ;; The below code declares a float variable
   (def y 1.25)

   ;; The below code declares a string variable
   (def str1 "Hello")
   
   ;; The below code declares a boolean variable
   (def status true))
(Example)

命名变量

变量的名称可以由字母、数字和下划线字符组成。它必须以字母或下划线开头。大小写字母是不同的,因为 Clojure 就像 Java 一样是区分大小写的编程语言。

例子

以下是 Clojure 中变量命名的一些示例。

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

;; This program displays Hello World
(defn Example []
   ;; The below code declares a Boolean variable with the name of status
   (def status true)
   
   ;; The below code declares a Boolean variable with the name of STATUS
   (def STATUS false)
   
   ;; The below code declares a variable with an underscore character.
   (def _num1 2))
(Example)

注意- 在上述语句中,由于区分大小写,status 和 STATUS 是 Clojure 中两个不同的变量定义。

上面的示例显示了如何定义带有下划线字符的变量。

打印变量

由于Clojure使用JVM环境,因此您还可以使用'println'函数。以下示例展示了如何实现这一点。

例子

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

;; This program displays Hello World
(defn Example []
   ;; The below code declares a integer variable
   (def x 1)
   
   ;; The below code declares a float variable
   (def y 1.25)
   
   ;; The below code declares a string variable
   (def str1 "Hello")
   (println x)
   (println y)
   (println str1))
(Example)

输出

上述程序产生以下输出。

1
1.25
Hello

Clojure - 运算符

运算是告诉编译器执行特定数学或逻辑操作的符号。

Clojure 有以下类型的运算符 -

  • 算术运算符
  • 关系运算符
  • 逻辑运算符
  • 按位运算符

注意- 在 Clojure 中,运算符和操作数按以下语法方式工作。

句法

(operator operand1 operand2 operandn)

例如,

例子

(+ 1 2)

上面的示例对数字 1 和 2 进行算术运算。

算术运算符

Clojure 语言与任何语言一样支持普通算术运算符。以下是 Clojure 中可用的算术运算符。

显示示例

操作员 描述 例子
+ 两个操作数相加 (+ 1 2) 将给出 3
- 从第一个操作数中减去第二个操作数 (- 2 1) 将给出 1
* 两个操作数相乘 (* 2 2) 将给出 4
/ 分子除以分母 (float (/ 3 2)) 将给出 1.5
公司 增量运算符用于将操作数的值加 1 inc 5 将给出 6
十二月 增量运算符用于将操作数的值减 1 12月5日将给出4
最大限度 返回其参数中最大的一个 最大 1 2 3 将返回 3
分钟 返回其参数中最小的一个 分钟 1 2 3 将返回 1
雷姆 第一个数除以第二个数的余数 rem 3 2 将给出 1

关系运算符

关系运算符允许比较对象。以下是 Clojure 中可用的关系运算符。

显示示例

操作员 描述 例子
= 测试两个对象之间的相等性 (= 2 2) 将给出 true
不= 测试两个对象之间的差异 (not = 3 2) 将给出 true
< 检查左侧对象是否小于右侧操作数 (< 2 3) 将给出 true
<= 检查左侧对象是否小于或等于右侧操作数 (<= 2 3) 将给出 true
> 检查左侧对象是否大于右侧操作数 (> 3 2) 将给出 true
>= 检查左侧对象是否大于或等于右侧操作数 (>= 3 2) 将给出 true

逻辑运算符

逻辑运算符用于计算布尔表达式。以下是 Groovy 中可用的逻辑运算符。

显示示例

操作员 描述 例子
这是逻辑“与”运算符 (或 true true)将给出 true
或者 这是逻辑“或”运算符 (和 true false)将给出 false
不是 这是逻辑“非”运算符 (不是假的)会给出真实的

以下代码片段显示了如何使用各种运算符。

按位运算符

Clojure 提供了四个按位运算符。以下是 Clojure 中可用的按位运算符。

显示示例

先生。 运算符及描述
1

位与

这是按位“与”运算符

2

位或

这是按位“或”运算符

3

位异或

这是按位“异或”或互斥“或”运算符

4

位不

这是按位求反运算符

以下是显示这些运算符的真值表。

p q p&q p| q p^q
0 0 0 0 0
0 1 0 1 1
1 1 1 1 0
1 0 0 1 1

运算符优先级

与 LISP 的一般情况一样,无需担心运算符优先级。这是 S 表达式和前缀表示法的好处之一。所有函数均从左到右、从内到外进行计算。Clojure 中的运算符只是函数,所有内容都完全用括号括起来。

Clojure - 循环

到目前为止,我们已经看到了以顺序方式一个接一个地执行的语句。此外,Clojure 中还提供了语句来更改程序逻辑中的控制流。然后它们被分类为我们将详细看到的控制语句流。

先生。 循环和描述
1 While 语句

while 语句的执行方式是首先评估条件表达式(布尔值),如果结果为 true,则执行 while 循环中的语句。

2 剂量声明

“doseq”语句类似于许多其他编程语言中的“foreach”语句。doseq 语句主要用于迭代序列。

3 多时声明

“dotimes”语句用于执行语句“x”次。

4 循环语句

循环特殊形式不像“for”循环。Loop的用法与let绑定相同。然而,循环设置了一个递归点

Clojure - 决策

决策结构要求程序员指定一个或多个要由程序评估或测试的条件,以及如果条件被确定为真则要执行的一条或多条语句,以及可选的如果条件成立则要执行的其他语句。条件被确定为假。

先生。 方法与说明
1 如果语句

在 Clojure 中,条件是一个表达式,用于评估其为 true 或 false。“如果”条件为真,则将执行语句#1,否则将执行语句#2。

2 如果/做表达式

Clojure 中的“if-do”表达式用于允许为“if”语句的每个分支执行多个表达式。

3 嵌套 If 语句

多个“if”语句相互嵌入。

4 案例陈述

Clojure 提供的“case”语句类似于Java 编程语言中的“switch”语句。

5 条件声明

Clojure 提供了另一种评估语句,称为“cond”语句。该语句采用一组测试/表达式对。

Clojure - 函数

Clojure 被称为函数式编程语言,因此您会期望看到对 Clojure 中函数如何工作的大量强调。本章介绍了 Clojure 中的函数可以完成的所有操作。

先生。 功能及说明
1 定义函数

函数是使用“defn”宏定义的。

2 匿名函数

匿名函数是没有与其关联的名称的函数。

3 具有多个参数的函数

Clojure 函数可以使用零个或多个参数来定义。传递给函数的值称为参数,参数可以是任何类型。

4 可变参数函数

Clojure 提供的“case”语句类似于 Java 编程语言中的“switch”语句。

5 高阶函数

高阶函数 (HOF) 是以其他函数作为参数的函数。HOF 是一种重要的函数式编程技术,在 Clojure 中非常常用。

Clojure - 数字

Clojure 中的Numbers数据类型派生自 Java 类。

Clojure 支持整数和浮点数。

  • 整数是不包含分数的值。

  • 浮点数是包含小数部分的十进制值。

以下是 Clojure 中数字的示例。

(def x 5)
(def y 5.25)

其中“x”是Integer类型,“y”是float 类型

在 Java 中,以下类附加到 Clojure 中定义的数字。

数字

要实际查看 Clojure 中的数字是从 Java 类派生的,请使用以下程序查看使用“def”命令时分配的数字类型。

例子

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

;; This program displays Hello World
(defn Example []
   (def x 5)
   (def y 5.25)
   (println (type x))
   (println (type y)))
(Example)

“type”命令用于输出与分配给变量的值关联的类。

输出

上面的代码将产生以下输出。

Java.lang.long
Java.lang.double

数字测试

以下测试函数可用于数字。

先生。 编号和描述
1 零?

如果数字为零则返回 true,否则返回 false。

2 邮政编码?

如果 number 大于零,则返回 true,否则返回 false。

3 否定?

如果 number 小于零,则返回 true,否则返回 false。

4 甚至?

如果数字是偶数,则返回 true;如果数字不是整数,则抛出异常。

5 奇怪的?

如果数字是奇数,则返回 true;如果数字不是整数,则抛出异常。

6 数字?

如果数字确实是数字,则返回 true。

7 整数?

如果数字是整数,则返回 true。

8 漂浮?

如果数字是浮点数,则返回 true。

Clojure - 递归

我们在前面的主题中已经看到了 recur 语句,虽然“for”循环有点像循环,但recur是 Clojure 中真正的循环。

如果你有编程背景,你可能听说过尾递归,这是函数式语言的一大特性。这种递归特殊形式是实现尾递归的形式。正如“尾递归”一词所示,recur 必须在尾部位置调用。换句话说,recur 必须是最后评估的。

recur 语句的最简单示例是在“for”循环中使用。在以下示例中,recur 语句用于更改变量“i”的值并将该变量的值反馈回循环表达式。

例子

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

;; This program displays Hello World
(defn Example []
   (loop [i 0]
      (when (< i 5)
      (println i)
      (recur (inc i)))))
(Example)

输出

上述程序产生以下输出。

0
1
2
3
4

Clojure - 文件 I/O

Clojure 在使用 I/O 时提供了许多辅助方法。它提供了更简单的类来为文件提供以下功能。

  • 读取文件
  • 写入文件
  • 查看文件是文件还是目录

让我们探讨一下 Clojure 提供的一些文件操作。

将文件内容作为整个字符串读取

如果您想以字符串形式获取文件的全部内容,可以使用clojure.core.slurp方法。slurp 命令打开文件的读取器并读取其所有内容,返回一个字符串。

以下是如何完成此操作的示例。

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

;; This program displays Hello World
(defn Example []
   (def string1 (slurp "Example.txt"))
   (println string1))
(Example)

如果文件包含以下行,它们将被打印为 -

line : Example1
line : Example2

一次一行读取文件的内容

如果您想一次一行地以字符串形式获取文件的全部内容,可以使用clojure.java.io/reader方法。clojure.java.io/reader 类创建一个读取器缓冲区,用于读取文件的每一行。

以下示例展示了如何做到这一点。

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

;; This program displays Hello World
(defn Example []
   (with-open [rdr (clojure.java.io/reader "Example.txt")]
   (reduce conj [] (line-seq rdr))))
(Example)

如果文件包含以下行,它们将被打印为 -

line : Example1
line : Example2

输出将显示为 -

["line : Example1" "line : Example2"]

写入“至”文件

如果您想写入“to”文件,可以使用clojure.core.spit命令将整个字符串写入文件。spit 命令与 slurp 方法相反。此方法作为写入器打开文件,写入内容,然后关闭文件。

下面是一个例子。

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

;; This program displays Hello World
(defn Example []
   (spit "Example.txt"
      "This is a string"))

在上面的示例中,如果您查看Example.txt文件的内容,您将看到“This is a string”的内容。

一次一行写入“至”文件

如果您想一次一行写入“至”文件,可以使用clojure.java.io.writer类。clojure.java.io.writer 类用于创建写入器流,其中数据字节被馈送到流中,然后馈送到文件中。

以下示例展示了如何使用 spit 命令。

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

;; This program displays Hello World
(defn Example []
   (with-open [w (clojure.java.io/writer "Example.txt" :append true)]
      (.write w (str "hello" "world"))))
(Example)

执行上述代码时,Example.txt 文件中将出现“hello world”行。append:true 选项是将数据追加到文件中。如果未指定此选项,则每当将数据写入文件时,该文件都会被覆盖。

检查文件是否存在

要检查文件是否存在,可以使用clojure.java.io.file类来检查文件是否存在。以下示例展示了如何实现这一点。

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

;; This program displays Hello World
(defn Example []
   (println (.exists (clojure.java.io/file "Example.txt"))))
(Example)

如果文件Example.txt存在,则输出将为true。

从控制台读取

要从控制台读取数据,可以使用read-line语句。以下示例展示了如何使用它。

如果您在 REPL 窗口中输入 (read-line) 命令,您将有机会在控制台窗口中输入一些内容。

user->(read-line)
Hello World

上面的代码将产生以下输出。

“Hello World”

Clojure - 字符串

在 Clojure 中,字符串文字是通过将字符串文本括在引号中来构造的Clojure 中的字符串需要使用双引号构建,例如“Hello World”。

例子

以下是 Clojure 中字符串的使用示例。

(ns clojure.examples.hello
   (:gen-class))
(defn hello-world []
   (println "Hello World")
   (println "This is a demo application"))
(hello-world)

输出

上述程序产生以下输出。

Hello World
This is a demo application

基本字符串操作

Clojure 有许多可以对字符串执行的操作。以下是操作。

先生。 字符串操作及说明
1 斯特

字符串的连接可以通过简单的 str 函数来完成。

2 格式

字符串的格式化可以通过简单的 format 函数来完成。format 函数使用 java.lang.String.format 格式化字符串

3 数数

返回字符串中的字符数。

4 潜艇

返回 '​​s' 的子字符串,从 start 开始,到 end(默认为字符串长度)结束(不包括)。

5 比较

当“x”在逻辑上“小于”、“等于”或“大于”、“y”时,返回负数、零或正数。

6 小写

将字符串转换为全部小写。

7 大写

将字符串转换为全部大写。

8 加入

返回集合中所有元素的字符串,由 (seq collection) 返回,由可选分隔符分隔。

9 分裂

根据正则表达式分割字符串。

10 分割线

分割字符串基于转义字符\n或\r\n。

11 撤销

反转字符串中的字符。

12 代替

用替换字符串替换字符串中匹配的所有实例。

13 修剪

删除字符串两端的空格。

14 特里姆尔

删除字符串左侧的空格。

15 修剪

删除字符串右侧的空格。

Clojure - 列表

列表是一种用于存储数据项集合的结构。在 Clojure 中,List 实现了ISeq接口。列表是在 Clojure 中使用 list 函数创建的。

例子

以下是在 Clojure 中创建数字列表的示例。

(ns clojure.examples.example
   (:gen-class))
(defn example []
   (println (list 1 2 3 4)))
(example)

输出

上面的代码产生以下输出。

(1 2 3 4)

以下是在 Clojure 中创建字符列表的示例。

(ns clojure.examples.example
   (:gen-class))
(defn example []
   (println (list 'a 'b 'c 'd)))
(example)

上面的代码产生以下输出。

(a b c d)

以下是 Clojure 中可用的方法列表。

先生。 列表和说明
1 列表*

创建一个新列表,其中包含添加到其余项目之前的项目,其中最后一个将被视为序列。

2 第一的

该函数返回列表中的第一项。

3 第n个

此函数返回列表中第“n”个位置的项目。

4 缺点

返回一个新列表,其中一个元素被添加到列表的开头。

5 连杰

返回一个新列表,其中列表位于开头,要追加的元素位于末尾。

6 休息

返回列表中第一个项目之后的剩余项目。

Clojure - 集

Clojure 中的集合是一组唯一值。集合是在 Clojure 中借助 set 命令创建的。

例子

以下是在 Clojure 中创建集合的示例。

(ns clojure.examples.example
   (:gen-class))
(defn example []
   (println (set '(1 1 2 2))))
(example)

输出

上面的代码产生以下输出。

#{1,2}

以下是 Clojure 中可用于集合的方法。

先生。 套装及说明
1 排序集

返回一组已排序的元素。

2 得到

返回索引位置处的元素。

3 包含?

找出集合是否包含某个元素。

4 连杰

将一个元素追加到集合中并返回新的元素集。

5 迪斯杰

从集合中分离一个元素。

6 联盟

返回一个集合,该集合是输入集合的并集

7 不同之处

返回一个集合,该集合是第一个集合,不含其余集合的元素。

8 路口

返回一个集合,该集合是输入集合的交集。

9 子集?

set1 是 set2 的子集吗?

10 超集?

set1 是 set2 的超集吗?

Clojure - 向量

Vector是由连续整数索引值的集合。向量是使用 Clojure 中的向量方法创建的。

例子

以下是在 Clojure 中创建向量的示例。

(ns clojure.examples.example
   (:require [clojure.set :as set])
   (:gen-class))
(defn example []
   (println (vector 1 2 3)))
(example)

输出

上面的代码产生以下输出。

[1 2 3]

以下是 Clojure 中可用的方法。

先生。 矢量和描述
1 向量

创建单个基本类型“t”的新向量,其中“t”是 :int :long :float :double :byte :short :char 或 :boolean 之一。

2 第n个

此函数返回向量中第 n 个位置的项目。

3 得到

返回向量中索引位置的元素。

4 连杰

将一个元素附加到向量并返回新的向量元素集。

5 流行音乐

对于列表或队列,返回不带第一项的新列表/队列,对于向量,返回不带最后一项的新向量。

6 子向量

从起始索引和结束索引返回子向量。

Clojure - 地图

Map是将映射到值的集合。提供了两种不同的映射类型 - 散列映射和排序映射。HashMap需要正确支持 hashCode 和 equals 的键。SortedMap需要实现 Comparable 的键或 Comparator 的实例。

可以通过两种方式创建映射,第一种是通过哈希映射方法。

创建 - HashMap

HashMap 具有典型的键值关系,是使用 hash-map 函数创建的。

(ns clojure.examples.example
   (:gen-class))
(defn example []
   (def demokeys (hash-map "z" "1" "b" "2" "a" "3"))
   (println demokeys))
(example)

输出

上面的代码产生以下输出。

{z 1, b 2, a 3}

创建 - SortedMaps

SortedMap 具有根据键元素对其元素进行排序的独特特征。以下示例展示了如何使用排序映射函数创建排序映射。

(ns clojure.examples.example
   (:gen-class))
(defn example []
   (def demokeys (sorted-map "z" "1" "b" "2" "a" "3"))
   (println demokeys))
(example)

上面的代码产生以下输出。

{a 3, b 2, z 1}

从上面的程序中您可以清楚地看到映射中的元素是根据键值排序的。以下是可用于地图的方法。

先生。 地图和说明
1 得到

返回映射到键的值,未找到或 nil(如果键不存在)。

2 包含?

查看地图是否包含所需的密钥。

3 寻找

返回键的映射条目。

4

返回映射中的键列表。

5 瓦尔斯

返回地图中的值列表。

6 解散

将键值条目与映射解除关联。

7 合并

将两个映射条目合并为一个映射条目。

8 合并

返回一个映射,该映射由连接到第一个映射的其余映射组成。

9 选择键

返回一个映射,仅包含映射中键为 key 的条目。

10 重命名键

将当前 HashMap 中的键重命名为新定义的键。

11 地图反转

反转映射,使值成为键,反之亦然。

Clojure - 命名空间

Clojure 中的命名空间用于将类区分为单独的逻辑空间,就像 Java 中一样。考虑以下陈述。

(:require [clojure.set :as set])

在上面的语句中,“clojure.set”是一个命名空间,其中包含程序中要使用的各种类和方法。例如,上面的命名空间包含名为 map-invert 的函数,该函数用于反转键值映射。除非我们明确告诉我们的程序包含这个命名空间,否则我们不能使用这个函数。

让我们看看可用于命名空间的不同方法。

先生。 方法与说明
1 *纳秒*

这用于查看您当前的命名空间。

2 纳秒

这用于创建新的命名空间并将其与正在运行的程序关联。

3 别名

将当前命名空间中的别名添加到另一个命名空间。参数是两个符号:要使用的别名和目标命名空间的符号名称。

4 全NS

返回所有名称空间的列表。

5 查找-ns

查找并返回特定的命名空间。

6 ns 名称

返回特定命名空间的名称。

7 ns 别名

返回与任何名称空间关联的别名。

8 ns-地图

返回命名空间的所有映射的映射。

9 非别名

返回一个映射,仅包含映射中键为 key 的条目。

Clojure - 异常处理

任何编程语言都需要异常处理来处理运行时错误,以便维持应用程序的正常流程。异常通常会扰乱应用程序的正常流程,这就是我们需要在应用程序中使用异常处理的原因。

异常大致分为以下几类 -

  • 检查异常- 扩展 Throwable 类(除了 RuntimeException 和 Error)的类称为检查异常。例如IOException、SQLException等。受检异常是在编译时检查的。

让我们考虑以下程序,它对名为 Example.txt 的文件执行操作。然而,总是存在文件Example.txt不存在的情况。

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

;; This program displays Hello World
(defn Example []
   (def string1 (slurp "Example.txt"))
   (println string1))
(Example)

如果文件Example.txt不存在,则程序将生成以下异常。

Caused by: java.io.FileNotFoundException: Example.txt (No such file or
directory)
at java.io.FileInputStream.open0(Native Method)
at java.io.FileInputStream.open(FileInputStream.java:195)
at java.io.FileInputStream.<init>(FileInputStream.java:138)
at clojure.java.io$fn__9185.invoke(io.clj:229)
at clojure.java.io$fn__9098$G__9091__9105.invoke(io.clj:69)
at clojure.java.io$fn__9197.invoke(io.clj:258)
at clojure.java.io$fn__9098$G__9091__9105.invoke(io.clj:69)

从上面的异常中,我们可以清楚地看到程序引发了FileNotFoundException。

  • Unchecked Exception - 扩展 RuntimeException 的类称为未经检查的异常。例如,ArithmeticException、NullPointerException、ArrayIndexOutOfBoundsException 等。 Unchecked except