using Pkg
Pkg.activate(mktempdir());
Activating new environment at `C:\Users\t2276\AppData\Local\Temp\jl_AYtZWF\Project.toml`
Pkg.add(["DataFrames"])
Updating registry at `C:\Users\t2276\.julia\registries\General` Updating git-repo `https://github.com/JuliaRegistries/General.git` Resolving package versions... Updating `C:\Users\t2276\AppData\Local\Temp\jl_AYtZWF\Project.toml` [a93c6f00] + DataFrames v1.3.1 Updating `C:\Users\t2276\AppData\Local\Temp\jl_AYtZWF\Manifest.toml` [34da2185] + Compat v3.41.0 [a8cc5b0e] + Crayons v4.0.4 [9a962f9c] + DataAPI v1.9.0 [a93c6f00] + DataFrames v1.3.1 [864edb3b] + DataStructures v0.18.11 [e2d170a0] + DataValueInterfaces v1.0.0 [59287772] + Formatting v0.4.2 [41ab1584] + InvertedIndices v1.1.0 [82899510] + IteratorInterfaceExtensions v1.0.0 [e1d29d7a] + Missings v1.0.2 [bac558e1] + OrderedCollections v1.4.1 [2dfb63ee] + PooledArrays v1.4.0 [08abe8d2] + PrettyTables v1.3.1 [189a3867] + Reexport v1.2.2 [a2af1166] + SortingAlgorithms v1.0.1 [3783bdb8] + TableTraits v1.0.1 [bd369af6] + Tables v1.6.1 [0dad84c5] + ArgTools [56f22d72] + Artifacts [2a0f44e3] + Base64 [ade2ca70] + Dates [8bb1440f] + DelimitedFiles [8ba89e20] + Distributed [f43a241f] + Downloads [9fa8497b] + Future [b77e0a4c] + InteractiveUtils [b27032c2] + LibCURL [76f85450] + LibGit2 [8f399da3] + Libdl [37e2e46d] + LinearAlgebra [56ddb016] + Logging [d6f4376e] + Markdown [a63ad114] + Mmap [ca575930] + NetworkOptions [44cfe95a] + Pkg [de0858da] + Printf [3fa0cd96] + REPL [9a3f8284] + Random [ea8e919c] + SHA [9e88b42a] + Serialization [1a1011a3] + SharedArrays [6462fe0b] + Sockets [2f01184e] + SparseArrays [10745b16] + Statistics [fa267f1f] + TOML [a4e569a6] + Tar [8dfed614] + Test [cf7118a7] + UUIDs [4ec0a83e] + Unicode [deac9b47] + LibCURL_jll [29816b5a] + LibSSH2_jll [c8ffd9c3] + MbedTLS_jll [14a3606d] + MozillaCACerts_jll [83775a58] + Zlib_jll [8e850ede] + nghttp2_jll [3f19e933] + p7zip_jll
在官方文件裡裡面,對於函式的定義是:”函式是一個將數組 (tuple) 引數 (argument) 對照到回傳值的物件”。也就是說,呼叫時是用數組的型態把引數傳遞給函式,函式內的運算結果再透過回傳值傳回。
我們可以透過函式的定義,將相同模式的動作或邏輯,抽象化提取出來成為可以重覆被呼叫使用的模塊。
可由以下方式建立函式:
function f(x, y)
x + y # 函式預設回傳最後一個執行的結果
end
或
f(x, y) = x + y
也可使用匿名的方式建立(相當於matlab 的function handle: f = @(x, y) x + y
)(More info):
f = (x, y) -> x + y
f(x, y) = x + y;
f(3, 2)
5
與 matlab 不同的是, julia 的函式都可以作為變數傳遞至其他函式:
f(x) = x^2 + 2x + 1;
f(1)
4
g(x, f) = 3*f(x);
g(1, f)
12
a = 2;
b = 3;
function add_abcd(c,d)
e = 7;
return a + b + c + d + e;
end
add_abcd(0.5,0.3)
12.8
e
UndefVarError: e not defined Stacktrace: [1] top-level scope @ :0 [2] eval @ .\boot.jl:360 [inlined] [3] include_string(mapexpr::typeof(REPL.softscope), mod::Module, code::String, filename::String) @ Base .\loading.jl:1116
julia 的多重分派讓使用者只需遵循簡單的邏輯就能寫出高效率的程式碼。 julia 語言允許同 scope 下出現複數個同名函式;多重分派指的是根據輸入引數不同,自動分派到不同方法進行計算。
另一方面, 在單一分派的物件導向程式設計中(例如python matlab),我們會將函式(function)歸類到某個類別(class)底下的方法(method)。 以下是 python的範例程式碼:
# 定義型別
class Foo:
def add(self, x):
return str(x)
class Bar:
def add(self, x):
return int(x)
# 建立屬於某型別的物件
foo = Foo()
bar = Bar()
# 呼叫函式:
foo.add()
bar.add()
在 matlab中,你必須寫例如像是
function abc(varargin)
if nargin > 1
...
else if nargin > 2 && ischar(varargin{1})
...
...
else if
...
else
...
end
使得根據不同引述而進行的不同計算方法(例如 abc(x), abc(str, x), ...
)能夠被實現。
建立一個🪵(木頭)的型別
mutable struct 🪵 # 型別: 木頭
A::Float64 # 截面積
L::Float64 # 長度
end
建立一個其型別屬於🪵的物件實體:
wood1 = 🪵(5, 70) # 透過預設的內部建構子(inner-constructor)建立物件
🪵(5.0, 70.0)
創建一個方法🪓把木頭🪵砍半:
function 🪓(w::🪵)
# (:: is type assertion. For example, A::Float64 returns nothing if A is a floating number of 64bit; otherwise, an error will be raised.)
w.L=w.L/2
[w, w]
end
🪓 (generic function with 1 method)
🪓(wood1)
2-element Vector{🪵}: 🪵(5.0, 35.0) 🪵(5.0, 35.0)
wood1
🪵(5.0, 35.0)
如果我想把木頭切成n等分?
function 🪓(w::🪵, n)
w.L=w.L/n;
fill(w, n) # 創建一個 [w, w, w, ... , w] 共 n 個元素的向量
end
🪓 (generic function with 2 methods)
wood2 = 🪵(5, 70)
🪓(wood2, 5)
5-element Vector{🪵}: 🪵(5.0, 14.0) 🪵(5.0, 14.0) 🪵(5.0, 14.0) 🪵(5.0, 14.0) 🪵(5.0, 14.0)
🪓還可以拿來砍別的東西吧? 如果我想要把一個向量砍半的話...
function 🪓(vec::Vector)
lenv = length(vec);
mid = Int(lenv/2); # "中間"定義為向量長度的一半向下取整數
return [vec[1:mid], vec[mid+1:end]]
end
🪓 (generic function with 3 methods)
v = [1,2,3,4,5,6,7,8,9,10];
🪓(v)
2-element Vector{Vector{Int64}}: [1, 2, 3, 4, 5] [6, 7, 8, 9, 10]
1+1
2
用methods
看一個函式有哪些方法:
methods(🪓)
實數乘法:
110*55
6050
虛數乘法:
(1+1im)*(1+1im)
0 + 2im
矩陣乘法:
$ \begin{pmatrix} 1 & 2 \\ 3 & 4 \end{pmatrix} \begin{pmatrix} 3 & 1 \\ 0 & 2 \end{pmatrix} = \begin{pmatrix} 3 & 5 \\ 9 & 11 \end{pmatrix} $
[1 2; 3 4]*[3 1; 0 2]
2×2 Matrix{Int64}: 3 5 9 11
字串相乘?
"hello"*"world"
"helloworld"
一般來說, 3*"Hello"
是會報錯的,因為不存在方法支援整數(Int)與字串(String)的相乘:
julia:repl
ERROR: MethodError: no method matching *(::Int64, ::String)
julia 的操作運算子也都是函式, 例如, 3*2
其實也可以寫成 *(3,2)
。
julia 內建的函式都放在 Base 下面。
利用多重分派, 我們可以新增乘法符號的新方法, 使得 3*"Hello"
得到 "HHHeeellllllooo"
:
function Base.:*(n::Integer, str::AbstractString) # :* 代表乘法(*)的符號,其類別是 Symbol
str1 = [];
for s in str
reps = join(fill(s,3)); # repeated s
push!(str1, reps)
end
return string(str1...)
end
3*"Hello"
"HHHeeellllllooo"
抽象型別(Abstract type)
💡Hint: 用
supertype
和subtypes
可以看一個型別的父型別和子型別們
'ACertainString'
) 與字串 (String, 例如 "ACertainString"
)在多數時候是可以互相替代的。'ACertainString'
會報錯)。在 julia 中,想要在字串中表達雙引號("
)有兩種方式。第一種是使用跳脫字元(Escape Character),例如:
print("For what physical quantity does \"ShearVel\" represent?")
For what physical quantity does "ShearVel" represent?
第二種是使用三連雙引號(triple double quotes)
print("""
For what physical quantity does \"ShearVel\" represent?
""")
For what physical quantity does "ShearVel" represent?
作為變數,兩者是相同的
a = """For what physical quantity does \"ShearVel\" represent?""";
b = "For what physical quantity does \"ShearVel\" represent?";
a == b
true
DataFrame
DataFrames
是否照慣例做並不影響執行結果。但是,遵循基本慣例可以讓我們光靠閱讀程式碼的階段就可以大致理解程式碼在做什麼;不遵循慣例會造成不必要的麻煩,對所有人都沒好處。
結尾有驚嘆號的函式提醒我們某一個輸入變數會直接被改變。
v = [1,2,3]
push!(v, 99);
v
4-element Vector{Int64}: 1 2 3 99
剛剛的🪓函式因為會改變輸入物件🪵本身。故按照慣例,命名應該要加驚嘆號才是!
function 🪓!(...)
...
end
DataFrame (型別或建構子) and DataFrames (套件)
using DataFrames
t = collect(1:5);
Y = 3 .+ 2t .+ t .^ 2
df = DataFrame(:Time => t, :Y => Y) # or DataFrame("Time" => t, "Y" => Y);
df
5 rows × 2 columns
Time | Y | |
---|---|---|
Int64 | Int64 | |
1 | 1 | 6 |
2 | 2 | 11 |
3 | 3 | 18 |
4 | 4 | 27 |
5 | 5 | 38 |
typeof(df)
DataFrame
說明:
df = DataFrame(:Time => t, :Y => Y)
是透過 DataFrame
型別的其中一個建構子(constructor) DataFrame(pairs::Pair...; makeunique::Bool=false, copycols::Bool=true)
所建構。methods(DataFrame)
可以查到DataFrame
所有的建構子。我們可以把建構子想成與型別名稱相同的函式。
String(:X)
是透過 String
型別下的一個接受符號(Symbol)類別 :X
為輸入的建構子,建構字串"X"
這個物件。
String(:X)
"X"
Symbol("X")
:X
我們常常可以用型別建構子將物件"轉換"成我們想要的類型。
x0 = 1.1; x1 = 7.63;
id0, id1 = floor(x0), floor(x1) # 同 matlab 的 floor
(1.0, 7.0)
id0:id1
1.0:1.0:7.0
Int64(id0):Int64(id1)
1:7
Vector{DataType}
=Array{DataType,1}
)Matrix{DataType}
=Array{DataType,2}
)⚠️ 注意:
1:10
)只記載了起始、終止和間隔這三項資訊,需要透過collect()
才能變成向量。例如:x = 1:5
1:5
v = collect(x)
5-element Vector{Int64}: 1 2 3 4 5
typeof([1.0,2.0,3.0])
Vector{Float64} (alias for Array{Float64, 1})
typeof([1 2; 3 4])
Matrix{Int64} (alias for Array{Int64, 2})
typeof(1:5)
UnitRange{Int64}
typeof(collect(1:5))
Vector{Int64} (alias for Array{Int64, 1})
⚠️ 注意:陣列(Array) 是可變的(mutable)。這時候 v1 = v
相當於將新變數名(v1)指向舊變數(v)。如果想要複製變數,請用 copy
(e.g., v1 = copy(v)
)。
v1 = v
5-element Vector{Int64}: 1 2 3 4 5
v1[end] = 99; v1
5-element Vector{Int64}: 1 2 3 4 99
v
5-element Vector{Int64}: 1 2 3 4 99
Tuple
)Dict
)Pair
)Set
)數組的建立是使用小括號和逗號分隔的元素,可以是由不同型別的元素組成。
數組是不可變的 (immutable),所以建立後元素值是不能被改變的,也無法再增加元素。
a = (1,2,"b")
(1, 2, "b")
"b" in a # the same as in("b", a)
true
a[3]
"b"
Pair 型別是用來建立 key / value 兩種物件對應的型別,Pair 也是不可變的 (immutable)。Pair 的建立可以透過建構子或是 key=>value
語法建立。
# 透過 Pair constructor 建立
a = Pair("key1", 1)
"key1" => 1
# 透過 key=>value 語法建立 Pair
a = "key1"=> 1
"key1" => 1
for p in a
println(p)
end
key1 1
可以透過數組或配對來建立字典
a = Dict([("A", 1), ("B", 2), ("C", 3)])
Dict{String, Int64} with 3 entries: "B" => 2 "A" => 1 "C" => 3
a = Dict("A"=> 1, "B"=> 2, "C"=> 3)
Dict{String, Int64} with 3 entries: "B" => 2 "A" => 1 "C" => 3
for (key, value) in a
println("$key is $value")
end
B is 2 A is 1 C is 3
新增條目
a["D"] = 4
4
delete!
pop!
, popfirst!
keys
, values
haskey
💡在julia REPL 輸入 ?<關鍵字> 獲得幫助!
a = Set([1, 2, 3, 4, 5, 5, 6])
Set{Int64} with 6 elements: 5 4 6 2 3 1
4 in a
true
聯集
b = Set([1, 3, 5])
Set{Int64} with 3 elements: 5 3 1
union(a, b)
Set{Int64} with 6 elements: 5 4 6 2 3 1
union
or ∪
(LaTeX 語法為 \cup)intersect
or ∩
(LaTeX 語法為 \cap)in
or ∈
(LaTeX 語法為 \in)setdiff
issubset
or ⊆
(LaTeX 語法為 \subseteq)a ∩ b
Set{Int64} with 3 elements: 5 3 1
比較特別的是 if ... end
本身會回傳值
function foo(x)
if x > 0
"positive!"
elseif x == 0
"zero"
else
"negative..."
end
end
println("x is ", foo(3))
x is positive!
using BenchmarkTools
┌ Info: Precompiling BenchmarkTools [6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf] └ @ Base loading.jl:1342
@benchmark let
n = 10000;
A = collect(1:n)
answer = fill(NaN, n); # 類似 matlab 的 nan(n,1)
for i = 1:5
A_i = A[i];
answer[i] = A_i^2;
end
end
BenchmarkTools.Trial: 10000 samples with 1 evaluation. Range (min … max): 7.800 μs … 3.735 ms ┊ GC (min … max): 0.00% … 98.68% Time (median): 29.000 μs ┊ GC (median): 0.00% Time (mean ± σ): 32.756 μs ± 116.999 μs ┊ GC (mean ± σ): 19.28% ± 5.69% ▂█ ▃ ▁▂▃██▃▂▃▅▅▃▂▂▁▁▁▁▃██▄▃▃▄▄▄▃▃▃▃▃▂▂▁▁▁▁▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▂ 7.8 μs Histogram: frequency by time 77.6 μs < Memory estimate: 156.41 KiB, allocs estimate: 4.
@benchmark let
n = 10000;
A = collect(1:n)
answer = [];
for A_i in A
answer = [answer..., A_i^2]; # 類似 matlab 的 answer = [answer, A_i^2];
end
answer
end
BenchmarkTools.Trial: 2 samples with 1 evaluation. Range (min … max): 2.855 s … 3.009 s ┊ GC (min … max): 15.10% … 18.39% Time (median): 2.932 s ┊ GC (median): 16.79% Time (mean ± σ): 2.932 s ± 108.279 ms ┊ GC (mean ± σ): 16.79% ± 2.33% █ █ █▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁█ ▁ 2.86 s Histogram: frequency by time 3.01 s < Memory estimate: 1.49 GiB, allocs estimate: 49822676.
julia 有簡單好用的 push!
,不用預分配也有不錯的速度:
@benchmark let
n = 10000;
A = collect(1:n)
answer = [];
for A_i in A
push!(answer, A_i^2)
end
answer
end
BenchmarkTools.Trial: 8900 samples with 1 evaluation. Range (min … max): 178.800 μs … 99.053 ms ┊ GC (min … max): 0.00% … 99.44% Time (median): 397.800 μs ┊ GC (median): 0.00% Time (mean ± σ): 559.061 μs ± 4.071 ms ┊ GC (mean ± σ): 33.67% ± 4.60% ▆█ ▁▁▁▆▇▄▅▇▄▃▂▂▂▂▂▁▁▁▁▂▅▂▄▄▂▅▅▄▆███▆▃▃▃▄▆▆▅▃▃▂▂▂▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▂ 179 μs Histogram: frequency by time 625 μs < Memory estimate: 490.75 KiB, allocs estimate: 9994.
@benchmark let
n = 10000;
A = collect(1:n)
answer = Int64[];
for A_i in A
push!(answer, A_i^2)
end
answer
end
BenchmarkTools.Trial: 10000 samples with 1 evaluation. Range (min … max): 118.400 μs … 124.757 ms ┊ GC (min … max): 0.00% … 99.82% Time (median): 342.500 μs ┊ GC (median): 0.00% Time (mean ± σ): 378.699 μs ± 3.018 ms ┊ GC (mean ± σ): 21.05% ± 2.64% █▁▃▂ ▆▅▁ ▂▇████▄▃▄▃▂▃▂▂▂▁▂▁▁▂▁▁▁▂▄▂▃▄▃▅▆▅▅████▇▇▇▇▇▇▆▅▄▃▃▂▃▂▂▂▂▁▂▁▁▁▁▁ ▃ 118 μs Histogram: frequency by time 525 μs < Memory estimate: 334.84 KiB, allocs estimate: 16.
宣告 x 和 y,並將值均指定為 3。
x = y = 3
3
x > 3 || y >3
false
v = collect(1:5) # v = [1,2,3,4,5]
v .> 3
5-element BitVector: 0 0 0 1 1
function f(x)
x^2 + 3x +1
end
f (generic function with 2 methods)
f(3)
19
v = [1,2,3,4,5,6];
f.(v)
6-element Vector{Int64}: 5 11 19 29 41 55
例如,
'HelloWorld'
➡️ ❌ ERROR: syntax: character literal contains multiple characters"HelloWorld"
➡️ ✔️ PASS!x = [5, 6, 7]; sin(x)
➡️ ❌ ERROR: MethodError: no method matching sin(::Vector{Int64})x = [5, 6, 7]; sin.(x)
➡️ ✔️ PASS!x = [5,6,7]; x > 3
➡️ ❌ MethodError: no method matching isless(::Int64, ::Vector{Int64})x = [5,6,7]; x .> 3
➡️ ✔️ PASS!function f(x) x^2 + 3x +1; end
➡️ ✔️ PASS!a = [1,2,3]; f.(a)
➡️ ✔️ PASS!for i=1:10 print(i) end
➡️ ✔️ PASS!a = [1]; for i = [1.2, 2.3, 3.1] push!(a, i); end
➡️ ❌ ERROR: InexactError: Int64(1.2)a = Float64[1]; for i = [1.2, 2.3, 3.1] push!(a, i); end
➡️ ✔️ PASS!a = [1.0]; for i = [1.2, 2.3, 3.1] push!(a, i); end
➡️ ✔️ PASS!function drop(v::Vector)
nothing # write your code here!
end
function drop(m::Matrix)
nothing # write your code here!
end
drop (generic function with 2 methods)
using Test
@testset "Testing drop(v::Vector)" begin
@test all([1,3,5] .== drop([1,3,5,7]))
v2 = rand(5);
@test all(v2[1:end-1] .== drop(v2))
end
@testset "Testing drop(v::Matrix)" begin
m1 = randn(3,2);
@test all(drop(m1) .== m1[1:end-1, :])
end
Testing drop(v::Vector): Test Failed at In[74]:3 Expression: all([1, 3, 5] .== drop([1, 3, 5, 7])) Stacktrace: [1] macro expansion @ In[74]:3 [inlined] [2] macro expansion @ C:\buildbot\worker\package_win64\build\usr\share\julia\stdlib\v1.6\Test\src\Test.jl:1151 [inlined] [3] top-level scope @ In[74]:3 Testing drop(v::Vector): Test Failed at In[74]:5 Expression: all(v2[1:end - 1] .== drop(v2)) Stacktrace: [1] macro expansion @ In[74]:5 [inlined] [2] macro expansion @ C:\buildbot\worker\package_win64\build\usr\share\julia\stdlib\v1.6\Test\src\Test.jl:1151 [inlined] [3] top-level scope @ In[74]:3 Test Summary: | Fail Total Testing drop(v::Vector) | 2 2
Some tests did not pass: 0 passed, 2 failed, 0 errored, 0 broken.
Stacktrace:
[1] finish(ts::Test.DefaultTestSet)
@ Test C:\buildbot\worker\package_win64\build\usr\share\julia\stdlib\v1.6\Test\src\Test.jl:913
[2] macro expansion
@ C:\buildbot\worker\package_win64\build\usr\share\julia\stdlib\v1.6\Test\src\Test.jl:1161 [inlined]
[3] top-level scope
@ In[74]:3
[4] eval
@ .\boot.jl:360 [inlined]
[5] include_string(mapexpr::typeof(REPL.softscope), mod::Module, code::String, filename::String)
@ Base .\loading.jl:1116
lastnan!
函式,包含2個方法:function lastnan!(v::Vector)
nothing # write your code here!
end
function lastnan!(v::Matrix)
nothing # write your code here!
end
lastnan! (generic function with 2 methods)
@testset "Testing lastnan!(v::Vector)" begin
v = randn(10);
v0 = copy(v);
lastnan!(v)
@test all(v0[1:end-1] .== v[1:end-1])
@test isnan(v[end])
end
@testset "Testing lastnan!(v::Matrix)" begin
v = randn(10,10);
v0 = copy(v);
lastnan!(v)
@test all(v0[1:end-1,:] .== v[1:end-1,:])
@test all(isnan.(v[end,:]))
end
Test Summary: | Pass Total Testing lastnan!(v::Vector) | 2 2 Test Summary: | Pass Total Testing lastnan!(v::Matrix) | 2 2
Test.DefaultTestSet("Testing lastnan!(v::Matrix)", Any[], 2, false, false)