- Clojure 教程
- Clojure - 主页
- Clojure - 概述
- Clojure - 环境
- Clojure - 基本语法
- Clojure-REPL
- Clojure - 数据类型
- Clojure - 变量
- Clojure - 运算符
- Clojure - 循环
- Clojure - 决策
- Clojure - 函数
- Clojure - 数字
- Clojure - 递归
- Clojure - 文件 I/O
- Clojure - 字符串
- Clojure - 列表
- Clojure - 集
- Clojure - 向量
- Clojure - 地图
- Clojure - 命名空间
- Clojure - 异常处理
- Clojure - 序列
- Clojure - 正则表达式
- Clojure - 谓词
- Clojure - 解构
- Clojure - 日期和时间
- Clojure - 原子
- Clojure - 元数据
- Clojure - 结构映射
- Clojure - 代理
- Clojure - 观察者
- Clojure - 宏
- Clojure - 参考值
- Clojure - 数据库
- Clojure - Java 接口
- Clojure - 并发编程
- Clojure - 应用程序
- Clojure - 自动化测试
- Clojure - 库
- Clojure 有用资源
- Clojure - 快速指南
- Clojure - 有用的资源
- Clojure - 讨论
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 编程的方法。
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 安装的位置。单击“下一步”按钮继续。
步骤 5 - 单击“安装”按钮开始安装。
安装完成后,您将可以选择打开 Clojure REPL,这是一个可用于创建和测试 Clojure 程序的环境。
Eclipse 安装
在继续安装之前,请确保满足以下系统要求。
系统要求
JDK | JDK 1.7 或更高版本 |
---|---|
蚀 | 日食 4.5(火星) |
步骤 1 - 打开 Eclipse 并单击菜单项。单击帮助 → Eclipse Marketplace。
步骤 2 - 在出现的对话框中输入关键字 Clojure,然后点击“Go”按钮。将出现逆时针选项,单击安装按钮开始安装此插件。
步骤 3 - 在下一个对话框中,单击“确认”按钮开始安装。
步骤 4 - 在下一个对话框中,系统将要求您接受许可协议。接受许可协议并单击完成按钮继续安装。
安装将开始,完成后,它将提示您重新启动 Eclipse。
重新启动 Eclipse 后,您将在 Eclipse 中看到创建新 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 窗口中评估 Clojure 命令。
要在 Eclipse 中启动 REPL 会话,请单击“菜单”选项,转到“运行方式”→“Clojure 应用程序”。
这将在一个单独的窗口中启动一个新的 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