B Julia语言入门

B.1 Julia的安装和运行

B.1.1 Julia程序语言介绍

Julia程序语言是一种计算机编程语言, 就像C、C++、Fortran、Java、R、Python、Matlab等程序语言一样。 Julia语言历史比较短,发布于2012年,是MIT的几位作者和全世界的参与者共同制作的。 主网站在https://julialang.org/

Julia与R、Python等一样是动态类型语言, 程序与R、Python一样简单, 但是它先进的设计使得Julia程序的效率基本达到和C、Fortran等强类型语言同样的高效率。 尤其适用于数值计算,在现今的大数据应用中也是特别合适的语言, 排在Python、R之后,已经获得广泛的关注, 现在用户较少只是因为历史还太短。

B.1.2 Julia软件安装和运行

从Julia网站下载安装Julia的命令行程序,称为REPL界面。

运行方式是在一个字符型窗口中, 在提示行julia>后面键入命令, 回车后在下面显示结果。

在MS Windows操作系统中, 设存放Julia源程序和数据文件的目录为c:\work, 将安装后显示在桌面上的Julia图标复制到目录中, 从右键选“属性”,将“起始位置”栏改为空白。 这样读写数据和程序文件的默认位置就是图标所在的目录。

Julia源程序用.jl作为扩展名, 为了运行当前目录中的“myprog.jl”文件, 在命令行用命令

julia> include("myprog.jl")

(其中julia>是自动显示的提示)。

B.1.3 Julia的其它运行方式

  1. Julia Computing公司下载Julia Pro的免费个人版。 其中包含了命令行程序,以及许多扩展包,尤其是绘图用的扩展包, 基于Atom编辑器的集成编辑和运行环境, 基于Jupyter的利用网络浏览器编辑、运行笔记本的IJulia功能。 这是一个本地安装比较简便的选择, 学习使用Julia的各种软件基本上都在一个安装包中提供了。

  2. 先注册一个Github网站的账号, 然后用Github账号登录https://www.juliabox.com网站, 可以用“笔记本”模式运行Julia。这是比较容易使用的一种方法, 不需要在本地安装软件。

  3. 安装Atom编辑器,从Atom编辑器中再安装其IJulia插件, 然后在Atom编辑器中编辑并运行Julia程序。 这称为Juno集成环境。第1条的Julia Pro软件包含了这一功能。

  4. 安装Jupyter软件,然后在一个网络浏览器中运行Julia。 与JuliaBox类似,但是在本地安装和运行。 安装方法是在命令行程序中运行

    Pkg.add("IJulia")

    这需要从网上下载许多软件并自动安装, 因为网络环境限制有可能会失败。 第1条的Julia Pro软件包含了这一功能。

B.2 Julia的基本数据和相应运算

B.2.1 整数与浮点数

Julia程序中的整数值可以直接写成如123或者-123这样。 虽然整数有多种类型, 一般程序中不必特别关心整数常量的具体类型。 Julia允许使用特别长的整数,这时其类型为BigInt。

Julia的浮点数可以写成带点的形式如123.0, 1.23, 也可以写成带有10的幂次如1.23e3(表示\(1.23\times 10^3\)), 1.23e-3(表示\(1.23\times 10^{-3}\))。 这些写法都属于Float64类型的浮点数。 Julia还有其他类型的浮点数,但是科学计算中主要使用Float64类型, 在别的语言中这称为双精度浮点数。

B.2.2 四则运算

表示加、减、乘、除、乘方的运算符分别为:

+  -   *  /  ^

浮点数的四则运算遵循传统的算数运算规则和优先级规定。如

4.74

表示 \[1.3 + 2.5 \times 2.0 - 3.6 \div 1.2 + 1.2^2\]

B.2.3 整数的四则运算

整数加法、减法、乘法结果仍为整数, 这样因为整数的表示范围有限,有可能发生溢出。 如

4

整数用“/”作的除法总是返回浮点数,即使结果是整数也是一样:

5.0

