Julia 笔记系列:


唠唠闲话

本篇介绍 Julia 的基本语法,对应课程第二讲,主要内容如下:

Ps:这是关于 @陈久宁 在华东师范大学内部做的 Julia 系列讲座。课程 GitHub 地址 及第一讲Julia 概述


Julia 的交互模式

概览

本节介绍 Julia 的五种交互模式,并重点介绍包管理模式,这五种模式分别为:

模式 说明 进入方式
Julian 模式 执行 Julia 代码的地方 默认模式,在其他模式下通过退格键 Backspace 进入
Pkg 模式 Julia 装包时使用的模式 在 Julian 模式按 ] 进入
Help 文档模式 查询文档时使用 在 Julian 模式下按 ? 进入
Shell 模式 切换到系统命令行 在 Julian 模式下按 ; 进入
Debug 模式 进入到代码调试模式 安装 Debugger.jl 后使用

经常用到且必须知晓的三个模式是 Julian 模式, Pkg 模式和 Help 模式。

基本介绍

  1. 首先在终端运行 Julia ,页面显示如下
    深度截图_选择区域_20220404005125

  2. 初始进入 Julia 时,显示的模式就是 Julian 模式,左侧是绿色的 julia>

  3. 在 Julian 模式下,按下 ] 进入 Pkg 模式(包管理模式),如下图
    深度截图_选择区域_20220404005727

  4. 任意模式下,按退格键(键盘上的 Backspace)可以回到 Julian 模式

  5. 在 Julian 模式下,按下 ? 进入 Help 模式,此时输入函数可以查看使用文档
    深度截图_选择区域_20220404005933

  6. 在 Julian 模式下,按下 ; 进入 Shell 模式,这个接触过 Linux 的应该懂怎么用

  7. Debug 模式个人目前用得不多,暂略。

包的安装

Julia 中安装工具包的方法很简单,有两种方式,一是使用 Pkg 模式,二是在 Julian 模式下调用 Pkg 模块

  1. 方法一,输入 ] 进入 Pkg 模式,如下图,左侧括号显示 Julia 的开发环境, @v1.6 代表 Julia 1.6 版本,使用默认环境
    深度截图_选择区域_20220404011026

  2. 输入 add <包名称> 安装具体模块,比如下图,模块的下载目录在用户主目录的 .julia
    深度截图_选择区域_20220404011715

  3. 方法二,退格回到 Julian 模式,输入 using Pkg 导入包管理模块,再输入 Pkg.add("<包名称>") 安装模块,如下图,注意包名称要用引号包含起来
    深度截图_选择区域_20220404012113

  4. 包安装完成后,可以用 using <包名称> 测试模块导入

上边只涉及了 Pkg.add 命令,包管理还有很多常用命令,后续再单独写篇介绍,推荐阅读:Pkg 官方手册Julia 读文档之包的实战

Julia 的数据结构

基本语法

Julia 用 # 代表注释,方便起见,相关知识点写在代码注释中

  1. @xxx宏(macro)的语法,宏可以实现“利用代码生成代码”。

  2. 演示常用的两种宏

    • @show 显示运算信息,式子间用 <空格> 隔开
    • @info 类似 @show,以第一个变量为打印信息
  3. 示例:

    1
    2
    3
    4
    x,y = 1,2
    @show x+y # x + y = 3
    @show x+y x-y # x + y = 3 \n x - y = -1
    @info "记录信息" x y x+y

    显示结果
    result

数据类型

  1. 整型,浮点数类型

    1
    2
    3
    4
    x = 1
    @show typeof(x) # 默认整型
    λ = .1
    @show typeof(λ) # .1 等同于 0.1

    输出内容
    2021-10-24 13-49-27 的屏幕截图

  2. Julia 支持使用 LaTeX ,比如 λ\lambda 通过 \lambda+<TAB> 输入;下标 cic_i 通过 c\_i + <TAB> 输入。

  3. Julia 中“数值 * 变量”用连接简记,比如 2x 等同于 2 * x

  4. 其他数值类型

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    @show typeof(1+im) # im 代表虚数 i
    @show typeof(1.0+2im) # 浮点虚数
    @show 1 / 0 # 无穷大
    @show 0 * Inf # 不定数
    @show typeof(Inf) # Float64
    @show typeof(NaN) # Float64
    @show typeof(pi) # 无理数
    @show typeof(missing) # 缺失类型,用于统计
    @show 2 * missing # missing
    @show typeof(nothing) # 相当于 Python 的None

    输出内容
    2021-10-24 13-52-53 的屏幕截图

  5. 字符相关

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    @show typeof('c') # 单引号代表字符
    @show typeof("c") # 双引号代表字符串
    @show 'c' == "c" # 数据结构不同
    @show "c"[1] === 'c' # 字符串用索引取出的数据类型是字符
    @show "c"[:] == 'c' # 切片取出字符串
    @show "hello " * "world" # 字符串拼接用 *
    @show "hi" ^ 3 # 字符串复制用 ^
    var = 2333
    @show "hello $var" # 使用 $ 插入实际变量
    @show "hello $(var/10)" # h插入运算结果

    输出内容
    2021-10-24 13-55-44 的屏幕截图

  6. 字符转数值,要用 parse,不能直接用 IntFloat64

    1
    2
    @show parse(Int,"1") #  Int 类型
    @show parse(Float64,"1") # Float 类型

    20211025122853

