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的其它运行方式
安装REPL界面后,下载安装Atom编辑器, 从Atom编辑器中再安装其IJulia插件, 然后在Atom编辑器中编辑并运行Julia程序。 这称为Juno集成环境。
安装Anaconda软件,并安装Julia的IJulia模块, 将IJulia所需要的Python和Jupyter可执行程序的路径指定为安装的Anaconda软件的位置。 这可以同时获得Python语言和一些科学计算、数据整理、统计建模、机器学习等软件。
参见http://www.math.pku.edu.cn/teachers/lidf/docs/Julia/JuliaIntro.html。
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 四则运算
表示加、减、乘、除、乘方的运算符分别为:
+ - * / ^
浮点数的四则运算遵循传统的算数运算规则和优先级规定。如
1.3 + 2.5*2.0 - 3.6/1.2 + 1.2^2
4.74
表示 \[1.3 + 2.5 \times 2.0 - 3.6 \div 1.2 + 1.2^2\]
B.2.3 整数的四则运算
整数加法、减法、乘法结果仍为整数, 这样因为整数的表示范围有限,有可能发生溢出。 如
10 + 2*3 - 3*4
4
整数用“/”作的除法总是返回浮点数,即使结果是整数也是一样:
10/2
5.0
整数用a % b
表示a整除b的余数,结果符号总是取a的符号。如
10 % 3
1
整数与浮点数的混合运算会将整数转换成浮点数再计算。
B.2.4 字符串
单个字符在两边用单撇号界定,如'A'
,'囧'
。
字符都是用Unicode编码存储,具体使用UTF-8编码。
零到多个字符组成字符串, 程序中的字符串在两边用双撇号界定,如 "A cat"
,"泰囧"
。
对于占据多行的字符串, 可以在两侧分别用三个双撇号界定。如
"""
这是第一行
这是第二行
三个双撇号界定的字符串中间的单个双撇号"不需要转义
"""
"这是第一行\n这是第二行\n三个双撇号界定的字符串中间的单个双撇号\"不需要转义\n"
B.2.5 字符串连接
用星号“*”连接两个字符串,如
"过去的" * "历史" * "不能忘记"
"过去的历史不能忘记"
B.3 变量
B.3.1 变量
变量名是一个标识符, 用来指向某个值在计算机内存中的存储位置。 变量名可以用英文大小写字母、下划线、数字、允许的Unicode字符。 变量名不允许使用空格、句点以及其它标点符号和井号之类的特殊字符。
给变量赋值,即将变量名与一个内存中的内容联系起来,也称为绑定(binding), 使用等号“=”,等号左边写变量名,右边写要保存到变量中的值。如
= 123 x
123
= 1+3/2 y
2.5
= "北京市海淀区颐和园路5号" addr100871
"北京市海淀区颐和园路5号"
变量的类型是由它保存的(指向的内存中的)值的类型决定的, 不需要说明变量类型(Julia允许说明变量类型,但一般不需要)。
变量赋值后,就可以参与运算,如:
= 123
x = 1+3/2
y + y*2 x
128.0
B.3.2 简单的输出
在Julia命令行,键入变量名或者计算表达式直接在下面显示结果。
可以用println()
函数显示指定的变量和结果。如
+ y*2) println(x
128.0
"x=", x, " y=", y, " x + y*2 =", x+y*2) println(
x=123 y=2.5 x + y*2 =128.0
B.4 向量
B.4.1 向量
在程序中直接定义一个向量,用方括号内写多个逗号分隔的数值,如
= [1, 3, 4, 9, 13] v1
5-element Array{Int64,1}:
1
3
4
9
13
= [1.5, 3, 4, 9.12] v2
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:5
1:2:9
1:2:9
B.4.2 向量下标
若x
是向量,i
是正整数,x[i]
表示向量的第i
个元素。如
2] v1[
3
对元素赋值将在原地修改元素的值,如
2] = -999; v1 v1[
5-element Array{Int64,1}:
1
-999
4
9
13
B.4.3 用范围作为下标
下标可以是一个范围,如
2:4] v1[
3-element Array{Int64,1}:
-999
4
9
熟悉Python语言的读者要注意,这里下标范围的终点是包含在内的, 而Python用这种方法取子集时不包括范围的终点。
用end
表示最后一个下标,如
4:end] v1[
2-element Array{Int64,1}:
9
13
1:(end-1)] v1[
4-element Array{Int64,1}:
1
-999
4
9
B.4.4 数组作为下标
向量的下标也可以是一个下标数组,如
1, 3, 5]] v1[[
3-element Array{Int64,1}:
1
4
13
取出的多个元素可以修改,可以赋值为同一个标量,如:
1:3] = 0; v1 v1[
5-element Array{Int64,1}:
0
0
0
9
13
也可以分别赋值,如
1, 3, 5]] = [101, 303, 505]; v1 v1[[
5-element Array{Int64,1}:
101
0
303
9
505
B.4.5 向量与标量的运算
向量与一个标量作四则运算, 将运算符前面加句点“.”:
.+ .- .* ./ .^
表示向量的每个元素分别与该标量作四则运算, 结果仍是向量。如
= [1, 3, 4, 9, 13]
v1 + 100 v1 .
5-element Array{Int64,1}:
101
103
104
109
113
100 .- v1
5-element Array{Int64,1}:
99
97
96
91
87
.* 2 v1
5-element Array{Int64,1}:
2
6
8
18
26
./ 10 v1
5-element Array{Float64,1}:
0.1
0.3
0.4
0.9
1.3
.^ 2 v1
5-element Array{Int64,1}:
1
9
16
81
169
B.4.6 向量与向量的四则运算
两个等长的向量之间作加点的四则运算,表示对应元素作相应的运算。如
= [1, 3, 4, 9, 13]
v1 = [2, 5, 6, 7, 10]
v3 + v3 v1 .
5-element Array{Int64,1}:
3
8
10
16
23
- v3 v1 .
5-element Array{Int64,1}:
-1
-2
-2
2
3
.* v3 v1
5-element Array{Int64,1}:
2
15
24
63
130
./ v3 v1
5-element Array{Float64,1}:
0.5
0.6
0.666667
1.28571
1.3
B.4.7 向量初始化
用zeros(n)
可以生成元素类型为Float64、元素值为0、长度为n的向量,如
5) zeros(
5-element Array{Float64,1}:
0.0
0.0
0.0
0.0
0.0
用Vector{Float64}(undef, n)
可以生成元素类型为Float64的长度为n的向量,
元素值未初始化,如
Vector{Float64}(undef, 3)
3-element Array{Float64,1}:
1.114676523e-315
1.92399178e-315
1.5759804e-315
类似可以生成其它元素类型的向量,如
Vector{Int}(undef, 3)
3-element Array{Int64,1}:
378739344
378739376
155525920
用这样的办法为向量分配存储空间后可以随后再填入元素值。
B.4.8 向量复制
定义一个向量后, 此向量的变量名就“绑定”在某个内存地址上。 修改变量元素实际是修改了变量名所绑定的地址中某个位置的值。如
= [2, 3, 5, 7, 11]
v 3] = 0
v[ v
5-element Array{Int64,1}:
2
3
0
7
11
将向量赋给另外一个向量,并不能实现复制; 两个向量实际上是绑定到了相同的内存地址。如
= v
w w
5-element Array{Int64,1}:
2
3
0
7
11
1] = 97
v[ w
5-element Array{Int64,1}:
97
3
0
7
11
可以看出修改了v
的元素值,w
的元素值也被修改了,
因为这两个变量名指向的是相同的内存地址。
为了制作一个向量的副本,
不能使用直接赋值的方法,而是使用copy()
函数。如
= [2, 3, 5, 7, 11]
v = copy(v)
w 3] = 0
v[ w
5-element Array{Int64,1}:
2
3
5
7
11
v
被修改后,其副本w
未受影响。
另外,给w
另外赋值,相当于解除了w
原来的绑定,
并将w
绑定到了将v
的内容复制到另一内存地址的副本地址上。
B.4.9 向量的循环遍历
可以用for
循环对向量的每个元素遍历访问。格式如
for i in eachindex(v1)
"v1[", i, "] = ", v1[i])
println(end
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”中,可用如下代码:
using DelimitedFiles
"tmp1.txt", v2, ' ') writedlm(
结果文件中每个数占一行。
B.4.11 向量的输入
假设文件“vecstore.txt”中包含如下的内容:
1.2 -5 3.6
7.8 9.12 4.11
可以用如下代码将文件中的数据读入到一个向量v4中:
using DelimitedFiles
= readdlm("vecstore.txt")[:]; v4 v4
B.5 矩阵
B.5.1 矩阵
前面讲的向量是一维数组,不区分行向量还是列向量。
矩阵是二维数组,有两个下标:行下标和列下标。
在方括号内两个同行的元素之间用空格分隔, 两行之间用分号分隔,可以在程序中输入矩阵,如
= [1 2 3; 4 5 6] A1
2×3 Array{Int64,2}:
1 2 3
4 5 6
也可以直接用换行符分隔不同行,例如
= [1 2 3
A1 4 5 6]
B.5.2 矩阵下标
设A
是矩阵,则A[i,j]
表示A
的第i
行第j
列元素,如:
2,3] A1[
6
给元素赋值可以在矩阵中修改元素值,如:
2,3] = -6; A1 A1[
2×3 Array{Int64,2}:
1 2 3
4 5 -6
B.5.3 矩阵列和行
设A
是矩阵,
则A[:, j]
表示A
的第j
列元素组成的向量(一维数组),如
:, 2] A1[
2-element Array{Int64,1}:
2
5
A[i, :]
表示A
的第i
行元素组成的向量(一维数组),如
2, :] A1[
3-element Array{Int64,1}:
4
5
-6
取出后的列或者行不分行向量和列向量。
B.5.4 子矩阵
如果A
是矩阵,I
和J
是范围或者向量,
则A[I,J]
表示A
的行号在I
中的行与列号在J
中的列交叉所得的子矩阵,如
1:2, 2:3] A1[
2×2 Array{Int64,2}:
2 3
5 -6
用冒号“:”作为行下标或列下标表示取该维的全部下标。
B.5.5 子矩阵赋值
可以给子矩阵赋值,赋值为一个标量使得对应元素都改为该标量值;如
1:2, 2:3] = 0; A1 A1[
2×3 Array{Int64,2}:
1 0 0
4 0 0
给子矩阵赋值为一个同样大小的子矩阵给对应元素赋值,如
1:2, 2:3] = [102 103; 202 203]; A1 A1[
2×3 Array{Int64,2}:
1 102 103
4 202 203
B.5.6 矩阵初始化
用zeros(m, n)
可以生成元素类型为Float64、元素值为0的\(m\times n\)矩阵,如
2, 3) zeros(
2×3 Array{Float64,2}:
0.0 0.0 0.0
0.0 0.0 0.0
用Array{Float64}(undef, m, n)
可以生成元素类型为Float64的\(m \times n\)矩阵,
元素值未初始化,如
Array{Float64}(undef, 2, 3)
2×3 Array{Float64,2}:
3.90752e-316 6.78756e-316 5.18128e-316
6.61297e-316 6.81373e-316 3.87102e-316
类似可以生成其它元素类型的矩阵,如
Array{Int}(undef, 2, 3)
2×3 Array{Int64,2}:
386423696 78323216 78323216
79156592 78323216 78331944
用这样的办法先为矩阵分配存储空间,然后可以再填入元素值。
B.5.7 矩阵元素遍历
对行下标和列下标分别循环,行下标变化最快,如
= [1 2 3; 4 5 6] A1
2×3 Array{Int64,2}:
1 2 3
4 5 6
for j = 1:size(A1,2), i = 1:size(A1,1)
"A1[", i, ", ", j, "] = ", A1[i, j])
println(end
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 i in eachindex(A1)
"A1[", i, "] = ", A1[i])
println(end
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\)矩阵。 读入方法如下:
using DelimitedFiles
= readdlm("vecstore.txt"); Ain
Ain
2×3 Array{Float64,2}:
1.2 -5.0 3.6
7.8 9.12 4.11
B.5.9 保存矩阵到文件中
考虑上面的Ain矩阵,为了将其按文本文件格式保存到“tmp2.txt”中, 用如下程序:
using DelimitedFiles
writedlm("tmp2.txt", Ain, ' ')
B.5.10 矩阵与标量的四则运算
矩阵与一个标量之间用加点的四则运算符号进行运算, 与向量和标量之间的运算类似, 表示矩阵的每个元素和该变量的四则运算, 结果仍为矩阵。如
= [1 2 3; 4 5 6] A1
2×3 Array{Int64,2}:
1 2 3
4 5 6
+ 100 A1 .
2×3 Array{Int64,2}:
101 102 103
104 105 106
100 .- A1
2×3 Array{Int64,2}:
99 98 97
96 95 94
.* 2 A1
2×3 Array{Int64,2}:
2 4 6
8 10 12
./ 10 A1
2×3 Array{Float64,2}:
0.1 0.2 0.3
0.4 0.5 0.6
.^ 2 A1
2×3 Array{Int64,2}:
1 4 9
16 25 36
B.5.11 两个矩阵之间的四则运算
两个同样大小的矩阵之间用加点的四则运算符号进行运算, 表示两个矩阵的对应元素的运算。如
= A1 .* 100 A2
2×3 Array{Int64,2}:
100 200 300
400 500 600
+ A2 A1 .
2×3 Array{Int64,2}:
101 202 303
404 505 606
- A1 A2 .
2×3 Array{Int64,2}:
99 198 297
396 495 594
.* A2 A1
2×3 Array{Int64,2}:
100 400 900
1600 2500 3600
./ A1 A2
2×3 Array{Float64,2}:
100.0 100.0 100.0
100.0 100.0 100.0
B.5.12 矩阵乘法
用A * B
表示矩阵乘法。 如
= [11 12; 21 22] A3
2×2 Array{Int64,2}:
11 12
21 22
* A1 A3
2×3 Array{Int64,2}:
59 82 105
109 152 195
一个矩阵与一个向量(一维数组)作矩阵乘法, 向量自动变成列向量,如:
* [1, -1] A3
2-element Array{Int64,1}:
-1
-1
注意结果是向量(一维数组),而不是\(2 \times 1\)矩阵(二维数组)。
行向量可以直接表示成方括号内多个数值之间用空格分隔的格式,如
1, -1] * [1 -1] [
2×2 Array{Int64,2}:
1 -1
-1 1
又如
1 -1] * A3 [
1×2 Array{Int64,2}:
-10 -10
注意结果是\(1 \times 2\)矩阵,即行向量,而不是向量。 向量是一维数组,行向量是二维数组。
从以上例子可以看出在矩阵运算中向量可以看成是列向量, 矩阵乘法结果如果是列向量,也会表示成向量(一维数组)。
B.5.13 矩阵转置
用A.’
表示矩阵A
的转置; 用A’
表示矩阵A
的共轭转置。 如
A1
2×3 Array{Int64,2}:
1 2 3
4 5 6
.' A1
3×2 Array{Int64,2}:
1 4
2 5
3 6
两个向量x和y的内积用dot(x, y)
表示, 而不要写成x.’ * y
。如
1, -1], [2, 3]) dot([
B.5.14 矩阵求逆和解线性方程组
inv(A)
表示\(A^{-1}\)。如
= [1 3; 3 1] A4
2×2 Array{Int64,2}:
1 3
3 1
inv(A4)
2×2 Array{Float64,2}:
-0.125 0.375
0.375 -0.125
用A \ B
表示\(A^{-1} B\), 当B
是向量或者列向量时,
就是求解线性方程组\(A x = B\)中的\(x\)。 如
\ [-2, 2] A4
2-element Array{Float64,1}:
1.0
-1.0
B.6 自定义函数
B.6.1 介绍
Julia语言支持自定义函数。 在计算机程序语言中, 函数是代码模块化、代码复用的基础。 将常用的计算或者操作写成函数以后, 每次要进行这样的计算或者操作, 只要简单地调用函数即可。 将复杂的任务分解成一个个函数, 可以使得任务流程更加明晰, 任务的不同阶段之间减少互相干扰。 另外, 用自定义函数表示的算法才能充分利用Julia的即时编译优势。
B.6.2 自定义函数的单行格式
类似于\(f(x) = x^2 + 3 x + 1\)这样的简单函数, 可以用一行代码写成
= x^2 + 3*x + 1 f(x)
f (generic function with 1 method)
调用如
2) f(
11
1.1) f(
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 } \] 自定义函数如
function mysd(x)
= length(x)
n = 0.0
mx for z in x
+= z
mx end
/= n
mx = 0.0
s for z in x
+= (z - mx)^2
s end
/ (n-1))
sqrt(s end
mysd (generic function with 1 method)
调用如
1, 2, 3, 4, 5]) mysd([
1.5811388300841898
事实上,上面的函数定义可以用已有的一些函数进行简化。
比如,sum(x)
对向量x的元素求和,于是以上的函数可以写成:
function mysd_simple(x)
= length(x)
n = sum(x)/n
mx - mx) / (n-1) )
sqrt( sum(x .end
mysd_simple (generic function with 1 method)
第二个版本只是利用了向量化和已有函数, 在Julia中其运行效率并不比第一个版本更好, 甚至于不如第一个版本。 Julia语言与R、Matlab等语言不同, 显式的循环一般有更高的执行效率, 向量化写法仅仅是可以使得程序比较简洁。
均值、标准差这些基本统计函数已经在Julia的Statistics标准库中有定义。
B.7 程序控制结构
B.7.1 复合表达式
用begin ... end
可以将多行的多个表达式组合起来当作一个表达式,
复合表达式的值是其中最后一个表达式的值。
如
= begin
z = 1
x = 2
y + y
x end
z
3
多个表达式也可以用分号分隔后写在圆括号中,作为一个复合表达式,如
= (x = 1; y = 2; x + y)
z z
3
B.7.2 比较运算
两个数值之间用如下的比较运算符进行比较:
== != < <= > >=
分别表示等于、不等于、小于、小于等于、大于、大于等于。 要特别注意“等于”比较用两个等号表示。
比较的结果是true(真值)或者false(假值)。 结果类型为Bool类型(布尔型)。
如
1 == 1.0
true
2 != 2.0001
true
3.5 > -1
true
3.5 > -1.5
true
-3.5 < 1.2
true
-3.5 <= 1.2
true
-3.5 > 1.2
false
-3.5 >= 1.2
false
两个字符串之间也可以比较, 比较时按字典序比较, 两个字符的次序按照其Unicode编码值比较。如
"abc" == "ABC"
false
"ab" < "abc"
true
"陕西省" != "山西省"
true
B.7.3 逻辑运算
比较通常有变量参与。如
= 35; sex="F"
age < 18 age
false
== "F" sex
true
有时需要构造复合的条件, 如“年龄不足18岁且性别为女”, “年龄在18岁以上或者性别为男”等。
用&&
表示要求两个条件同时成立,
用||
表示只要两个条件之一成立则结果为真,
用!cond
表示cond
的反面。
如
< 18 && sex == "F" age
false
>= 18 || sex == "M" age
true
B.7.4 短路与运算和分支
&&
是一种短路运算, 表达式cond && expr
仅当cond
为true时才计算(运行)expr
,
所以这种写法经常用作程序分支的简写: 条件cond
为真时执行expr
,
否则不执行。
比如,在计算x的平方根之前,先判断其非负:
= -1.44
x < 0 && println("平方根计算:自变量定义域错误,x=", x) x
平方根计算:自变量定义域错误,x=-1.44
B.7.5 短路或运算与分支
||
是一种短路或运算,表达式cond || expr
仅当cond为false时才计算(运行)expr
,
所以这种写法经常作为程序分支的缩写:
条件cond为假时才执行expr
,否则不执行。
比如,求平方根时当自变量不为负时才计算平方根:
< 0 || (y = sqrt(x)) x
true
B.7.6 if–end结构
可以用if cond ... end
结构在条件cond
成立时才执行某些语句,如
= 1.44
x if x >= 0
= sqrt(x)
y "√", x, " = ", y)
println(end
√1.44 = 1.2
注意条件不需要用括号包围,结构以end
语句结尾。
B.7.7 if–else–end结构
if cond ... else ... end
结构当条件成立时执行第一个分支中的语句,
当条件不成立时执行第二个分支中的语句。
如
= -1.44
x if x >= 0
= sqrt(x)
y "√", x, " = ", y)
println(else
= sqrt(-x)
y "√", x, " = ", y, "i")
println(end
√-1.44 = 1.2i
B.7.8 if-elseif-else-end结构
if cond1 ... elseif cond2 ... else ... end
可以有多个分支,
有多个条件cond1
, cond2
, ……,
依次判断各个条件,那个条件成立就执行对应分支的语句,
所有条件都不成立则执行else
分支的语句。
条件cond2
隐含条件cond1
不成立,
cond3
隐含cond1
和cond2
都不成立, 依此类推。
例如
= 35
age if age < 18
"未成年")
println(elseif age < 60
"中青年")
println(elseif age < 100
"老年")
println(else
"老寿星!")
println(end
中青年
B.7.9 三元运算符
可以用cond ? expr1 : expr2
表示比较简单的两分支选择,
当cond
成立时结果为expr1
的结果,
当cond
不成立时结果为expr2
的结果。
如
= -1.44
x = x >= 0 ? sqrt(x) : sqrt(-x) y
1.2
B.7.10 for循环
for循环一般是沿着某个范围进行计算或处理,格式如下:
for loopvar = a:b
expr1
expr2
...
end
其中loopvar
是自己命名的循环变量名,
for结构块内的语句(表达式)先对loopvar=a
运行,
再对loopvar=a+1
运行, 最后对loopvar=b
运行,然后结束。
如
for i=1:3
= i^3
y , "^3 = ", y)
println(iend
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, 5, 7, 11]
x for i in x
= i^3
y , "^3 = ", y)
println(iend
2^3 = 8
3^3 = 27
5^3 = 125
7^3 = 343
11^3 = 1331
B.7.12 向量元素按下标循环
设x
是一个向量,上面的做法可以遍历x
每个元素。
有时还需要按照向量的下标遍历,这时使用eachindex(x)
,如
= [2, 3, 5, 7, 11]
x for i in eachindex(x)
"Prime No. ", i, " = ", x[i])
println(end
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的立方的向量,可以写成
= [i^3 for i=1:3] xcube
3-element Array{Int64,1}:
1
8
27
对前5个素数作立方,可以写成
= [2, 3, 5, 7, 11]
x = [z^3 for z in x] y
5-element Array{Int64,1}:
8
27
125
343
1331
B.7.14 两重for循环
for循环可以嵌套,如
for i=1:9
for j=1:i
, "×", j, " = ", i*j, " ")
print(iend
println()end
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语句,外层循环先写, 内层循环后写,中间用逗号分隔。 如
for i=1:9, j=1:i
, "×", j, " = ", i*j, " ")
print(i==i && println()
jend
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的矩阵是按列存储的, 所以循环时先对第一列各个元素循环, 再对第二列各个元素循环, ……,按这样的次序遍历是比较有效的。 如
= [1 2 3; 4 5 6]
A1 for j=1:3, i=1:2
"A1[", i, ", ", j, "] = ", A1[i,j])
println(end
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
在方括号内用两重的循环变量遍历可以定义矩阵。如
= [i*100 + j for i=1:2, j=1:3] Ac
2×3 Array{Int64,2}:
101 102 103
201 202 203
这里写在前面的循环变量i
对应于行下标,
写在后面的循环变量j
对应于列下标。
执行时行下标i
在内层循环,列下标j
在外层循环。
又如,如下程序返回矩阵对角化的结果:
==j ? Ac[i,i] : 0) for i=1:2, j=1:3] [(i
2×3 Array{Int64,2}:
101 0 0
0 202 0
B.7.17 while循环
for循环适用于对固定的元素或者下标的遍历, 在预先未知具体循环次数时,需要使用当型循环循环或者直到型循环。
Julia中用while cond ... end
表示当型循环,
在条件cond成立时执行结构内的语句, 直到cond不成立时不再循环。
例如,要判断一个奇数x
是否素数,
从3开始逐个用奇数去除x
,直到除尽或者除数等于x
为止:
= 1333333
x = 3
i while i < x && x % i != 0
+= 2
i end
if i == x
, "is prime.")
println(xelse
, "=", i, " X ", x ÷ i)
println(xend
1333333=23 X 57971
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
,
当计算的通项小于此精度时停止计算。
程序用直到型型循环写成:
= 0.0001
eps = 1.0
x = x; xk = x; sgn = 1; k = 1
y while true
+= 1; sgn *= -1; xk *= x
k = xk / k
item += sgn*item
y < eps && break
item end
"eps = ", eps, " log(1+", x, ") = ", y,
println(" Iterations: ", k)
eps = 0.0001 log(1+1.0) = 0.6931971730609582 Iterations: 10001