整数用a % b表示a整除b的余数,结果符号总是取a的符号。如

1

整数与浮点数的混合运算会将整数转换成浮点数再计算。

B.2.4 字符串

单个字符在两边用单撇号界定,如'A''囧'。 字符都是用Unicode编码存储,具体使用UTF-8编码。

零到多个字符组成字符串, 程序中的字符串在两边用双撇号界定,如 "A cat""泰囧"

对于占据多行的字符串, 可以在两侧分别用三个双撇号界定。如

"这是第一行\n这是第二行\n三个双撇号界定的字符串中间的单个双撇号\"不需要转义\n"

B.2.5 字符串连接

用星号“*”连接两个字符串,如

"过去的历史不能忘记"

B.3 变量

B.3.1 变量

变量名是一个标识符, 用来指向某个值在计算机内存中的存储位置。 变量名可以用英文大小写字母、下划线、数字、允许的Unicode字符。 变量名不允许使用空格、句点以及其它标点符号和井号之类的特殊字符。

给变量赋值,即将变量名与一个内存中的内容联系起来,也称为绑定(binding), 使用等号“=”,等号左边写变量名,右边写要保存到变量中的值。如

123
2.5
"北京市海淀区颐和园路5号"

变量的类型是由它保存的(指向的内存中的)值的类型决定的, 不需要说明变量类型(Julia允许说明变量类型,但一般不需要)。

变量赋值后,就可以参与运算,如:

128.0

B.3.2 简单的输出

在Julia命令行,键入变量名或者计算表达式直接在下面显示结果。 可以用println()函数显示指定的变量和结果。如

128.0
x=123 y=2.5 x + y*2 =128.0

B.4 向量

B.4.1 向量

在程序中直接定义一个向量,用方括号内写多个逗号分隔的数值,如

5-element Array{Int64,1}:
  1
  3
  4
  9
 13
4-element Array{Float64,1}:
 1.5 
 3.0 
 4.0 
 9.12

其中v1是整数型的向量, v2是浮点型Float64的向量。

向量实际上是一维数组。

可以用1:5定义一个范围, 在仅使用其中的元素值而不改写时作用与[1, 2, 3, 4, 5]类似。 1:2:9定义带有跨度的范围, 与[1, 3, 5, 7, 9]类似。

1:5
1:2:9

B.4.2 向量下标

x是向量,i是正整数,x[i]表示向量的第i个元素。如

3

对元素赋值将在原地修改元素的值,如

5-element Array{Int64,1}:
    1
 -999
    4
    9
   13

B.4.3 用范围作为下标

下标可以是一个范围,如

3-element Array{Int64,1}:
 -999
    4
    9

在这种范围中,用end表示最后一个下标,如

2-element Array{Int64,1}:
  9
 13
4-element Array{Int64,1}:
    1
 -999
    4
    9

B.4.4 数组作为下标

向量的下标也可以是一个下标数组,如

3-element Array{Int64,1}:
  1
  4
 13

取出的多个元素可以修改,可以赋值为同一个标量,如:

5-element Array{Int64,1}:
  0
  0
  0
  9
 13

也可以分别赋值,如

5-element Array{Int64,1}:
 101
   0
 303
   9
 505

B.4.5 向量与标量的运算

向量与一个标量作四则运算, 将运算符前面加句点“.”:

.+   .-   .*   ./   .^

表示向量的每个元素分别与该标量作四则运算, 结果仍是向量。如

5-element Array{Int64,1}:
 101
 103
 104
 109
 113
5-element Array{Int64,1}:
 99
 97
 96
 91
 87
5-element Array{Int64,1}:
  2
  6
  8
 18
 26
5-element Array{Float64,1}:
 0.1
 0.3
 0.4
 0.9
 1.3
5-element Array{Int64,1}:
   1
   9
  16
  81
 169

B.4.6 向量与向量的四则运算

两个等长的向量之间作加点的四则运算,表示对应元素作相应的运算。如

5-element Array{Int64,1}:
  3
  8
 10
 16
 23
