Julia 初学者指南(二) | 数据类型与函数基础
唠唠闲话
Julia 是一种高性能的动态编程语言,特别适用于数值分析和计算科学领域。它拥有一个强大的类型系统和灵活的多重分派机制,这使得代码易于编写同时还能保持接近 C 语言的运行速度。此外,Julia 也能无缝调用 C 和 Fortran 库,使得它能充分利用现有的高性能计算资源。
本系列教程旨在带领读者从零开始,逐步深入学习 Julia 编程。无论你是编程新手还是有经验的开发者,这些教程都将为你提供清晰的学习路径和丰富的示例代码,帮助你快速掌握 Julia 的核心技术和高级功能。
在上节中,我们介绍了 Julia 的安装、配置及常用编译器。本篇将探讨 Julia 中的数据类型和函数,这是编程的基础内容。
相关链接:
Julia 的交互模式
概览
本节介绍 Julia 的五种交互模式,并重点介绍包管理模式,这五种模式分别为:
模式 | 说明 | 进入方式 |
---|---|---|
Julian 模式 | 执行 Julia 代码的地方 | 默认模式,在其他模式下通过退格键 Backspace 进入 |
Pkg 模式 | Julia 装包时使用的模式 | 在 Julian 模式按 ] 进入 |
Help 文档模式 | 查询文档时使用 | 在 Julian 模式下按 ? 进入 |
Shell 模式 | 切换到系统命令行 | 在 Julian 模式下按 ; 进入 |
Debug 模式 | 进入代码调试模式 | 安装 Debugger.jl 后使用 |
除了 Debug 模式,其他四种模式都会经常用到,进入方式不仅适用于终端,也适用于 Jupyter Notebook 等环境。
基本介绍
四种常见模式:
-
初始进入 Julia 时,显示的模式就是 Julian 模式,左侧是绿色的
julia>
,页面显示如下:
-
在 Julian 模式下,按下
]
进入 Pkg 模式(包管理模式),如下图:
-
在任意模式下,按退格键(键盘上的
Backspace
)可以回到 Julian 模式。 -
在 Julian 模式下,按下
?
进入 Help 模式,此时输入函数可以查看使用文档:
-
在 Julian 模式下,按下
;
进入 Shell 模式,这个接触过 Linux 的应该懂怎么用。 -
Debug 模式个人目前用得不多,暂略。
包的安装
Julia 中安装工具包的方法很简单,有两种方式,一是使用 Pkg 模式,二是在 Julian 模式下调用 Pkg
模块。
-
方法一,输入
]
进入 Pkg 模式,如下图,左侧括号显示 Julia 的开发环境,@v1.6
代表 Julia 1.6 版本,使用默认环境:
-
输入
add <包名称>
安装具体模块,比如下图,模块下载到用户主目录的.julia
下:
-
方法二,退格回到 Julian 模式,输入
using Pkg
导入包管理模块,再输入Pkg.add("<包名称>")
安装模块,如下图,注意包名称要用引号包含起来:
-
包安装完成后,可以用
using <包名称>
测试模块导入。
以上只涉及了 Pkg.add
命令,包管理还有很多常用命令,后续再单独写篇介绍,推荐阅读:Pkg 官方手册,Julia 读文档之包的实战。
Julia 的数据结构
基本语法
在 Julia 中,#
用于注释。为了方便学习,相关知识点都写在代码注释中。
-
@xxx
是 宏 (macro) 的语法。宏可以实现“利用代码生成代码”。 -
演示常用的两种宏:
@show
显示运算信息,表达式之间用空格隔开。@info
类似@show
,但以第一个变量为打印信息。
-
示例:
1
2
3
4x, y = 1, 2
# x + y = 3 x + y
# x + y = 3 \n x - y = -1 x + y x - y
"记录信息" x y x + y显示结果:
数据类型
整型和浮点数类型
1 | x = 1 |
输出内容:
- Julia 支持使用 LaTeX,比如 通过
\lambda
+<TAB>
输入;下标 通过c\_i
+<TAB>
输入。 - 在 Julia 中,“数值 * 变量”用连接简记,比如
2x
等同于2 * x
。
其他数值类型
1 | 1 + im) # im 代表虚数 i typeof( |
输出内容:
字符相关
1 | 'c') # 单引号代表字符 typeof( |
输出内容:
字符转数值
使用 parse
,不能直接用 Int
或 Float64
:
1 | Int, "1") # Int 类型 parse( |
输出内容:
容器数据类型
列向量
使用 []
得列向量 Vector
:
1 | 1, 2, 3] # Vector (column vector) x = [ |
输出内容:
切片规则
左闭右闭,首位索引为1,末位索引为 end
:
1 | x = [1, 2, 3] |
输出内容:
切片两端留空代表取全部,此时得到的是新对象;和 Python 不同的是,切片不能只写一侧,比如 x[2:]
。
常用属性
size(x)
: 尺寸,返回Tuple{Int64}
类型length(x)
: 长度,类似 Python 的len
eltype(x)
: 元素类型
矩阵
1 | 1 2 3] # 空格分列,分号换行 y = [ |
输出内容:
元组(不可变类型)
1 | x = (1, 2, 3.) |
输出内容:
容器内使用 ...
展开序列
1 | x = [1, 2, 3] |
输出内容:
元组的拆分组合操作运行很快
1 | modify_first!(t::Tuple, x) = (x, t[2:end]...) |
字典
注意:初始化后,字典键值类型确定,新键值必须保持类型一致。
1 | # Dict{Any, String} with 3 entries |
输出内容:
命名元组
可以简单理解为不可变的字典类型:
1 | d = (x = "first item", y = "second item") |
注意键名必须是变量,不能为数值之类:
集合交并补操作
1 | x = [1, 2, 3] |
输出内容:
列表生成器和迭代器
和 Python 类似:
1 | "列表生成器" [i for i in -0.5:0.2:1] |
输出内容:
由于迭代器不需要创建中间变量来存储所有结果,在内存使用上是比较高效的:
1 | clamp(x, lo=0, hi=1) = x < lo ? lo : x > hi ? hi : x |
输出内容:
Julia 的函数
基本使用
-
用关键字
function
定义函数:1
2
3
4
5# 如果没有 return ,默认返回最后一个变量
function f(x, y)
tmp = x + y
return tmp
end -
用单行定义函数:
1
f(x, y) = x + y
-
三元表达式
cond ? true_rst : false_rst
,注意?
和:
前后要留空格:1
2greater(a, b) = a > b ? true : false
1, 2) greater( -
用
=
预定义参数值:1
2f(x, y=10) = x + y
1) f(1, 2) # 使用预定义 f(
注意:与 Python 不同,Julia 函数的默认值是在函数调用时确定的,因此如果预定义值为可变对象,下次调用将重新创建,不必担心被修改。 -
函数指定参数类型,类似 C++ 的重载:
1
2
3
4f(x::Tuple) = "input tuple"
f(x::Int) = "input int"
1, 2)) # 输入元组 f((
1) # 输入整型 f( -
可变参数用
...
,类似 Python 的*
:1
2
3f(x, y...) = y # 定义函数
1) # 可变参为空元组 f(
1, 2, 3, 4) f( -
关键词参数:
1
2
3
4
5f(x; y, z=10) = x + y + z # 分号隔开关键字
0, y=2) # z 取默认值,y 用关键字赋值 f(
# f(1, 2, 3) # 报错,必须用关键字赋值
f(x) = 0 # 函数重载只比对关键字前的部分
0) # 函数被重置 f( -
当函数修改变量信息时,约定上,函数名末尾追加
!
:1
2
3
4
5x = [2, 1]
sort(x)
# x 不变 x
sort!(x)
# x 改变 x
Julia 编写函数时,可以写两个版本,一个带!
允许修改变量,运算速度快;再写一个不带!
的版本,通过调用第一个函数来定义。
函数式编程
一等公民
在 Julia 中,函数是“一等公民”(First-class citizen)。在编程语言中,一等公民是指支持所有操作的实体,这些操作通常包括作为参数传递、从函数返回、修改并分配给变量等。比如 int
类型,它支持作为参数传递,可以从函数返回,也可以赋值给变量,因此它是一等公民。类似的,函数作为一等公民,意味着可以把函数赋值给变量或存储在数据结构中,也可以把函数作为其他函数的参数或者返回值。一般来说,函数式编程语言、动态语言和现代编程语言中,函数都会作为一等公民。
Julia 与函数式编程
-
Julia 里的符号可以当函数来用,比如:
1
1, 2) # 相当于 1 + 2 +(
-
高阶函数:
1
2
3f = x -> x^2 # 匿名函数
map(f, 1:5) # [1, 4, 9, 16, 25] | 函数作用在右侧数据上,返回向量
map((x, y) -> x + y, 1:5, 6:10) # [7, 9, 11, 13, 15] | 二元函数支持输入值为函数的还有
reduce
、mapreduce
、foreach
、sum
、filter
等。这部分和 MMA 类似,就不仔细介绍了。 -
do
创建一个匿名函数并将其作为第一个参数传递给函数调用:1
2
3
4
5
6
7
8map(1:5) do x
return x^2
end
# 相当于
function f(x)
return x^2
end
map(f, 1:5)
Curry 化
柯里化,指把接受多个参数的函数变换成接受多个单一参数的函数的技术。
例如 >
是一个二参函数,可以通过指定一参来创建一个新函数:
1 | filter(>(3), 1:10) |
这里 >(3)
就是一个柯里化的函数,它接受一个参数,返回一个函数。
一般的,在 Julia 中可以用 Base.Fix1
或 Base.Fix2
来实现柯里化:
1 | f(x, y) = x + y |
面向对象
和 Mathematica 一样,Julia 不支持面向对象,不能继承方法。但面向对象的很多功能可以用结构体及多重派发替代。
Julia 中的抽象对象,某种程度上可以替代继承的功能,我们下一节会详细介绍。
总结
本节内容,介绍了 Julia 的交互模式,基本的数据结构,函数及函数式编程。这些基础知识将为进一步深入学习 Julia 奠定坚实的基础。