Julia 编程基础(二):开发 Julia 项目必须掌握的预备知识

本文是《Julia 编程基础》开源版本第 2 章:编程环境。本书旨在帮助编程爱好者和专业程序员快速地熟悉 Julia 编程语言,并能够在夯实基础的前提下写出优雅、高效的程序。这一系列文章由 郝林 采用 CC BY-NC-ND 4.0
知识共享 署名 – 非商业性使用 – 禁止演绎 4.0 国际 许可协议
)进行许可,请在转载之前仔细阅读上述许可协议。

本书的示例项目名为 Programs.jl,地址在 这里
。其中会包含本书所讲的大部分代码,但并不是那些代码的完全拷贝。这个示例项目中的代码旨在帮助本书读者更好地记忆和理解书中的要点。它们算是对书中代码的再整理和补充。

2.1 REPL 环境及其用法

命令 julia
为我们提供了一个简易但足够强大的编程环境。这就是 REPL 环境。不过
,REPL 环境并不是 Julia 语言独有的。像 Python、Ruby 这类现代的动态编程语言大都具备 REPL 环境。
Julia 的 REPL 环境非常有用,尤其是在我们进行试验性编程的时候。我们可以在其中试着执行一些代码片段,并在确认无误之后把这些片段在源码文件中组装起来,形成真正的程序。因此,REPL 环境绝对算得上一个优秀的编程学习助手。我起初就是借助这个环境来学习 Julia 编程的。

2.1.1 初探 REPL

我们已经知道,直接在命令行中输入 julia
并回车就可以进入 Julia 的 REPL 环境。下面我们就来说说在进入这个环境之后都能做些什么。

你还记得我们在上一章使用过的命令 julia -e 'a = 5 * 8; println(a)'
吗?现在,我们就在 REPL 环境中输入其中单引号里的表达式列表,像这样:


复制代码

julia> a =5*8; println(a)
40

julia>

可以看到,REPL 环境在对这两个表达式进行逐一求值之后就返回了。注意,如果对我们输入的最后一个表达式进行求值后可以得到显式的结果,那么这个结果也会被回显出来。这与通过前面的命令对表达式进行求值的效果是相同的。不过,它并不会返回到命令行,而是保留在当前的环境下。顺便说一句, julia>
是 Julia 的 REPL 环境下的提示符。
我们还可以对这两个表达式分别求值,如:


复制代码

julia> a =5*8
40

julia> println(a)
40

julia>

当我们输入 a = 5 * 8
并回车之后,REPL 环境会对它进行求值,并在完成后回显结果。如果你不想让它立即回显结果,那么可以在表达式的最后输入 ;
,就像这样:


复制代码

julia> a =5*8;

julia> println(a);
40

julia>

注意, 40
并不是表达式 println(a)‌
的结果。所以我们在它后面添加 ;
并不会影响到下面的回显内容。

另外,之所以 println(a)‌
这个表达式是合法的,是因为 a
在当下是一个已定义的变量。REPL 环境会暂存我们给予的所有表达式及其求值结果。所以,我们可以像写脚本程序那样在 REPL 环境里编程。

在 REPL 环境中,如果我们想再看一下上一个表达式的求值结果,那么仅需要输入 ans
并回车,如:


复制代码

julia> a =5*8;

julia> ans
40

julia>

我们在 REPL 环境中按下“向上箭头”键可以翻找曾经输入过的代码。这时,“向下箭头”键也是有用的,用于向更近时间的方向翻找。当然了,这样翻找的效率并不高,尤其是在你翻找很久以前的代码的时候。

如果你想在较大的时间范围内查找,我建议你去查阅一个名叫 repl_history.jl
的文件。它被保存在了 Julia 的日志目录下。比如,在 macOS 操作系统中,它的存放路径就是 ~/.julia/logs/repl_history.jl

2.1.2 主要的 4 种模式

Julia 的 REPL 环境主要有 4 种可供切换的模式。当我们输入 julia
命令并进入到 REPL 环境时,它会处于 julia 模式。在这种模式下,我们可以输入 Julia 语言的任何表达式,并让它帮我们计算结果。就像我们在前面做的那样。