5-element Array{Int64,1}:
 -1
 -2
 -2
  2
  3
5-element Array{Int64,1}:
   2
  15
  24
  63
 130
5-element Array{Float64,1}:
 0.5     
 0.6     
 0.666667
 1.28571 
 1.3     

B.4.7 向量初始化

zeros(n)可以生成元素类型为Float64、元素值为0、长度为n的向量,如

5-element Array{Float64,1}:
 0.0
 0.0
 0.0
 0.0
 0.0

VectorFloat64(n)可以生成元素类型为Float64的长度为n的向量, 元素值未初始化,如

5-element Array{Float64,1}:
 1.97841e-315
 3.91086e-316
 3.89761e-316
 3.99354e-316
 3.86942e-316

类似可以生成其它元素类型的向量,如

5-element Array{Int64,1}:
 115410272
  78323312
 400675248
  78348688
  78446672

用这样的办法为向量分配存储空间后可以随后再填入元素值。

B.4.8 向量复制

定义一个向量后, 此向量的变量名就“绑定”在某个内存地址上。 修改变量元素实际是修改了变量名所绑定的地址中每个位置的值。如

5-element Array{Int64,1}:
  2
  3
  0
  7
 11

将向量赋给另外一个向量,并不能实现复制;两个向量实际上是绑定到了相同的内存地址。如

5-element Array{Int64,1}:
  2
  3
  0
  7
 11
5-element Array{Int64,1}:
 97
  3
  0
  7
 11

可以看出修改了v的元素值,w的元素值也被修改了,因为这两个变量名指向的是相同的内存地址。 为了制作一个向量的副本,不能使用直接赋值的方法,而是使用copy()函数。如

5-element Array{Int64,1}:
  2
  3
  5
  7
 11

v被修改后,其副本w未受影响。 另外,给w另外赋值,相当于解除了w原来的绑定, 并将w绑定到了将v的内容复制到另一内存地址的副本地址上。

B.4.9 向量的循环遍历

可以用for循环对向量的每个元素遍历访问。格式如

v1[1] = 1
v1[2] = 3
v1[3] = 4
v1[4] = 9
v1[5] = 13

这里i是循环产生的向量下标。

B.4.10 向量的输出

v2 = [1.5, 3, 4, 9.12], 为了将其按显示格式保存到文件“tmp1.txt”中,可用如下代码:

writedlm("tmp1.txt", v2, ' ')

结果文件中每个数占一行。

B.4.11 向量的输入

假设文件“vecstore.txt”中包含如下的内容:

1.2 -5  3.6
7.8 9.12 4.11

可以用如下代码将文件中的数据读入到一个向量v4中:

v4 = readdlm("vecstore.txt")[:]; v4

B.5 矩阵

B.5.1 矩阵

前面讲的向量是一维数组,不区分行向量还是列向量。

矩阵是二维数组,有两个下标:行下标和列下标。

在方括号内两个同行的元素之间用空格分隔, 两行之间用分号分隔,可以在程序中输入矩阵,如

2×3 Array{Int64,2}:
 1  2  3
 4  5  6

也可以直接用换行符分隔不同行,例如

B.5.2 矩阵下标

A是矩阵,则A[i,j]表示A的第i行第j列元素,如:

6

给元素赋值可以在矩阵中修改元素值,如:

2×3 Array{Int64,2}:
 1  2   3
 4  5  -6

B.5.3 矩阵列和行

A是矩阵, 则A[:, j]表示A的第j列元素组成的向量(一维数组),如

2-element Array{Int64,1}:
 2
 5

A[i, :]表示A的第i行元素组成的向量(一维数组),如

3-element Array{Int64,1}:
  4
  5
 -6

取出后的列或者行不分行向量和列向量。

B.5.4 子矩阵

如果A是矩阵,IJ是范围或者向量, 则A[I,J]表示A的行号在I中的行与列号在J中的列交叉所得的子矩阵,如

2×2 Array{Int64,2}:
 2   3
 5  -6