容器数据类型

  1. 使用 [] 得列向量 Vector

    1
    2
    3
    @show x = [1, 2, 3] # Vector (column vector)
    @show typeof(x) # 等同于 Array{Int64, 1}
    @show Vector(3:5) # Vector(3:5) = [3, 4, 5]

    深度截图_选择区域_20211025123253

  2. 切片规则:左闭右闭,首位索引为1,末位索引为 end

    1
    2
    3
    4
    x = [1,2,3]
    @show x[1:end-1] # end-1 倒数第二位置
    @show x[:] # 取全部
    @show x[:] === x # 切片创建了新对象

    深度截图_选择区域_20211025123048
    切片两端留空代表取全部,此时得到的是新对象;和 Python 不同的一点是,切片不能只写一侧,比如 x[2:]

  3. 常用属性

    • size(x): 尺寸,Tuple{Int64} 类型
    • length(x): 长度,类似 Python 的 len
    • eltype(x): 元素类型
  4. 矩阵

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    @show y = [1 2 3] # 空格分列,分号换行
    @show [1 2 3 4; 5 6 7 8]
    @show [1 2 3 4
    5 6 7 8] # 支持用换行号换行
    @show typeof(y) # 行向量为矩阵类型
    @show size(y) # 矩阵形状
    @show size(x) # 列向量形状
    @show x == y # false | 矩阵尺寸不同,返回否
    @show x' == y # 向量转置,数据格式化为矩阵,类型相等
    @show y' == x # x 仍是向量,y 是 3*1 矩阵

    深度截图_选择区域_20211025123737

  5. 元组(不可变类型)

    1
    2
    3
    4
    5
    x = (1,2,3.)
    @show typeof(x) # 元组会单独记录每个位置的数据结构
    @show x = 1, 2, 3. # 定义时括号可省
    @show x,y = 1,2 # 同 Python 的拆包功能
    @show x, y = 1, 2, 3, 4, 5 # 拆包多余部分被丢弃

    深度截图_选择区域_20211025123926

  6. 编译器足够聪明,元组的拆分组合操作运行很快

    1
    2
    3
    modify_first!(t::Tuple, x) = (x, t[2:end]...)
    t = (1, 2, 3)
    @btime modify_first!($t, 0)

    20211103142242

  7. 容器内,使用 ... 可将序列打开

    1
    2
    x = [1,2,3]
    @show (0, x[2:end]...) [3,x...]

    深度截图_选择区域_20211025124048

  8. 字典
    注意:初始化后,字典键值类型确定,此时新键值必须保持类型。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    # Dict{Any, String} with 3 entries
    d = Dict(1 => "item1",
    "2" => "item2",
    (3, 4) => "item3-4") # 创建字典
    @show d[1] # 取出元素
    d[3] = "item3" # 新增键值
    # d[2] = 2 # 报错,与字符串类型不符
    @show typeof(1=>2) # 创建使用了 Pair 类型
    items = (x=>x^2 for x in 1:5) # 先创建 Pair
    @show d = Dict(items) # 再生成字典

    深度截图_选择区域_20211025124503

  9. 命名元组,可简单理解成不可变的字典类型

    1
    2
    3
    4
    5
    d = (x = "first item",
    y = "second item")
    @show typeof(d) # 命名元组
    @show d[:x] # 使用 Symbol 取出元素
    @show d.y # 使用 . 取出函数

    注意键名必须是变量,不能为数值之类
    深度截图_选择区域_20211025124633

  10. 集合交并补操作

    1
    2
    3
    4
    5
    x = [1, 2, 3]
    y = [2, 3, 4]
    @show setdiff(x, y) # setdiff(x, y) = [1]
    @show union(x, y) # union(x, y) = [1, 2, 3, 4]
    @show intersect(x, y) # intersect(x, y) = [2, 3]

    深度截图_选择区域_20211025124703

  11. 列表生成器和迭代器,和 Python 类似

    1
    2
    @info "列表生成器" [i for i in -0.5:0.2:1 ]
    @info "迭代器" (i for i in -0.5:0.2:1 )

    20211103143826

  12. 迭代器不需要创建中间变量来存储所有结果,在内存使用上是比较高效

    1
    2
    3
    4
    5
    clamp(x, lo=0, hi=1) = x < lo ? lo : x > hi ? hi : x
    f_list() = Dict([x => clamp(x) for x in -0.5:0.2:1.5])
    f_generator() = Dict((x => clamp(x) for x in -0.5:0.2:1.5))
    @btime f_list() # 使用列表生成器
    @btime f_generator() # 使用迭代器

    深度截图_选择区域_20211104215458

