Một tập hợp các ví dụ không “chính thống” về lập trình Julia – một ngôn ngữ lập trình động, bậc cao, hiệu năng cao dành cho tính toán kĩ thuật.
Dưới đây là một loạt các ví dụ về những phép tính thông dụng trong Julia. Coi như là bạn đã cài đặt Julia vào máy và khởi động rồi (các ví dụ này đã được kiểm tra với Julia v1.0.5).
https://juliabyexample.helpmanual.io
Hello World
println("hello world")
#> hello world
Với Julia đã được cài đặt và thêm vào đường dẫn trong máy tính của bạn, mã lệnh này có thể được chạy bằng cách gõ julia hello_world.jl
. Cũng có thể chạy nó từ dấu nhắc lệnh trong Julia bằng cách gõ include("hello_world.jl")
, bằng cách thứ hai này sẽ tính toán tất cả những biểu thức hợp lệ có trong file chương trình và trả lại kết quả của biểu thức cuối cùng.
Các hàm đơn giản
Ví dụ trên cho thấy hai hàm đơn giản, cách gọi chúng và in kết quả. Những ví dụ kĩ hơn về định dạng số sẽ được trình bày sau đây.
# hàm để tính thể tích một khối cầu
function sphere_vol(r)
# julia cho phép đặt tên theo font Unicode (UTF-8). Xem thêm: https://docs.julialang.org/en/v1/manual/unicode-input/#Unicode-Input-1
# (bạn có thể viết các tên hàm, tên biến bằng chữ tiếng Việt có dấu
# Vì dùng font Unicode, bạn có thể viết "pi" hoặc kí hiệu π
return 4/3*pi*r^3
end
# hàm có thể được định nghĩa gọn hơn, ví dụ như hàm tính nghiệm PT bậc 2 dưới đây
quadratic(a, sqr_term, b) = (-b + sqr_term) / 2a
# giải PT bậc 2: 0 = a*x^2+b*x+c để tìm x, các kiểu đối số có thể được định nghĩa từ đầu
# (xem thêm về kiểu đối số: https://docs.julialang.org/en/v1/manual/functions/#Further-Reading-1)
function quadratic2(a::Float64, b::Float64, c::Float64)
# khác với những ngôn ngữ khác, 2a ở đây tương đương với 2*a
# a^2 được dùng thay cho a**2 hoặc pow(a,2)
sqr_term = sqrt(b^2-4a*c)
r1 = quadratic(a, sqr_term, b)
r2 = quadratic(a, -sqr_term, b)
# một hàm có thể trả lại nhiều giá trị bằng các cặp (tuple)
# nếu bỏ qua từ khoá return, thì biểu thức cuối cùng sẽ được trả lại
r1, r2
end
vol = sphere_vol(3)
# @printf cho phép định dạng số nhưng không tự động chèn thêm dấu xuống dòng \n vào câu lệnh, xem dưới đây
using Printf
@printf "volume = %0.3f\n" vol
#> volume = 113.097
quad1, quad2 = quadratic2(2.0, -2.0, -12.0)
println("nghiệm 1: ", quad1)
#> result 1: 3.0
println("nghiệm 2: ", quad2)
#> result 2: -2.0
Cơ bản về chuỗi
Tập hợp các ví dụ khác nhau về chuỗi (đánh chỉ số chuỗi cũng giống như đánh chỉ số mảng: xem bên dưới).
# các chuỗi được xác định bằng cặp dấu nháy kép
# cũng như các biến, chuỗi có thể chứa bất kì kí tự Unicode nào
s1 = "The quick brown fox jumps over the lazy dog α,β,γ"
println(s1)
#> The quick brown fox jumps over the lazy dog α,β,γ
# println sẽ xuống dòng sau khi in xong
# print cũng có thể dùng nếu bạn không muốn xuống dòng
print("this")
#> this
print(" and")
#> and
print(" that.\n")
#> that.
# các kí tự được định nghĩa bằng cặp nháy đơn
c1 = 'a'
println(c1)
#> a
# giá trị ascii của một kí tự có thể nhận được bằng hàm Int():
println(c1, " ascii value = ", Int(c1))
#> a ascii value = 97
println("Int('α') == ", Int('α'))
#> Int('α') == 945
# do vậy bạn phải thấy được
println(Int('1') == 1)
#> false
# các chuỗi có thể được chuyển đổi về dạng viết chữ in hoặc chữ thường:
s1_caps = uppercase(s1)
s1_lower = lowercase(s1)
println(s1_caps, "\n", s1_lower)
#> THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG Α,Β,Γ
#> the quick brown fox jumps over the lazy dog α,β,γ
# các chuỗi con có thể được đánh chỉ số như các mảng:
# (còn show sẽ in ra giá trị thô)
show(s1[11]); println()
#> 'b'
# hoặc có thể tạo được các chuỗi con:
show(s1[1:10]); println()
#> "The quick "
# end được dùng để chỉ vị trí cuối mảng hoặc chuỗi
show(s1[end-10:end]); println()
#> "dog α,β,γ"
# julia cho phép nội suy chuỗi (string interpolation):
a = "chào mừng"
b = "julia"
println("$a đến $b.")
#> chào mừng đến julia.
# cách này có thể dùng để tính các biểu thức:
println("1 + 2 = $(1 + 2)")
#> 1 + 2 = 3
# chuỗi cũng có thể được kết nối bằng toán tử *
# dùng * chứ không phải + có thể khiến bạn hơi bỡ ngỡ khi bắt đầu dùng Julia,
# nhưng người ta vẫn cho rằng dấu * là hợp lý hơn
s2 = "this" * " and" * " that"
println(s2)
#> this and that
# cũng như là dùng hàm string
s3 = string("this", " and", " that")
println(s3)
#> this and that
Chuỗi: Chuyển đổi và định dạng
# Các chuỗi có thể được chuyển đổi bằng float và int: e_str1 = "2.718" e = parse(Float64, e_str1) println(5e) #> 13.59 num_15 = parse(Int, "15") println(3num_15) #> 45 # Các số có thể được chuyển thành chuỗi và định dạng bằng printf using Printf @printf "e = %0.2f\n" e #> e = 2.72 # hoặc để tạo chuỗi mới bằng sprintf e_str2 = @sprintf("%0.3f", e) # Để cho thấy rằng 2 chuỗi giống hệt nhau không println("e_str1 == e_str2: $(e_str1 == e_str2)") #> e_str1 == e_str2: true # Các kí tự để định dạng số gồm có f, e, a, g, c, s, p, d: # (pi là một hằng số định nghĩa trước; tuy nhiên vì dạng của nó là # "MathConst", nó cần được chuyển đổi thành dạng dấu phẩy động trước khi định dạng) @printf "cố định phần thập phân: %0.3f\n" float(pi) #> cố định phần thập phân: 3.142 @printf "dạng khoa học: %0.6e\n" 1000pi #> dạng khoa học: 3.141593e+03 @printf "dấu phẩy động định dạng hex (16): %a\n" 0xff #> dấu phẩy động định dạng hex (16): 0xf.fp+4 @printf "cố định phần thập phân: %g\n" pi*1e8 #> cố định phần thập phân: 3.14159e+08 @printf "một kí tự: %c\n" 'α' #> một kí tự: α @printf "một chuỗi: %s\n" "look I'm a string!" #> một chuỗi: look I'm a string! @printf "căn lề phải chuỗi sau trong khoảng 50 kí tự: %50s\n" "width 50, text right justified!" #> căn lề phải chuỗi sau trong khoảng 50 kí tự: width 50, text right justified! @printf "một con trỏ: %p\n" 100000000 #> một con trỏ: 0x0000000005f5e100 @printf "in ra một số nguyên: %d\n" 1e10 #> in ra một số nguyên: 10000000000
Thao tác với chuỗi
s1 = "The quick brown fox jumps over the lazy dog α,β,γ" # search trả lại chỉ số đầu tiên của một kí tự i = findfirst(isequal('b'), s1) println(i) #> 11 # tham số thứ hai tương đương với tham số thứ hai trong hàm split, xem dưới đây # hoặc một khoảng nếu ta gọi nó với một chuỗi khác r = findfirst("brown", s1) println(r) #> 11:15 # cách thay thế chuỗi được làm như sau: r = replace(s1, "brown" => "red") show(r); println() #> "The quick red fox jumps over the lazy dog α,β,γ" # tìm kiếm và thay thễ cũng có thể nhận các biểu thức thường quy (regular expressions) bằng cách viết 'r' phía trước chuỗi: r = findfirst(r"b[\w]*n", s1) println(r) #> 11:15 # một lần nữa với biểu thức thường quy r = replace(s1, r"b[\w]*n" => "red") show(r); println() #> "The quick red fox jumps over the lazy dog α,β,γ" # cũng có những hàm cho các biểu thức thường quy để trả lại những kiểu RegexMatch # match sẽ rà soát từ trái qua phải để tìm vị trí khớp đầu tiên (hoặc ta có thể cho một chỉ số bắt đầu) r = match(r"b[\w]*n", s1) println(r) #> RegexMatch("brown") # Các kiểu RegexMatch có một thuộc tính match; thuộc tính này sẽ lưu giữ chuỗi được khớp show(r.match); println() #> "brown" # eachmatch trả lại một bộ lặp gồm tất cả những chuỗi khớp r = eachmatch(r"[\w]{4,}", s1) for i in r print("\"$(i.match)\" ") end #> "quick" "brown" "jumps" "over" "lazy" println() r = collect(m.match for m = eachmatch(r"[\w]{4,}", s1)) println(r) #> SubString{String}["quick", "brown", "jumps", "over", "lazy"] # một chuỗi có thể được lặp lại bằng hàm repeat, # hoặc viết gọn hơn bằng cú pháp ^: r = "hello "^3 show(r); println() #> "hello hello hello " # hàm strip có tác dụng giống như trong Python: # ví dụ với một tham số, nó sẽ gọt bớt các dấu trống ở hai phía đầu chuỗi r = strip("hello ") show(r); println() #> "hello" # hoặc với một tham số thứ hai là một mảng các kí tự, hàm này sẽ gọt bất kì kí tự nào có tên trong bảng này r = strip("hello ", ['h', ' ']) show(r); println() #> "ello" # (chú ý: mảng chứa các kí tự chứ không phải các chuỗi) # tương tự, split cũng có tác dụng cơ bản như trong Python: r = split("hello, there,bob", ',') show(r); println() #> SubString{String}["hello", " there", "bob"] r = split("hello, there,bob", ", ") show(r); println() #> SubString{String}["hello", "there,bob"] r = split("hello, there,bob", [',', ' '], limit=0, keepempty=false) show(r); println() #> SubString{String}["hello", "there", "bob"] # (hai tham số cuối tương đương với limit và include_empty trong Python, xem các tài liệu) # ngược lại với split, join, đơn giản như sau: r = join(collect(1:10), ", ") println(r) #> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
Mảng
function printsum(a) # summary dùng để tạo ra thông tin tóm tắt về một đối tượng println(summary(a), ": ", repr(a)) end # mảng có thể được khởi tạo trực tiếp: a1 = [1,2,3] printsum(a1) #> 3-element Array{Int64,1}: [1, 2, 3] # hoặc khởi tạo là mảng trống: a2 = [] printsum(a2) #> 0-element Array{Any,1}: Any[] # vì mảng này không có kiểu, nên những hàm như push! (xem bên dưới) không dùng được # thay vào đó, các mảng có thể được khởi tạo với một kiểu nhất định: a3 = Int64[] printsum(a3) #> 0-element Array{Int64,1}: Int64[] # các khoảng thì khác với mảng: a4 = 1:20 printsum(a4) #> 20-element UnitRange{Int64}: 1:20 # tuy nhiên, chúng có thể được dùng để tạo ra mảng, như sau: a4 = collect(1:20) printsum(a4) #> 20-element Array{Int64,1}: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, #> 15, 16, 17, 18, 19, 20] # các mảng cũng có thể được tạo ra bằng cách liệt kê đầy đủ (comprehension): a5 = [2^i for i = 1:10] printsum(a5) #> 10-element Array{Int64,1}: [2, 4, 8, 16, 32, 64, 128, 256, 512, 1024] # mảng cũng có thể chứa kiểu bất kì, nên ta có thể tạo ra mảng chứa các mảng: a6 = (Array{Int64, 1})[] printsum(a6) #> 0-element Array{Array{Int64,1},1}: Array{Int64,1}[] # (lưu ý: đây là một "mảng lồng ghép" (theo nghĩa một mảng chứa các mảng), # chứ không phải mảng đa chiều - loại này không được đề cập ở đây). # Julia cung cấp một số hàm cho hàng đợi hai đầu ("Dequeue"), # hàm thông dụng nhất để bổ sung vào cuối của mảng là push! # dấu ! ở cuối tên hàm ngụ ý rằng tham số thứ nhất được cập nhật. push!(a1, 4) printsum(a1) #> 4-element Array{Int64,1}: [1, 2, 3, 4] # push!(a2, 1) sẽ gây lỗi: push!(a3, 1) printsum(a3) #> 1-element Array{Int64,1}: [1] #> 1-element Array{Int64,1}: [1] push!(a6, [1,2,3]) printsum(a6) #> 1-element Array{Array{Int64,1},1}: Array{Int64,1}[[1, 2, 3]] # sử dụng repeat() để tạo mảng # bạn phải dùng các từ khoá "inner" và "outer" # tất cả đối số phải là mảng (chứ không phải khoảng) a7 = repeat(a1,inner=[2],outer=[1]) printsum(a7) #> 8-element Array{Int64,1}: [1, 1, 2, 2, 3, 3, 4, 4] a8 = repeat(collect(4:-1:1),inner=[1],outer=[2]) printsum(a8) #> 8-element Array{Int64,1}: [4, 3, 2, 1, 4, 3, 2, 1]
Xử lý lỗi
# try, catch có thể được dùng để xử lý những lỗi, giống như các ngôn ngữ lập trình khác try push!(a,1) catch err showerror(stdout, err, backtrace());println() end #> UndefVarError: a not defined #> Stacktrace: #> [1] top-level scope at C:\JuliaByExample\src\error_handling.jl:5 #> [2] include at .\boot.jl:317 [inlined] #> [3] include_relative(::Module, ::String) at .\loading.jl:1038 #> [4] include(::Module, ::String) at .\sysimg.jl:29 #> [5] exec_options(::Base.JLOptions) at .\client.jl:229 #> [6] _start() at .\client.jl:421 println("Continuing after error") #> Continuing after error
Mảng đa chiều
Julia có khả năng làm việc với mảng đa chiều rất tốt. Hãy xem tài liệu hướng dẫn.
# repeat có thể giúp ích cho việc mở rộng một lưới số liệu
# cũng như hàm expand.grid() của ngôn ngữ lập trình R:
m1 = hcat(repeat([1,2],inner=[1],outer=[3*2]),
repeat([1,2,3],inner=[2],outer=[2]),
repeat([1,2,3,4],inner=[3],outer=[1]))
printsum(m1)
#> 12×3 Array{Int64,2}: [1 1 1; 2 1 1; 1 2 1; 2 2 2; 1 3 2; 2 3 2; 1 1 3; 2 1 3;
#> 1 2 3; 2 2 4; 1 3 4; 2 3 4]
# để lặp lại các mảng một cách đơn giản,
# hãy dùng repeat
m2 = repeat(m1,1,2) # lặp a9 một lần theo chiều dim1 và hai lần theo chiều dim2
println("size: ", size(m2))
#> size: (12, 6)
m3 = repeat(m1,2,1) # lặp a9 hai lần theo chiều dim1 và một lần theo chiều dim2
println("size: ", size(m3))
#> size: (24, 3)
# Phép liệt kê đầy đủ (comprehension) cũng là một cách khác để ta dễ dàng
# tạo được những mảng đa chiều
m4 = [i+j+k for i=1:2, j=1:3, k=1:2] # tạo nên một mảng 2x3x2 các phần tử kiểu Int64
m5 = ["Hi Im # $(i+2*(j-1 + 3*(k-1)))" for i=1:2, j=1:3, k=1:2]
# các biểu thức rất linh hoạt
# bạn có thể chỉ định kiểu của mảng
# chỉ bằng việc ghi nó phía trước biểu thức
# (chẳng hạn, dưới đây chỉ định kiểu chuỗi `LegacyString.ASCIIString
`
# cho các phần tử trong mảng).
import LegacyStrings
m5 = LegacyStrings.ASCIIString["Hi Im element # $(i+2*(j-1 + 3*(k-1)))" for i=1:2, j=1:3, k=1:2]
printsum(m5)
#> 2×3×2 Array{LegacyStrings.ASCIIString,3}: LegacyStrings.ASCIIString[
#> "Hi Im element # 1" "Hi Im element # 3" "Hi Im element # 5";
#> "Hi Im element # 2" "Hi Im element # 4" "Hi Im element # 6"]
#>
#> LegacyStrings.ASCIIString["Hi Im element # 7" "Hi Im element # 9"
#> "Hi Im element # 11"; "Hi Im element # 8" "Hi Im element # 10" "Hi Im element # 12"]
# Thống kê trong mảng (array reduction)
# nhiều hàm trong Julia có một phương thức mảng
# áp dụng được cho từng chiều riêng của một mảng:
sum(m4, dims=3) # lấy tổng dọc theo chiều thứ ba
sum(m4, dims=(1,3)) # tổng dọc theo các chiều thứ nhất và thứ ba
maximum(m4, dims=2) # tìm phần tử lớn nhất dọc theo chiều thứ 2
findmax(m4, dims=3) # tìm phần tử lớn nhất cùng với chỉ số của nó dọc theo chiều thứ 3
# (hàm findmax chỉ có ở những phiên bản Julia gần đây)
# Quảng bá
# khi bạn kết hợp các mảng có kích thước khác nhau trong cùng một phép toán
# thì Julia sẽ cố gắng "trải ra" hoặc "quảng bá" mảng nhỏ hơn sao cho
# hai mảng trở nên cùng kích thước. Toán tử quảng bá có một dấu chấm đứng trước:
m4 .+ 3 # cộng 3 vào tất cả phần tử
m4 .+ [1,2] # cộng vec tơ [1,2] vào tất cả phần tử dọc theo chiều thứ nhất
# lát cắt và khung ngắm
m4 = m4[:,:,1] # giữ cố định chiều thứ 3
m4[:,2,:] # đó là một mảng 2x1x2, thật khó hình dung
# loại bỏ những chiều nào vốn chỉ có kích cỡ bằng 1:
dropdims(m4[:,2,:], dims=2) # thế này hay hơn
# gán những giá trị mới vào một khung ngắm nhất định
m4[:,:,1] = rand(1:6,2,3)
printsum(m4)
#> 2×3 Array{Int64,2}: [3 5 3; 1 3 5]
# (để xem thêm các ví dụ về try - catch, hãy trở lại Mục Xử lý lỗi bên trên)
try
# lệnh kế tiếp sẽ gây lỗi; lẽ ra bạn phải gán kiểu dữ liệu đúng
m4[:,:,1] = rand(2,3)
catch err
println(err)
end
#> InexactError(:Int64, Int64, 0.7603891754678744)
try
# lệnh kế tiếp sẽ gây lỗi; lẽ ra bạn phải gán hình dạng đúng
m4[:,:,1] = rand(1:6,3,2)
catch err
println(err)
end
#> DimensionMismatch("tried to assign 3×2 array to 2×3×1 destination")
Từ điển
Julia sử dụng Dicts như các tập hợp có liên kết tương ứng. Cách dùng rất giống như ngôn ngữ Python, chỉ trừ cách viết định nghĩa =>
khá kì quặc.
# từ điển có thể được khởi tạo một cách trực tiếp: a1 = Dict(1=>"one", 2=>"two") printsum(a1) #> Dict{Int64,String} with 2 entries: Dict(2=>"two",1=>"one") # và sau đó được bổ sung: a1[3]="three" printsum(a1) #> Dict{Int64,String} with 3 entries: Dict(2=>"two",3=>"three",1=>"one") # (note dicts cannot be assumed to keep their original order) # từ điển cũng có thể được tạo nên với kiểu dữ liệu được nêu rõ a2 = Dict{Int64, AbstractString}() a2[0]="zero" printsum(a2) #> Dict{Int64,AbstractString} with 1 entry: Dict{Int64,AbstractString}(0=>"zero") # từ điển, cũng như các mảng, có thể được khởi tạo từ bản kê đầy đủ (comprehension) using Printf a3 = Dict([i => @sprintf("%d", i) for i = 1:10]) printsum(a3) #> Dict{Int64,String} with 10 entries: Dict(7=>"7",4=>"4",9=>"9",10=>"10", #> 2=>"2",3=>"3",5=>"5",8=>"8",6=>"6",1=>"1") # như ta mong đợi, Julia có tất cả các hàm trợ giúp thong thường # đối với từ điển, chẳng hạn haskey println(haskey(a1,1)) #> true # vốn tương đương với println(1 in keys(a1)) #> true # trong đó keys tạo ra một bộ lặp cho các khóa trong từ điển # cũng giống như keys, values sẽ lấy bộ lặp cho các giá trị của từ điển: printsum(values(a1)) #> Base.ValueIterator for a Dict{Int64,String} with 3 entries: ["two", "three", "one"] # sử dụng collect để thu được một mảng printsum(collect(values(a1))) #> 3-element Array{String,1}: ["two", "three", "one"]
Vòng lặp và ánh xạ
Các vòng lặp for có thể được định nghĩa bằng nhiều cách.
for i in 1:5 print(i, ", ") end #> 1, 2, 3, 4, 5, # Trong các lời định nghĩa vòng lặp thì "in" tương đương với "=" # (tức là, lệnh trên sẽ tương đương với lệnh dưới) for i = 1:5 print(i, ", ") end println() #> 1, 2, 3, 4, 5, # các mảng cũng có thể được lặp một cách trực tiếp: a1 = [1,2,3,4] for i in a1 print(i, ", ") end println() #> 1, 2, 3, 4, # continue và break là các từ khoá hoạt động tương tự như lập trình Python: a2 = collect(1:20) for i in a2 if i % 2 != 0 continue end print(i, ", ") if i >= 8 break end end println() #> 2, 4, 6, 8, # Nếu mảng được xử lý trong quá trình tính toán, thì bạn cần phải dùng đến vòng lặp while. # Hàm pop có tác dụng loại bỏ phần tử cuối trong một mảng while !isempty(a1) print(pop!(a1), ", ") end println() #> 4, 3, 2, 1, d1 = Dict(1=>"one", 2=>"two", 3=>"three") # Các từ điển (dict) có thể được lặp qua bằng hàm keys: for k in sort(collect(keys(d1))) print(k, ": ", d1[k], ", ") end println() #> 1: one, 2: two, 3: three, # Cũng như trong Python, ta có thể dùng enumerate để lấy được cả chỉ số và giá trị trong một vòng lặp: a3 = ["one", "two", "three"] for (i, v) in enumerate(a3) print(i, ": ", v, ", ") end println() #> 1: one, 2: two, 3: three, # (lưu ý rằng enumerate sẽ bắt đầu từ 1 vì các mảng Julia được đánh chỉ số từ 1, không như Python) # Hàm map (ánh xạ) có tác dụng như bạn hình dung: thực hiện một hàm cho trước đối với từng phần tử # của một mảng hoặc một đối tượng lặp được, giống như phép liệt kê đầy đủ (comprehension) a4 = map((x) -> x^2, [1, 2, 3, 7]) print(a4) println() #> [1, 4, 9, 49]
Tính toán theo điều kiện
Các câu lệnh if/else hoạt động giống như các ngôn ngữ lập trình khác – những phần tử boolean là true
và false
.
if true println("It's true!") else println("It's false!") end #> It's true! if false println("It's true!") else println("It's false!") end #> It's false! # Các số có thể được so sánh bằng các toán tử như <, >, ==, != 1 == 1. #> true 1 > 2 #> false "foo" != "bar" #> true # và nhiều hàm trả lại các giá trị boolean occursin("that", "this and that") #> true # Các câu lệnh logic phức tạp có thể thực hiện được nhờ `elseif` function checktype(x) if x isa Int println("Look! An Int!") elseif x isa AbstractFloat println("Look! A Float!") elseif x isa Complex println("Số phức - complex!") else println("Tôi không biết đó là loại gì") end end checktype(2) #> Look! An Int! checktype(√2) #> Look! A Float! checktype(√Complex(-2)) #> Số phức - complex! checktype("who am I?") #> Tôi không biết đó là loại gì # Với các phép toán logic đơn giản, ta có thể dùng toán tử "ba ngôi" (ternary operator) để cho gọn, # toán tử này có dạng `mệnh_đề ? làm_nhánh_đúng : làm_nhánh_sai` 1 > 2 ? println("đúng rồi!") : println("sai rồi!") #> sai rồi! noisy_sqrt(x) = x ≥ 0 ? sqrt(x) : "Số âm mà!" noisy_sqrt(4) #> 2.0 noisy_sqrt(-4) #> Số âm mà! # "Định lượng đoản mạch" là một lựa chọn khác cho các câu lệnh điều kiện. # Những toán tử `&&` nghĩa là AND (và), `||` nghĩa là OR (hoặc) chỉ định lượng # vế phải của câu lệnh nếu cần, tuỳ theo mệnh đề. # Về mặt logic, nếu tôi muốn biết `42 == 0 AND x < y` đúng hay sai, # thì bất kể `x` và `y` là gì đi nữa, chỉ cần vế trước là sai thì toàn bộ biểu thức là sai. # This can be exploited to only evaluate a statement if something is true - # the second statement doesn't even have to be boolean! everything = 42 everything < 100 && println("đúng rồi!") #> "that's true!" everything == 0 && println("đúng rồi!") #> false √everything > 0 || println("sai rồi!") #> true √everything == everything || println("sai rồi!") #> sai rồi!
Kiểu dữ liệu
Kiểu dữ liệu là yếu tố quan trọng khi bạn tạo cấu trúc dữ liệu trong Julia.
# Định nghĩa kiểu có lẽ gần sát với typedef trong C
# một kiểu dữ liệu đơn giản mà không có hàm tạo dựng (constructor) đặc biệt nào có thể viết như sau:
mutable struct Người
tên::AbstractString
nam_giới::Bool
tuổi::Float64
số_con::Int
end
p = Người("Julia", false, 4, 0)
printsum(p)
#> Người: Người("Julia", false, 4.0, 0)
nhóm = Người[]
push!(nhóm, Người("Steve", true, 42, 0))
push!(nhóm, Người("Jade", false, 17, 3))
printsum(nhóm)
#> 2-element Array{Người,1}: Người[Người("Steve", true, 42.0, 0), Người("Jade", false, 17.0, 3)]
# các kiểu cũng có thể chứa các mảng và từ điển
# các hàm tạo dựng có thể được định nghĩa, để giúp ta dễ dàng tạo nên các đối tượng
mutable struct GiaĐình
tên::AbstractString
thành_viên::Array{AbstractString, 1}
thêm::Bool
# hàm tạo dựng nhận vào một tham biến và tạo nên giá trị mặc định còn lại
GiaĐình(tên::AbstractString) = new(tên, AbstractString[], false)
# hàm tạo dựng nhận vào một tham biến và suy luận ra giá trị còn lại
GiaĐình(tên::AbstractString, thành_viên) = new(tên, thành_viên, length(thành_viên) > 3)
end
gđ1 = GiaĐình("blogs")
println(gđ1)
#> GiaĐình("blogs", AbstractString[], false)
gđ2 = GiaĐình("jones", ["anna", "bob", "charlie", "dick"])
println(gđ2)
#> GiaĐình("jones", AbstractString["anna", "bob", "charlie", "dick"], true)
Đầu vào và đầu ra
Cú pháp cơ bản để đọc và ghi file trong Julia thì cũng tương tự như với Python.
File simple.dat
dùng trong ví dụ này có thể được lấy về từ Github.
fname = "simple.dat"
# dùng từ khóa 'do
' nghĩa là file được tự động đóng lại
# như cách dùng "with" trong Python.
# simple.dat là một file đơn giản chứa hai dòng chữ
# và hai con số, như nội dung của file được ghi ra dưới đây:
open(fname,"r") do f
for line in eachline(f)
println(line)
end
end
#> this is a simple file containing
#> text and numbers:
#> 43.3
#> 17
f = open(fname,"r")
show(readlines(f)); println()
#> ["this is a simple file containing", "text and numbers:", "43.3", "17"]
close(f)
f = open(fname,"r")
fstring = read(f, String)
close(f)
println(summary(fstring))
#> String
print(fstring)
#> this is a simple file containing
#> text and numbers:
#> 43.3
#> 17
outfile = "outfile.dat"
# Việc ghi ra file cũng đơn giản
f = open(outfile, "w")
# Cả print và println đều dùng được như thông thường nhưng thêm f là tham biến đầu tiên cho hàm
println(f, "some content")
print(f, "more content")
print(f, " more on the same line")
close(f)
# sau đó ta có thể kiểm tra nội dung file vừa được ghi ra
# "do" ở trên đã tạo ra một hàm khuyết danh và truyền nó đến open
# ta có thể suy luận tương tự, từ đó truyền readall, qua đó gom gọn tất cả
# open, read và close file vào cùng một dòng
outfile_content = open(f->read(f, String), outfile, "r")
println(repr(outfile_content))
#> "some content\nmore content more on the same line"
Các gói và việc kèm (include) file
Các gói (package) giúp ta mở rộng tính năng của thư viện chuẩn Julia.
# Có thể bạn sẽ không muốn chạy cả file này, vì các lệnh Pkg có thể # sẽ chạy rất lâu mới xong. using Pkg # liệt kê tất cả các gói hiện có: #Pkg.available() # cài đặt một gói (chẳng hạn Calculus) cùng tất cả thứ đi kèm theo nó: Pkg.add("Calculus") # để liệt kê tất cả những gói đã cài đặt Pkg.installed() # để cập nhật tất cả những gói lên đến phiên bản mới nhất của chúng Pkg.update() # để sử dụng một gói: using Calculus # sẽ nhập vào tất cả các hàm có trong gói đó vào không gian tên (namespace) hiện thời, # nhờ đó ta có thể gọi: derivative(x -> sin(x), 1.0) # mà không cần phải chỉ định tên gói chứa hàm này. import Calculus # sẽ cho phép bạn chỉ định cụ thể là hàm được gọi tương ứng với gói nào Calculus.derivative(x -> cos(x), 1.0) # Dùng `import` sẽ đặc biệt hữu ích nếu có sự xuong đột tên hàm / tên kiểu # giữa các gói.
Vẽ đồ thị
Việc vẽ đồ thị trong Julia chỉ thực hiện được bằng các gói phụ trợ. Ví dụ về một số gói chính được trình bày dưới đây
Plots
Gói Plots.jl được cài đặt qua lệnh
Pkg.add("Plots"); Pkg.add("GR");
using Plots # vẽ đồ thị cho vài số liệu plot([cumsum(rand(500) .- 0.5), cumsum(rand(500) .- 0.5)]) # lưu bản vẽ đồ thị hiện tại savefig("plots.svg") # .eps, .pdf, & .png cũng là các đuôi file được hỗ trợ # nhưng ở đây ta dùng svg vì nó bảo tồn tỉ lệ bề ngang và chiều cao đã nêu trên
DataFrames
Gói DataFrames.jl cho ta công cụ làm việc với dữ liệu dạng bảng dữ liệu.
File iris.csv
trong ví dụ này có thể lấy về từ github.
Bạn có thể cũng cần đến gói CSV.jl để đọc dữ liệu từ file CSV.
using DataFrames showln(x) = (show(x); println()) # TODO: cần thêm đường link đến tài liệu # Một DataFrame là một cơ sở dữ liệu lưu trong bộ nhớ df = DataFrame(A = [1, 2], B = [ℯ, π], C = ["xx", "xy"]) showln(df) #> 2×3 DataFrames.DataFrame #> │ Row │ A │ B │ C │ #> │ │ Int64 │ Float64 │ String │ #> ├─────┼───────┼─────────┼────────┤ #> │ 1 │ 1 │ 2.71828 │ xx │ #> │ 2 │ 2 │ 3.14159 │ xy │ # Các cột của một DataFrame có thể được gán chỉ số là các số hoặc tên gọi showln(df[!, 1]) #> [1, 2] showln(df[!, :A]) #> [1, 2] showln(df[!, 2]) #> [2.71828, 3.14159] showln(df[!, :B]) #> [2.71828, 3.14159] showln(df[!, 3]) #> ["xx", "xy"] showln(df[!, :C]) #> ["xx", "xy"] # Các hàng của một DataFrame có thể được đánh chỉ số chỉ dùng số: showln(df[1, :]) #> DataFrameRow #> │ Row │ A │ B │ C │ #> │ │ Int64 │ Float64 │ String │ #> ├─────┼───────┼─────────┼────────┤ #> │ 1 │ 1 │ 2.71828 │ xx │ showln(df[1:2, :]) #> 2×3 DataFrames.DataFrame #> │ Row │ A │ B │ C │ #> │ │ Int64 │ Float64 │ String │ #> ├─────┼───────┼─────────┼────────┤ #> │ 1 │ 1 │ 2.71828 │ xx │ #> │ 2 │ 2 │ 3.14159 │ xy │ # Nhập dữ liệu vào trong DataFrame # -------------------------------- using CSV # DataFrame có thể được nạp từ file CSV bằng cách dùng CSV.read() iris = CSV.read("iris.csv") # Bộ dữ liệu iris (cùng nhiệu bộ khác) có thể được lấy bằng cách using RData, RDatasets iris = dataset("datasets","iris") # Bạn có thể trực tiếp nhập từ file .rda của ngôn ngữ lập trình R với # mydf = load("path/to/your/df.rda")["name_of_df"], ví dụ diamonds = load(joinpath(dirname(pathof(RDatasets)),"..","data","ggplot2","diamonds.rda"))["diamonds"] # Hiển thị DataFrame # ------------------ # Kiểm tra tên và kiểu dữ liệu của từng cột trong DataFrame mới showln(names(iris)) #> Symbol[:SepalLength, :SepalWidth, :PetalLength, :PetalWidth, :Species] showln(eltypes(iris)) #> DataType[Float64, Float64, Float64, Float64, CategoricalString{UInt8}] # Phân chia tập con cho DataFrame để chỉ bao gồm các hàng cho một loài showln(iris[iris[!, :Species] .== "setosa", :]) #> 50×5 DataFrames.DataFrame #> │ Row │ SepalLength │ SepalWidth │ PetalLength │ PetalWidth │ Species │ #> │ │ Float64 │ Float64 │ Float64 │ Float64 │ Categorical… │ #> ├─────┼─────────────┼────────────┼─────────────┼────────────┼──────────────┤ #> │ 1 │ 5.1 │ 3.5 │ 1.4 │ 0.2 │ setosa │ #> │ 2 │ 4.9 │ 3.0 │ 1.4 │ 0.2 │ setosa │ #> │ 3 │ 4.7 │ 3.2 │ 1.3 │ 0.2 │ setosa │ #> │ 4 │ 4.6 │ 3.1 │ 1.5 │ 0.2 │ setosa │ #> │ 5 │ 5.0 │ 3.6 │ 1.4 │ 0.2 │ setosa │ #> │ 6 │ 5.4 │ 3.9 │ 1.7 │ 0.4 │ setosa │ #> │ 7 │ 4.6 │ 3.4 │ 1.4 │ 0.3 │ setosa │ #> ⋮ #> │ 43 │ 4.4 │ 3.2 │ 1.3 │ 0.2 │ setosa │ #> │ 44 │ 5.0 │ 3.5 │ 1.6 │ 0.6 │ setosa │ #> │ 45 │ 5.1 │ 3.8 │ 1.9 │ 0.4 │ setosa │ #> │ 46 │ 4.8 │ 3.0 │ 1.4 │ 0.3 │ setosa │ #> │ 47 │ 5.1 │ 3.8 │ 1.6 │ 0.2 │ setosa │ #> │ 48 │ 4.6 │ 3.2 │ 1.4 │ 0.2 │ setosa │ #> │ 49 │ 5.3 │ 3.7 │ 1.5 │ 0.2 │ setosa │ #> │ 50 │ 5.0 │ 3.3 │ 1.4 │ 0.2 │ setosa │ # Đếm số hàng dữ liệu cho từng loài showln(by(iris, :Species, df -> size(df, 1))) #> 3×2 DataFrames.DataFrame #> │ Row │ Species │ x1 │ #> │ │ Categorical… │ Int64 │ #> ├─────┼──────────────┼───────┤ #> │ 1 │ setosa │ 50 │ #> │ 2 │ versicolor │ 50 │ #> │ 3 │ virginica │ 50 │ # Xử lý dữ liệu theo từng cột một iris[!, :SepalLength] = round.(Integer, iris[!, :SepalLength]) iris[!, :SepalWidth] = round.(Integer, iris[!, :SepalWidth]) # Lập bảng dữ liệu tương ứng với các cột đã xử lý để nhìn thấy các "cụm" (cluster) tabulated = by( iris, [:Species, :SepalLength, :SepalWidth], df -> size(df, 1) ) showln(tabulated) #> 18×4 DataFrames.DataFrame #> │ Row │ Species │ SepalLength │ SepalWidth │ x1 │ #> │ │ Categorical… │ Int64 │ Int64 │ Int64 │ #> ├─────┼──────────────┼─────────────┼────────────┼───────┤ #> │ 1 │ setosa │ 5 │ 4 │ 17 │ #> │ 2 │ setosa │ 5 │ 3 │ 23 │ #> │ 3 │ setosa │ 4 │ 3 │ 4 │ #> │ 4 │ setosa │ 6 │ 4 │ 5 │ #> │ 5 │ setosa │ 4 │ 2 │ 1 │ #> │ 6 │ versicolor │ 7 │ 3 │ 8 │ #> │ 7 │ versicolor │ 6 │ 3 │ 27 │ #> ⋮ #> │ 11 │ virginica │ 6 │ 3 │ 24 │ #> │ 12 │ virginica │ 7 │ 3 │ 14 │ #> │ 13 │ virginica │ 8 │ 3 │ 4 │ #> │ 14 │ virginica │ 5 │ 2 │ 1 │ #> │ 15 │ virginica │ 7 │ 2 │ 1 │ #> │ 16 │ virginica │ 7 │ 4 │ 1 │ #> │ 17 │ virginica │ 6 │ 2 │ 3 │ #> │ 18 │ virginica │ 8 │ 4 │ 2 │ # Bạn có thể lập một dataframe có phân nhóm như sau: gdf = groupby(iris,[:Species, :SepalLength, :SepalWidth]) # rồi lặp qua bảng này for idf in gdf println(size(idf,1)) end # Thêm / bớt các cột # ------------------ # insert!(df::DataFrame,index::Int64,item::AbstractArray{T,1},name::Symbol) # thêm các số ngẫu nhiên ở cột thứ 5: insertcols!(iris, 5, :randCol => rand(nrow(iris))) # xóa cột này đi select!(iris, Not(:randCol))