用冒号“:”作为行下标或列下标表示取该维的全部下标。

B.5.5 子矩阵赋值

可以给子矩阵赋值,赋值为一个标量使得对应元素都改为该标量值;如

2×3 Array{Int64,2}:
 1  0  0
 4  0  0

给子矩阵赋值为一个同样大小的子矩阵给对应元素赋值,如

2×3 Array{Int64,2}:
 1  102  103
 4  202  203

B.5.6 矩阵初始化

zeros(m, n)可以生成元素类型为Float64、元素值为0的\(m\times n\)矩阵,如

2×3 Array{Float64,2}:
 0.0  0.0  0.0
 0.0  0.0  0.0

ArrayFloat64(m, n)可以生成元素类型为Float64的\(m \times n\)矩阵, 元素值未初始化,如

2×3 Array{Float64,2}:
 3.90752e-316  6.78756e-316  5.18128e-316
 6.61297e-316  6.81373e-316  3.87102e-316

类似可以生成其它元素类型的矩阵,如

2×3 Array{Int64,2}:
 386423696  78323216  78323216
  79156592  78323216  78331944

用这样的办法先为矩阵分配存储空间,然后可以再填入元素值。

B.5.7 矩阵元素遍历

对行下标和列下标分别循环,行下标变化最快,如

2×3 Array{Int64,2}:
 1  2  3
 4  5  6
A1[1, 1] = 1
A1[2, 1] = 4
A1[1, 2] = 2
A1[2, 2] = 5
A1[1, 3] = 3
A1[2, 3] = 6

另一种办法是用类似于向量遍历的方法,如:

A1[1] = 1
A1[2] = 4
A1[3] = 2
A1[4] = 5
A1[5] = 3
A1[6] = 6

B.5.8 矩阵读入

设当前目录中文件“vecstore.txt”中包含如下内容:

1.2 -5  3.6
7.8 9.12 4.11

将文件中的内容每一行看作矩阵的一行, 文件中保存了一个\(2 \times 3\)矩阵。 读入方法如下:

Ain = readdlm("vecstore.txt"); Ain

B.5.9 保存矩阵到文件中

考虑上面的Ain矩阵,为了将其按文本文件格式保存到“tmp2.txt”中, 用如下程序:

writedlm("tmp2.txt", Ain, ' ')

B.5.10 矩阵与标量的四则运算

矩阵与一个标量之间用加点的四则运算符号进行运算, 与向量和标量之间的运算类似, 表示矩阵的每个元素和该变量的四则运算, 结果仍为矩阵。如

2×3 Array{Int64,2}:
 1  2  3
 4  5  6
2×3 Array{Int64,2}:
 101  102  103
 104  105  106
2×3 Array{Int64,2}:
 99  98  97
 96  95  94
2×3 Array{Int64,2}:
 2   4   6
 8  10  12
2×3 Array{Float64,2}:
 0.1  0.2  0.3
 0.4  0.5  0.6
2×3 Array{Int64,2}:
  1   4   9
 16  25  36

B.5.11 两个矩阵之间的四则运算

两个同样大小的矩阵之间用加点的四则运算符号进行运算, 表示两个矩阵的对应元素的运算。如

2×3 Array{Int64,2}:
 100  200  300
 400  500  600
2×3 Array{Int64,2}:
 101  202  303
 404  505  606
2×3 Array{Int64,2}:
  99  198  297
 396  495  594
2×3 Array{Int64,2}:
  100   400   900
 1600  2500  3600
2×3 Array{Float64,2}:
 100.0  100.0  100.0
 100.0  100.0  100.0

B.5.12 矩阵乘法

A * B表示矩阵乘法。 如

2×2 Array{Int64,2}:
 11  12
 21  22
2×3 Array{Int64,2}:
  59   82  105
 109  152  195

一个矩阵与一个向量(一维数组)作矩阵乘法, 向量自动变成列向量,如:

2-element Array{Int64,1}:
 -1
 -1