Julia 的函数

基本使用

  1. 用关键字 function 定义函数

    1
    2
    3
    4
    5
    # 如果没有 return ,默认返回最后一个变量
    function f(x,y)
    tmp = x + y
    return tmp
    end
  2. 用单行定义函数 f(x,y) = x + y

  3. 三元表达式 cond ? true_rst : false_rst ,注意 ?: 前后要留空格

    1
    2
    greater(a,b)= a>b ? true : false
    @show greater(1,2)

    深度截图_选择区域_20211025125237

  4. = 预定义参数值

    1
    2
    f(x,y=10) = x+y # 用等号预定义
    @show f(1) f(1,2) # 使用预定义

    深度截图_选择区域_20211025125345
    注意:和 Python 不同的是,Julia 函数的默认值发生在函数调用时,所以如果预定义值为可变对象,下次调用将重新创建,而不必担心被修改。

  5. 函数指定参数类型,类似 C++ 的重载

    1
    2
    3
    4
    f(x::Tuple) = "input tuple"
    f(x::Int) = "input int"
    @show f((1,2)) # 输入元组
    @show f(1) # 输入整型

    深度截图_选择区域_20211025125351

  6. 可变参数用 ... ,类似 Python 的 *

    1
    2
    3
    f(x,y...) = y # 定义函数
    @show f(1) # 可变参为空元组
    @show f(1,2,3,4)

    深度截图_选择区域_20211025125356

  7. 关键词参数

    1
    2
    3
    4
    5
    f(x;y,z=10) = x + y + z # 分号隔开关键字
    @show f(0,y=2) # z 取默认值,y 用关键值赋值
    # f(1,2,3) # 报错,必须用关键字赋值
    f(x) = 0 # 函数重载只比对关键字前的部分
    @show f(0) # 函数被重置

    深度截图_选择区域_20211025125401

  8. 当函数修改变量信息时,约定上,函数名末尾追加 !

    1
    2
    3
    4
    5
    x = [2,1]
    sort(x)
    @show x # x 不变
    sort!(x)
    @show x # x 改变

    深度截图_选择区域_20211025125406
    Julia 编写函数时,可以写两个版本,一个带 ! 允许修改变量,运算速度快;再写一个不带 ! 的版本,通过调用第一个函数来定义。

函数式编程

一等公民

在 Julia 的语言中,函数是“一等公民”(First-class citizen)。编程语言中,所谓一等公民,是指支持所有操作的实体, 这些操作通常包括作为参数传递,从函数返回,修改并分配给变量等。比如 int 类型,它支持作为参数传递,可以从函数返回,也可以赋值给变量,因此它是一等公民。类似的,函数是一等公民,意味着可以把函数赋值给变量或存储在数据结构中,也可以把函数作为其它函数的参数或者返回值。一般来说,函数式编程语言、动态语言和现代的编程语言,函数都会作为一等公民(参考 CSDN)。

Julia 与函数式编程

  1. Julia 里的符号可以当函数来用,比如

    1
    @show +(1,2) # 相当于 1 + 2
  2. 高阶函数

    1
    2
    3
    f = x->x^2 # | 匿名函数
    map(f,1:5) # [1,2,3,4,5] | 函数作用在右侧数据上,返回向量
    map((x,y)->x+y,1:5,6:10) # [7,9,11,13,15] | 二元函数

    支持输入值为函数的还有 reducemapreduceforeachsumfilter 等。这部分和 MMA 类似,就不仔细介绍了。

  3. do 创建一个匿名函数并将其作为第一个参数传递给函数调用

    1
    2
    3
    4
    5
    6
    7
    8
    map(1:5) do x
    return x^2
    end
    # 相当于
    function f(x)
    return x^2
    end
    map(f,1:5)

面向对象

和 Mathematica 一样,Julia 不支持面向对象,不能继承方法。但面向对象的很多功能,可以用结构体及多重派发替代。

可补充

  1. homework
  2. 函数式编程