我们在 julia 模式中紧挨着提示符输入 ]
,就会进入到 pkg 模式。此模式是用来管理程序包的。比如,在该模式下输入 add
就可以安装某个新的程序包。又比如,输入 update
就可以更新某个已安装的程序包。还比如,仅输入 up
并回车就可以更新所有已安装的程序包。示例如下:


复制代码

(v1.1) pkg> add DataFrames
Resolving package versions...
Installed DiffEqDiffTools ─ v0.12.0
Installed DataValues ────── v0.4.8
Updating`~/.julia/environments/v1.1/Project.toml`
[a93c6f00] + DataFrames v0.18.3
Updating`~/.julia/environments/v1.1/Manifest.toml`
[e7dc6d0d] ↑ DataValues v0.4.7⇒ v0.4.8
[01453d9d] ↑ DiffEqDiffTools v0.11.0⇒ v0.12.0

(v1.1) pkg>

注意,在 pkg 模式下,提示符会变为 (v1.1) pkg>
。其中的 v1.1
代表的是 Julia 语言的特性版本。

关于 pkg 模式支持的更多命令,你可以通过在该模式下输入 ?
或者 help
进行查询。另外,如果你想回到 julia 模式,那么可以同时按下 Ctrl
c

Julia 的 REPL 环境还支持 shell 模式。我们在 julia 模式中紧挨着提示符输入英文分号 ;
,就会进入到 shell 模式。这种模式让我们可以执行普通的 Shell 命令。当我们需要时不时地操纵文件系统时,这样会很方便,省去了我们在多个命令行之间切换的工作量。与 pkg 模式不同的是,在执行完一个 shell 命令之后,REPL 环境会自动地退出 shell 模式并切换回 julia 模式。

REPL 环境支持的第 4 种主要模式是 help 模式,或者说帮助模式。我们在 julia 模式中紧挨着提示符输入英文问号 ?
,就会进入到 help 模式。顾名思义,这种模式是用来查询 Julia 语言的帮助文档的。在该模式下,我们只要输入要查询的类型、变量、结构体、函数、宏等的名称并回车,REPL 环境就可以搜索并显示关于它的详细文档。随着搜索结果的显示,它会自动地切换回 julia 模式。

记住,我们在除了 julia 模式的另外 3 种模式下都可以进行手动地退出。手动退出的方式都是紧挨着提示符按下 Backspace
键或者同时按下 Ctrl
c

2.1.3 快捷键

除了 Ctrl+c
之外,Julia 的 REPL 环境还支持不少的快捷键。最常用的有:

  • Alt+←
    :将光标移动到左边最近的单词开始处。
  • Alt+→
    :将光标移动到右边最近的单词末尾处之后。
  • Ctrl+d
    :退出 julia
    命令,回到原始的命令行。
  • Ctrl+e
    :将光标移动到当前行的末尾处之后。
  • Ctrl+h
    :将光标移动到当前行的开始处。
  • Ctrl+l
    :清除当前界面中的历史命令,或称清屏。
  • Ctrl+r
    :向后搜索历史命令。
  • Ctrl+s
    :向前搜索历史命令。

关于更多的快捷键,你可以查阅 Julia 语言的 官方文档

在这里,我重点说一下用于搜索历史命令的快捷键,即: Ctrl+s
Ctrl+r
。当我们分别按下这两个快捷键之后,REPL 环境的提示符都会有所变化。当按下 Ctrl+s
时,提示符的前缀会变为 (forward-i-search)
;当按下 Ctrl+r
时,提示符的前缀会变为 (reverse-i-search)

这两个快捷键都可以让 REPL 环境进入到搜索模式。我在前面说过,我们在 REPL 环境中输入过的 Julia 表达式或者命令都会被保存在一个名为 repl_history.jl
的文件中。搜索模式实际上就是针对这个历史文件的。