注意结果是向量(一维数组),而不是\(2 \times 1\)矩阵(二维数组)。

行向量可以直接表示成方括号内多个数值之间用空格分隔的格式,如

2×2 Array{Int64,2}:
  1  -1
 -1   1

又如

1×2 Array{Int64,2}:
 -10  -10

注意结果是\(1 \times 2\)矩阵,即行向量,而不是向量。 向量是一维数组,行向量是二维数组。

从以上例子可以看出在矩阵运算中向量可以看成是列向量, 矩阵乘法结果如果是列向量,也会表示成向量(一维数组)。

B.5.13 矩阵转置

A.’表示矩阵A的转置; 用A’表示矩阵A的共轭转置。 如

2×3 Array{Int64,2}:
 1  2  3
 4  5  6
3×2 Array{Int64,2}:
 1  4
 2  5
 3  6

两个向量x和y的内积用dot(x, y)表示, 而不要写成x.’ * y。如

-1

B.5.14 矩阵求逆和解线性方程组

inv(A)表示\(A^{-1}\)。如

2×2 Array{Int64,2}:
 1  3
 3  1
2×2 Array{Float64,2}:
 -0.125   0.375
  0.375  -0.125

A \ B表示\(A^{-1} B\), 当B是向量或者列向量时, 就是求解线性方程组\(A x = B\)中的\(x\)。 如

2-element Array{Float64,1}:
  1.0
 -1.0

B.6 自定义函数

B.6.1 自定义函数

Julia语言支持自定义函数。 在计算机程序语言中, 函数是代码模块化、代码复用的基础。 将常用的计算或者操作写成函数以后, 每次要进行这样的计算或者操作, 只要简单地调用函数即可。 将复杂的任务分解成一个个函数, 可以使得任务流程更加明晰, 任务的不同阶段之间减少互相干扰。

B.6.2 自定义函数的单行格式

类似于\(f(x) = x^2 + 3 x + 1\)这样的简单函数, 可以用一行代码写成

f (generic function with 1 method)

调用如

11
5.510000000000001

B.6.3 自定义函数的多行格式

需要用多行才能完成的计算或者操作, 就需要写成多行的形式。格式如

function funcname(x, y, z)
  ...
end

其中funcname是函数名称, x, y, z等是自变量名, ...是函数内的语句或表达式, 函数以最后一个表达式为返回值(结果)。

例如,写一个函数,以向量的形式输入一个变量的样本值, 计算样本标准差: \[s = \sqrt{ \frac{1}{n-1} \sum_{i=1}^n (x_i - \bar x)^2 }\] 自定义函数如

mysd (generic function with 1 method)

调用如

1.5811388300841898

事实上,上面的函数定义可以用已有的一些函数进行简化。 比如,sum(x)对向量x的元素求和,于是以上的函数可以写成:

mysd_simple (generic function with 1 method)

第二个版本只是利用了向量化和已有函数, 在Julia中其运行效率并不比第一个版本更好, 甚至于不如第一个版本。 Julia语言与R、Matlab等语言不同, 显式的循环一般有更高的执行效率, 向量化写法仅仅是可以使得程序比较简洁。

B.7 程序控制结构

B.7.1 复合表达式

begin ... end可以将多行的多个表达式组合起来当作一个表达式, 复合表达式的值是其中最后一个表达式的值。 如

3

多个表达式也可以用分号分隔后写在圆括号中,作为一个复合表达式,如

3

B.7.2 比较运算

两个数值之间用如下的比较运算符进行比较:

==   !=   <   <=   >   >=

分别表示等于、不等于、小于、小于等于、大于、大于等于。 要特别注意“等于”比较用两个等号表示。

比较的结果是true(真值)或者false(假值)。 结果类型为Bool类型(布尔型)。

true
true
true
true
true
true
false
false

两个字符串之间也可以比较, 比较时按字典序比较, 两个字符的次序按照其Unicode编码值比较。如

false
true
true

B.7.3 逻辑运算

比较通常有变量参与。如

false
true

