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