很显然,这两个快键键分别用于从两个不同的方向在这个历史文件中搜索指定的内容。只要我们在这种模式下输入一些字符,它就可以实时地为我们查找相关的历史表达式或命令。此时,光标会自动地定位在匹配内容的开始处。并且,当我们再次按下 Ctrl+s
Ctrl+r
时,它还会立即为我们显示下一个或上一个匹配的内容。
一旦找到想要的表达式或命令,我们就可以直接按下回车键。这样就可以让 REPL 环境在切换到对应模式的同时把相应的表达式或命令粘贴过来。

举个例子。我们在 julia 模式中按下 Ctrl+r
,进入到搜索模式,并随即输入 add
。此时, REPL 环境的界面可能会是这样:


复制代码

(reverse-i-search)`add': add DataFrames

在反引号和单引号之间的就是我们刚才输入的内容,而在冒号后面的就是 REPL 环境为我们回显的第一个搜索结果。由于我们先前在 pkg 模式下安装过 DataFrames
程序包,所以这里的搜索结果是 add DataFrames

假设我们要寻找的就是这个命令,那么可以直接按下回车键。此时,上面那一行内容就会变成:


复制代码

(v1.1) pkg> add DataFrames

REPL 环境知道对应的表达式或命令是在哪个模式下输入的。这其实并不稀奇,历史文件中就保存了此类信息。

2.1.4 代码补全

我们在编程时,尤其是还不那么熟练的时候,通常记不住那么多可用的常量、函数、类型等等。不过不用担心,Julia 语言及其标准库(即官方提供的程序包的集合)的文档非常完善。这些文档可以从各个方面帮助我们用对、用好 Julia 语言。
在 REPL 环境中,我们除了可以很方便地查阅文档,还可以利用它的代码补全功能帮助我们选择要使用的程序定义(具体到这个例子中,就是函数定义),就像这样:


复制代码

julia> read[Tab]
read readavailable readchomp readline readlink
read! readbytes! readdir readlines readuntil
julia> read

这里的 read[Tab]
表示我在输入 read
之后又按下了 Tab
键(开始需要按两次,后续只需按一次)。这主要是为了让你更加清晰地看到输入与回显间的关系。

可以看到,在我输入 read[Tab]
之后,REPL 环境为我提供了很多可选的程序定义。这些都是函数,并且名称都是以 read
为前缀的。也就是说,我们记不住程序定义的全名没有关系,只要知道开头的几个字母就可以了。剩下的完全可以交给代码补全这个助手。另外,我们也可以利用这一功能进行编程的学习。就像前面展示的那样,我们可以借此了解到所有与“读”有关的函数都有哪些。
不仅如此,我们还可以利用代码补全功能即时地查询函数的使用方法,例如:


复制代码

julia> filter([Tab]
filter(f, a::Array{T,1}whereT)inBase at array.jl:2351
filter(f, Bs::BitArray)inBase at bitarray.jl:1710
filter(f, As::AbstractArray)inBase at array.jl:2312
filter(f, d::AbstractDict)inBase at abstractdict.jl:389
filter(pred, s::AbstractSet)inBase at abstractset.jl:332
filter(f, s::AbstractString)inBase at strings/basic.jl:581
julia> filter(

我在输入 filter(
之后又按下了 Tab
键,这样就可以看到 filter
函数以及从它衍生出的各种方法了。回显内容中也包含了每个函数或方法所在的模块和源码文件。

顺便说一下,在 Julia 语言中,函数代表的可以是一种泛化的功能。而能够作用于特定类型的参数的那些具体实现被称为方法。借助多重分派机制,Julia 可以根据我们在调用函数时给予的参数值的类型找到对应的方法并完成调用过程。对于上述示例,最前面展示的 filter(f, a::Array{T,1} where T)
代表的就是泛化的函数,而在它下面的那几个代表的就是具体的方法。

另外, Tab
键还可以对字符串中的文件路径进行补全,比如:


复制代码

julia> path ="/usr/[Tab]"
bin/ lib/ libexec/local/ sbin/ share/ standalone/
julia> path ="/usr/"

以上这些只是对 Tab
键的主要使用场景进行了简要的说明。这个代码补全的功能实际上比这里展示的更加强大。不过,其余的就留给你在 REPL 环境中自行探索吧。
到这里,我们已经讲了很多关于 REPL 环境的使用方法,包括:基本的表达式求值、4 种主要模式、常用的快捷键、代码补全功能等。除此之外 ,REPL 环境还支持复杂代码(比如含有流程控制语句的代码)的输入和求值。有了这些,你基本上就可以玩转 Julia 的 REPL 环境了,并且可以利用它大幅提高你的编程效率。

2.2 程序包与环境配置

我们已经知道,在 REPL 环境的 pkg 模式中,我们可以通过 add
命令安装新的程序包。那么,这些新的程序包都被存储到哪里了呢?

2.2.1 仓库目录

说到程序包的存储,就不得不提及 Julia 的项目环境(project environment)了。在这个项目环境下,包含了一组仓库目录。这些目录的路径会被包含在全局数组 DEPOT_PATH
中。比如,在我的 macOS 操作系统中,这个数组的信息如下:


复制代码

julia> DEPOT_PATH
3-elementArray{String,1}:
"/Users/haolin/.julia"
"/Applications/Julia-1.1.app/Contents/Resources/julia/local/share/julia"
"/Applications/Julia-1.1.app/Contents/Resources/julia/share/julia"

在默认情况下,我们自己安装的包都会被存储到第一个仓库目录的 packages
子目录中。在这里,该目录的路径是 /Users/haolin/.julia/packages
。我们可以称之为程序包存储目录。Julia 在为我们查找程序包的时候就会进入到这里。

顺便说一句,以 /Applications/Julia-1.1.app
开头的仓库目录只会在 macOS 操作系统下出现。如果是在 Linux 操作系统中,这里的仓库目录应该还会有 /usr/local/share/julia
/usr/share/julia

在程序包存储目录里,每一个程序包都会独占一个次级目录,而这些次级目录的名称会与对应程序包的名称一致。为了描述方便,我们可以称这样的次级目录为程序包目录。

由于 Julia 的程序包管理器(也就是 Pkg
包中的程序)允许我们安装名称相同但实则不同的多个程序包,所以在这些程序包目录中至少还会有一个再次一级的子目录。这些目录的命名看起来并没有什么规律。比如:


复制代码

$ls ~/.julia/packages/JSON
Hs3Dj ebvl3

可以看到,在程序包目录 JSON
中,还有 Hs3Dj
ebvl3
这两个子目录。实际上,此类子目录的名称是 Julia 根据程序包的具体信息(如 UUID 和源码仓库地址的哈希值)计算得出的唯一识别码。官方把这种识别码叫做 slug。因此,我们可以把这些再次一级的子目录称为 slug 目录。

2.2.2 环境配置

现在,让我们再把焦点扩散开来,并考虑一个问题:我们可以直接使用仓库目录中的所有程序包吗?你可能会想,既然程序包已经在这里了,那肯定是可以的。可事实并非如此。这又涉及到 项目环境包含的另一部分——环境配置。

Julia 的环境配置一般由两个配置文件代表,即: Project.toml
Manifest.toml
。前者叫做项目文件,后者叫做清单文件。对于 Julia 的全局环境配置,这两个文件通常会被存储到 ~/.julia/environments
目录中。又由于不同特性版本的 Julia 都会有独立的全局环境配置,所以对于特性版本为 v1.1
的 Julia 来说,它的全局环境配置文件会被保存在 ~/.julia/environments/v1.1
目录下。

那么,什么是环境配置?全局环境配置又是什么意思呢?这里的环境配置是指,用于确定 Julia 程序的依赖关系和规则的配置。更具体地说,它会规定用户编写的 Julia 程序在当前的项目环境下可以导入( import
)或使用( using
)哪些程序包。并且,环境配置还会帮助我们记录项目中程序的完整依赖关系图。
Julia 会把自身的安装环境看做是一个全局项目。显然,这个全局项目对于当前的 Julia 安装来说就是全局的。因此,针对此项目的环境配置就是我在前面所说的全局环境配置。其中配置的依赖规则主要针对于:

  • 在 REPL 环境下输入的 Julia 代码。
  • 不属于其他项目的独立的 Julia 程序。

举个例子。当我们在 REPL 环境中安装第一个程序包的时候,在程序包存储目录中就会出现相应的程序包目录。比如,我在 REPL 环境下第一次安装一个名叫 DataFrames
的程序包:


复制代码

(v1.1) pkg> add DataFrames
Cloning default registries into`~/.julia`
Cloning registry from"https://github.com/JuliaRegistries/General.git"
Added registry`General`to`~/.julia/registries/General`
Resolving package versions...
# 省略一些输出,此处会显示安装了哪些程序包。
Updating`~/.julia/environments/v1.1/Project.toml`
[a93c6f00] + DataFrames v0.18.3
Updating`~/.julia/environments/v1.1/Manifest.toml`
# 省略一些输出,此处会显示清单文件的更新细节。

在安装完成后,我的 ~/.julia/packages
目录的内容是这样的:


复制代码

$ls ~/.julia/packages
CategoricalArrays JSON Requires
Compat Missings SortingAlgorithms
DataFrames OrderedCollections StatsBase
DataStructures PooledArrays TableTraits
IteratorInterfaceExtensions Reexport Tables

可以看到,除了 DataFrames
之外,其中还包括了一些我们未曾相识的程序包目录。这些目录所代表的程序包实际上是 DataFrames
包的依赖包。在当前的项目环境下,这些程序包可以被 DataFrames
包使用,但却不能被我们编写的程序直接使用。为什么这么说呢?我们先来看一下当前环境中的项目文件( ~/.julia/environments/v1.1/Project.toml
)的内容:


复制代码

[deps]
DataFrames="a93c6f00-e57d-5684-b7b6-d8193f3e46c0"

此项目文件的 [deps]
部分会罗列我们显式安装的所有程序包的名称和 UUID。显而易见,这里只有 DataFrames
包。Julia 规定,环境配置所针对的项目中的程序只能直接依赖在该配置的项目文件中出现的那些程序包。

因此,当我们想在 REPL 环境下导入 JSON
包的时候,就会出现如下的情况:


复制代码

julia>importJSON
ERROR:ArgumentError: Package JSON not foundincurrent path:
- Run`import Pkg; Pkg.add("JSON")`to install the JSON package.

Stacktrace:
[1] require(::Module, ::Symbol) at ./loading.jl:823

julia>

Julia 向我们报告了错误并提示“在当前的路径下没有找到 JSON
包”,并且还告诉我们“可以通过调用 Pkg
包的 add
函数显式地安装这个包。这正是由于在当前环境的项目文件中并没有 JSON
包的记录而导致的。

顺便说一句,在 REPL 环境中,在 julia 模式下通过调用 Pkg.add
函数安装程序包与在 pkg 模式下通过 add
命令安装程序包是等同的。

下面,我们就按照 Julia 的提示安装 JSON
包:


复制代码

julia>importPkg; Pkg.add("JSON")
Resolving package versions...
Updating`~/.julia/environments/v1.1/Project.toml`
[682c06a0] + JSON v0.20.0
Updating`~/.julia/environments/v1.1/Manifest.toml`
[no changes]

julia>

由于 JSON
包已经存在于程序包存储目录中了,所以 Julia 无需再次下载。它只需要更新一下环境配置的文件即可。并且,由于这些程序包之间的依赖关系并没有改变,所以清单文件也没有任何变化。
现在,项目文件中的内容变为了:


复制代码

[deps]
DataFrames="a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
JSON="682c06a0-de6a-54ab-a142-c8b1cf79cde6"

我们此时再在 REPL 环境中直接导入 JSON
包就没有任何问题了:


复制代码

julia>importJSON
[ Info: Precompiling JSON [682c06a0-de6a-54ab-a142-c8b1cf79cde6]

julia>

顺便说一句,在我们第一次导入 JSON
包的时候,Julia 会先对该包进行预编译。这主要是为了让代码跑得更快。

2.3 项目的创建与引入

到目前为止,我们已经对 Julia 的项目环境有了一定的了解,并讨论了仓库目录、程序包存储目录和环境配置这几个重要的概念。

项目的环境配置一般由项目文件 Project.toml
和清单文件 Manifest.toml
体现。全局环境配置针对的是 REPL 环境中的代码和独立的 Julia 程序。该环境配置的文件通常会在 ~/.julia/environments
目录的特性版本子目录(如 v1.1
)中。
我们已经学会了在全局环境中怎样安装和导入程序包。这对于编写和部署一些脚本程序来说已经足够了。不过,我们在正式开发 Julia 项目的时候往往还需要专属的环境配置文件。这样才能与全局环境区别开。这么做有 3 个好处:

  1. 一旦有了专属的环境配置文件,我们的项目就可以独立地管理依赖包了。
  2. 针对某个 Julia 项目的程序包管理操作不会影响到全局的环境配置。反之亦然。
  3. 拥有环境配置文件的 Julia 项目可以为项目的分发(以供他人使用)做好准备。

换句话说,我们的 Julia 项目可以因此成为独立的、可重用的以及对分发友好的项目。

2.3.1 项目的创建

创建一个 Julia 项目很容易,在 REPL 环境中就可以办到。我们先在命令行中通过输入 julia
命令进入到 REPL 环境。然后,我们在它的 shell 模式中进入某个专用的目录(比如 ~/Projects
),就像这样:


复制代码

shell> cd ~/Projects/
/Users/haolin/Projects

julia>

这时,我们可以再确认一下当前的目录:


复制代码

julia> pwd()
"/Users/haolin/Projects"

julia>

这里的 pwd
是 Print Working Directory 的缩写。所以, pwd
函数的含义就是打印当前的工作目录。这与在命令行中输入 pwd
命令的作用是类似的。只不过调用表达式 pwd()
的求值结果是一个字符串。

在确认了工作目录之后,我们就可以切换到 REPL 环境的 pkg 模式,然后输入 generate
命令,并后跟一个空格和项目的名称 Programs
(你也可以用别的名字):


复制代码

(v1.1) pkg> generate Programs
Generating project Programs:
Programs/Project.toml
Programs/src/Programs.jl

(v1.1) pkg>

注意,我在 generate
命令后面追加的参数是我们要创建的 Julia 项目的名称。随后,这个命令创建了一个名为 Programs
的目录,并在该目录下生成了两个文件。一个是项目文件 Project.toml
,另一个是 src
目录(即源码目录)下的源码文件 Programs.jl

我们先来看项目文件,它的内容如下:


复制代码

name="Programs"
uuid="e525bb1a-bb1e-11e9-07f5-1125a61c95e2"
authors= ["robert.hao "]
version="0.1.0"

这里有 4 个条目,分别代表项目的名称、UUID、作者信息和初始版本号。其中的 UUID 是 Julia 的程序包管理器自动生成的。而项目作者信息是从当前操作系统中的 Git 配置信息复制过来的。

我们再来看源码文件 Programs.jl
的内容:


复制代码

modulePrograms

greet() = print("Hello World!")

end# module

其中只定义了一个名为 Programs
的模块。并且,该模块仅包含了一个可以向计算机的标准输出打印 Hello World!
的函数 greet
。这显然只是一个简单的程序模板。不过,它为我们后续的编码开了个头。
注意,这个源码文件是有重要意义的:

Programs
Programs

正因为有了这样的一个源码文件,使得 Programs
项目可以被 Julia 视为一个程序包。更明确地讲,如果存在一个名为 X
X.jl
的 Julia 项目,只要该项目包含一个相对路径为 src/X.jl
的源码文件,并且在该文件中定义的最外层模块名为 X
,那么它就是一个有效的程序包。

最后,一个可选的操作是,我们可以把这个项目的名称变更为 Programs.jl
。如此可以让它更具 Julia 项目的特色。由前述内容可知,这样做并不会妨碍此项目成为一个有效的程序包。注意,项目 Programs.jl
所代表的程序包的名称依然是 Programs
,同时它的主模块的名称也依然是 Programs

2.3.2 程序包的引入

既然 Programs.jl
项目已经是一个有效的程序包了,那么我们就可以在代码中对它进行引入(更明确地说,是引入它的主模块 Programs
)。具体怎么做呢?
当我们试图在全局环境中导入该程序包的时候,Julia 会提示找不到这个程序包:


复制代码

julia>importPrograms
ERROR:ArgumentError: Package Programs not foundincurrent path:
- Run`import Pkg; Pkg.add("Programs")`to install the Programs package.

Stacktrace:
[1] require(::Module, ::Symbol) at ./loading.jl:823

julia>

为了解决这个问题,我们可以先在 REPL 环境下进入到 Programs.jl
项目所在的目录,然后切换到 pkg 模式,并输入命令 activate .
。注意,这里的输入是 activate
加一个空格
,再加一个英文点号 .
。示例如下:


复制代码

shell> cd ~/Projects/Programs.jl
/Users/haolin/Projects/Programs.jl

(v1.1) pkg> activate .

(Programs) pkg>

我们可以看到,在使用 activate
命令之后,REPL 环境的提示符再次改变了,变成了当前程序包的名称 Programs
,也就是在当前目录下的 Project.toml
文件中记录的那个名称。命令 activate .
的作用正是把程序包管理器的操作目录切换到当前项目所在的目录,即: ~/Projects/Programs.jl
。还记得吗?它原先的(或者说默认的)操作目录是 ~/.julia/environments/v1.1
,对应于 Julia 的 v1.1
版本的全局环境。顺便说一下,如果你想切换回全局环境,那么只需要再次输入命令 activate
(不加任何参数)就可以了。

在这之后,我们再在当前的 REPL 环境中导入 Programs
就不会有问题了:


复制代码

julia>importPrograms
[ Info: Precompiling Programs [e525bb1a-bb1e-11e9-07f5-1125a61c95e2]

julia> Programs.greet()
Hello World!

如果我们确实需要在全局环境中引入 Programs
,那么可以先把这个项目上传到一个代码托管仓库(比如 GitHub)中,然后再使用 Julia 的程序包管理器把它安装到本地的仓库目录。

比如,我们的这个 Programs.jl
项目已经在 GitHub 上了,它的 git 地址是 git@github.com:hyper0x/Programs.jl.git
。所以,我们现在就可以直接在 REPL 环境中进行如下操作:


复制代码

(Programs) pkg> activate

(v1.1) pkg> add git@github.com:hyper0x/Programs.jl.git
Updating registry at`~/.julia/registries/General`
Updating git-repo`https://github.com/JuliaRegistries/General.git`
Cloning git-repo`git@github.com:hyper0x/Programs.jl.git`
Updating git-repo`git@github.com:hyper0x/Programs.jl.git`
Resolving package versions...
Updating`~/.julia/environments/v1.1/Project.toml`
[d2b7efac] + Programs v0.1.0#master (git@github.com:hyper0x/Programs.jl.git)
Updating`~/.julia/environments/v1.1/Manifest.toml`
[d2b7efac] + Programs v0.1.0#master (git@github.com:hyper0x/Programs.jl.git)

(v1.1) pkg>

一旦 Programs
程序包被记录在了全局环境的项目文件中,我们在该环境下引入它也就不会有问题了。

2.4 小结

在本章,我们一直在讨论 Julia 程序的编写环境。首先,我们详述了 Julia 语言的 REPL 环境的用法,包括在其中编写程序的简单规则、它的主要模式及其使用和切换方法、常用的快捷键,以及其中的代码补全功能。REPL 环境将会是我们编写 Julia 程序时最常用的。
之后,我们介绍了 Julia 项目环境的概念,并在此基础上讲解了仓库目录、程序包存储目录、环境配置文件,以及项目的创建和程序包的引入。这些内容都是我们在开发 Julia 项目时必须掌握的。
从下一章开始,我们就要正式地讲解 Julia 语言的语法了,包括各种程序定义的写法以及各式流程控制语句的用法。我相信,在掌握了前面所述的预备知识之后,你可以很轻松地开始下一阶段的学习。同时,你在之前留下的一些疑虑和问题,也将会在后面被陆续地解开。