有时需要构造复合的条件, 如“年龄不足18岁且性别为女”, “年龄在18岁以上或者性别为男”等。

&&表示要求两个条件同时成立, 用||表示只要两个条件之一成立则结果为真, 用!cond表示cond的反面。 如

false
true

B.7.4 短路与运算和分支

&& 是一种短路运算, 表达式cond && expr 仅当cond为true时才计算(运行)expr, 所以这种写法经常用作程序分支的简写: 条件cond为真时执行expr, 否则不执行。

比如,在计算x的平方根之前,先判断其非负:

平方根计算:自变量定义域错误,x=-1.44

B.7.5 短路或运算与分支

||是一种短路或运算,表达式cond || expr 仅当cond为false时才计算(运行)expr, 所以这种写法经常作为程序分支的缩写: 条件cond为假时才执行expr,否则不执行。

比如,求平方根时当自变量不为负时才计算平方根:

true

B.7.6 if–end结构

可以用if cond ... end结构在条件cond成立时才执行某些语句,如

√1.44 = 1.2

注意条件不需要用括号包围,结构以end语句结尾。

B.7.7 if–else–end结构

if cond ... else ... end结构当条件成立时执行第一个分支中的语句, 当条件不成立时执行第二个分支中的语句。 如

√-1.44 = 1.2i

B.7.8 if-elseif-else-end结构

if cond1 ... elseif cond2 ... else ... end可以有多个分支, 有多个条件cond1, cond2, ……, 依次判断各个条件,那个条件成立就执行对应分支的语句, 所有条件都不成立则执行else分支的语句。 条件cond2隐含条件cond1不成立, cond3隐含cond1cond2都不成立, 依此类推。 例如

中青年

B.7.9 三元运算符

可以用cond ? expr1 : expr2表示比较简单的两分支选择, 当cond成立时结果为expr1的结果, 当cond不成立时结果为expr2的结果。 如

1.2

B.7.10 for循环

for循环一般是沿着某个范围进行计算或处理,格式如下:

for loopvar = a:b
  expr1
  expr2
  ...
end

其中loopvar是自己命名的循环变量名, for结构块内的语句(表达式)先对loopvar=a运行, 再对loopvar=a+1运行, 最后对loopvar=b运行,然后结束。 如

1^3 = 1
2^3 = 8
3^3 = 27

范围也可以是1:2:9, 0:0.1:1这样的带有跨度(增量)的, 可以是倒数的如3:-1:1表示3, 2, 1。

B.7.11 对向量元素循环

in关键字,可以使得循环变量遍历某个向量的元素,如:

2^3 = 8
3^3 = 27
5^3 = 125
7^3 = 343
11^3 = 1331

B.7.12 向量元素循环

x是一个向量,上面的做法可以遍历x每个元素。 有时还需要按照向量的下标遍历,这时使用eachindex(x),如

Prime No. 1 = 2
Prime No. 2 = 3
Prime No. 3 = 5
Prime No. 4 = 7
Prime No. 5 = 11

B.7.13 理解(Comprehension)

对向量的循环,经常可以表达成一种称为comprehension的语法。 例如,为了生成1, 2, 3的立方的向量,可以写成

3-element Array{Int64,1}:
  1
  8
 27

对前5个素数作立方,可以写成

5-element Array{Int64,1}:
    8
   27
  125
  343
 1331

B.7.14 两重for循环

for循环可以嵌套,如

1×1 = 1  
2×1 = 2  2×2 = 4  
3×1 = 3  3×2 = 6  3×3 = 9  
4×1 = 4  4×2 = 8  4×3 = 12  4×4 = 16  
5×1 = 5  5×2 = 10  5×3 = 15  5×4 = 20  5×5 = 25  
6×1 = 6  6×2 = 12  6×3 = 18  6×4 = 24  6×5 = 30  6×6 = 36  
7×1 = 7  7×2 = 14  7×3 = 21  7×4 = 28  7×5 = 35  7×6 = 42  7×7 = 49  
8×1 = 8  8×2 = 16  8×3 = 24  8×4 = 32  8×5 = 40  8×6 = 48  8×7 = 56  8×8 = 64  
9×1 = 9  9×2 = 18  9×3 = 27  9×4 = 36  9×5 = 45  9×6 = 54  9×7 = 63  9×8 = 72  9×9 = 81  

这种两重循环可以简写为一个for语句,外层循环先写, 内层循环后写,中间用逗号分隔。 如

1×1 = 1  
2×1 = 2  2×2 = 4  
3×1 = 3  3×2 = 6  3×3 = 9  
4×1 = 4  4×2 = 8  4×3 = 12  4×4 = 16  
5×1 = 5  5×2 = 10  5×3 = 15  5×4 = 20  5×5 = 25  
6×1 = 6  6×2 = 12  6×3 = 18  6×4 = 24  6×5 = 30  6×6 = 36  
7×1 = 7  7×2 = 14  7×3 = 21  7×4 = 28  7×5 = 35  7×6 = 42  7×7 = 49  
8×1 = 8  8×2 = 16  8×3 = 24  8×4 = 32  8×5 = 40  8×6 = 48  8×7 = 56  8×8 = 64  
9×1 = 9  9×2 = 18  9×3 = 27  9×4 = 36  9×5 = 45  9×6 = 54  9×7 = 63  9×8 = 72  9×9 = 81  

B.7.15 矩阵元素遍历

矩阵元素按照行列下标遍历,可以写成两重循环的形式。 Julia的矩阵是按列存储的, 所以循环时先对第一列各个元素循环, 再对第二列各个元素循环, ……,按这样的次序遍历是比较有效的。 如

A1[1, 1] = 1
A1[2, 1] = 4
A1[1, 2] = 2
A1[2, 2] = 5
A1[1, 3] = 3
A1[2, 3] = 6

for结构的两重循环中写在前面的是外层循环, 循环变量变化较慢, 写在后面的是内层循环,循环变量变化较快。 上例中列下标j写在前面,行下标i写在后面, 所以关于列的循环j是外层循环,关于行的循环i是内层循环, 这样的矩阵元素遍历方式是按列次序遍历。

B.7.16 矩阵的comprehension

在方括号内用两重的循环变量遍历可以定义矩阵。如

2×3 Array{Int64,2}:
 101  102  103
 201  202  203

这里写在前面的循环变量i对应于行下标, 写在后面的循环变量i对应于列下标。 执行时行下标i在内层循环,列下标j在外层循环。

又如,如下程序返回矩阵对角化的结果:

2×3 Array{Int64,2}:
 101    0  0
   0  202  0

B.7.17 while循环

for循环适用于对固定的元素或者下标的遍历, 在预先未知具体循环次数时,需要使用当型循环循环或者直到型循环。

Julia中用while cond ... end表示当型循环, 在条件cond成立时执行结构内的语句, 直到cond不成立时不再循环。

例如,设向量x中保存了许多\[0,1\]上的随机数, 依次查询直到找到第一个大于0.99的为止:

i = 113 y = 0.9925011556689483

B.7.18 直到型循环与break语句

当型循环每次进入循环之前判断循环条件是否成立, 成立才进入循环。

直到型循环每次先进入循环,在循环末尾判断循环退出条件是否满足, 满足退出条件时就不再循环。 Julia语言没有提供专门的直到型循环语法,可以用如下的方法制作直到型循环:

while true
  expr1
  expr2
  ...
  cond && break
end

其中cond是循环退出条件。break语句表示退出一重循环。

例如,用泰勒展开近似计算自然对数\(\log(1 + x)\)\[\log(1 + x) = x + \sum_{k=2}^\infty (-1)^{k-1} \frac{x^k}{k}\]

实际计算时不可能计算无穷次,所以指定一个精度如eps=0.0001, 当计算的通项小于此精度时停止计算。 程序用直到型型循环写成:

eps = 0.0001 log(1+1.0) = 0.6931971730609582 Iterations: 10001