julia 可跟 C, Python 的程式互動。另外 juila 支援 meta programming 的形式。
Interacting with OS
在 REPL 可用 ; 進入 shell,執行 shell commands,如果在 julia,則是直接用 pwd()
shell> pwd
/Users/user
julia> pwd()
"/Users/user"filesystem operations
存取檔案的 functions
- homedir(), joinpath() 
 - 為支援 linux, windows,使用 joinpath() 產生檔案的路徑 
julia> homedir()
"/Users/user"
julia> joinpath(homedir(), "Documents")
"/Users/user/Documents"- pwd(), cd() 
 - 取得目前的目錄,以及切換目錄 
 - cd("../")
- readdir() 
 - 取得目錄內的檔案列表,包含目錄及檔案 
 - readdir(".")
- mkdir() 
 - d = joinpath(homedir(), "LearningJulia") mkdir(d)
- stat 
 - stat("somefile")會取得- StatStruct(mode=0o100644, size=28676),而 StatStruct 裡面有這些欄位
 - ref: https://github.com/JuliaLang/julia/blob/master/base/stat.jl 
 - ex: - stat("somefile").size
 - Name - Description - size - The size (in bytes) of the file - device - ID of the device that contains the file - inode - The inode number of the file - mode - The protection mode of the file - nlink - The number of hard links to the file - uid - The user id of the owner of the file - gid - The group id of the file owner - rdev - If this file refers to a device, the ID of the device it refers to - blksize - The file-system preferred block size for the file - blocks - The number of such blocks allocated - mtime - Unix timestamp of when the file was last modified - ctime - Unix timestamp of when the file was created 
 - cp, mv
 
 - julia> cp("test", "test1") "test1" julia> isfile("test1") true julia> mv("test1", "test2") "test2"
- isdir, homedir, basename, dirname, and splitdir 
 - julia> homedir() "/Users/user" julia> joinpath(homedir(), "Documents") "/Users/user/Documents" julia> isdir(joinpath(homedir(), "Documents")) true julia> basename(homedir()) "user" julia> dirname(homedir()) "/Users" julia> splitdir(homedir()) ("/Users", "user")
- mkpath, ispath, abspath, and joinpath 
 - julia> ispath(homedir()) true julia> abspath(".") "/Users/user/Downloads/" julia> joinpath(homedir(), "Documents") "/Users/user/Documents" julia> mkpath(joinpath("Users","adm")) "Users/adm" julia> for (root, dirs, files) in walkdir("Users") println("Directories in $root") end Directories in Users Directories in Users/adm
I/O Operations
STDOUT, STDERR, STDIN 是三個 global variables,分別為 standard output, error, input stram
open(), close(), write, read
julia> file = open("sample.txt")
IOStream(<file sample.txt>)
julia> file
IOStream(<file sample.txt>)
julia> lines = readlines(file)
2-element Array{String,1}:
 "Hi there!"
 "Learning Julia is so much fun."
julia> write("sample.txt", "hi how are you doing?")
21
julia> read("sample.txt")
21-element Array{UInt8,1}:
julia> readline("sample.txt")
"hi how are you doing?"
julia> close(file)讀取某個文字檔,計算裡面有幾個 "Julia" 這個 word
# Arguments
# ARGS[0] is the command itself
in_file = ARGS[1]
out_file = ARGS[2]
# Keeping track using a counter
counter = 0
for line in eachline(in_file)
    for word in split(line)
        if word == "Julia"
            global counter += 1
        end
    end
end
# Write the contents to the o/p file
write(out_file, "The count for the word julia is $counter")
# Finally, read the contents to inform the user
for line in readlines(out_file)
    println(line)
endshell> julia sample.jl sample.txt out.txt
The count for the word julia is 4Calling C and Python
Calling C from Julia
compile (ex: LLVM) 需要知道什麼,才能由 Julia 呼叫 C
- Name of the library
- Name of the function
- Number and types of the arguments (也稱為 Arity)
- return type of the function
- values of the arguments passed
julia 使用 ccall((:name,"lib"), return_type, (arg1_type, arg2_type...), arg1, arg2)   處理
呼叫 C 標準函式庫的 clock function
julia> ccall((:clock, :libc), Int64, ())
3206897
julia> ccall((:clock, :libc), Cint, ())
3213482
julia> Int32 == Cint
trueCint 是 C 的資料型別,等於 signed int c-type,另外還有 Cint, Cuint, Clong, Culong, and Cchar
呼叫 getenv,取得 SHELL value,Ptr{Cchar} 是參數的資料型別,後面 return value 資料型別也是 Ptr{Cchar}
julia> syspath = ccall( (:getenv, :libc), Ptr{Cchar}, (Ptr{Cchar},), "SHELL")
Ptr{Int8} @0x00007ffee37b857a
julia> unsafe_string(syspath)
"/bin/bash"C 的 struct 可以替換為 julia 的 composite types
Calling Python form Julia
使用 PyCall
julia> using Pkg
julia> Pkg.add("PyCall")
julia> using PyCall
julia> py"len('julia')"
5
julia> py"str(5)"
"5"
julia> py"""
       print('hello world')
       """
hello world如果要使用 python 的 built-in data type (ex: dict),可使用 pybuiltin
julia> pybuiltin(:dict)(a=1,b=2)
Dict{Any,Any} with 2 entries:
  "b" => 2
  "a" => 1
julia> d = pycall(pybuiltin("dict"), Any, a=1, b=2)
PyObject {'b': 2, 'a': 1}
julia> typeof(d)
PyObject
# 利用 PyDict 轉換為 julia dict object
julia> julia_dictionary = PyDict{Symbol, Int64}(pycall(pybuiltin("dict"), Any, a=1, b=2))
PyDict{Symbol,Int64,true} with 2 entries:
  :b => 2
  :a => 1
julia> typeof(julia_dictionary)
PyDict{Symbol,Int64,true}
julia> julia_dictionary[:a]
1
# 產生一個 kw 參數的 function
julia> f(; a=0, b=0) = [10a, b]
f (generic function with 1 method)
# 以 dict 呼叫 function
julia> f(;julia_dictionary...)
2-element Array{Int64,1}:
 10
  2Expressions
這是 metaprogramming 的功能,這是參考 LISP 的功能
首先說明 julia 如何 interprets a code
julia> code = "println(\"hello world \")"
"println(\"hello world \")"
julia> expression = Meta.parse(code)
:(println("hello world "))
julia> typeof(expression)
Exprjulia> expression.args
2-element Array{Any,1}:
 :println
 "hello world "
julia> expression.head
:call
julia> dump(expression)
Expr
  head: Symbol call
  args: Array{Any}((2,))
    1: Symbol println
    2: String "hello world "產生 expression,用 eval 運算, :call 是 symbol,表示 head of the expression
julia> sample_expr = Expr(:call, +, 10, 20)
:((+)(10, 20))
julia> eval(sample_expr)
30
julia> sample_expr.args
3-element Array{Any,1}:
   +
 10
 20將參數替換為 :x, :y 變數,也可以直接寫 x, y
julia> x = 10; y =10
10
julia> sample_expr = Expr(:call, +, :x, :y)
:((+)(x, y))
julia> sample_expr = Expr(:call, +, x, y)
:((+)(10, 10))
julia> eval(sample_expr)
20也可以使用 $
julia> x = 10; y=10
10
julia> e = :($x + $y)
:(10 + 10)
julia> eval(e)
20所以有 : 跟 $ 兩種方式可產生 Expr object
- Using quotes(:) at runtime
- Using dollar($) ar parse time
另外還有一個方式,是使用 quote 這個 keyword。quote 跟 : 的差別,是用 quote 可讓程式碼排列更好,比較容易實作
julia> quote
          30 * 100
       end
quote
    #= REPL[88]:2 =#
    30 * 100
end
julia> eval(ans)
3000
julia> :(30 * 100)
:(30 * 100)
julia> eval(ans)
3000Macros
類似 function,但 function 使用 variables 為參數,而 macros 使用 expressions,並回傳 modified expressions,呼叫 macro 是用 @
macro NAME
    # some custom code
    # return modified expression
end因為 REPL 建立的 macro 是在 Main module 裡面
julia> macro HELLO(name)
         :( println("Hello! ", $name))
       end
@HELLO (macro with 1 method)
julia> @HELLO("world")
Hello! world
julia> macroexpand(Main, :(@HELLO("world")))
:((Main.println)("Hello! ", "world"))why metaprogramming?
metaprogramming 可省略很多重複的程式碼
for sym in [:foo, :bar, :baz]
    @eval function $(Symbol(sym))(n::Int64)
        for i in 1:n
            println( $(string(sym)) )
        end
    end
end可這樣使用
foo(1)
bar(2)
baz(3)for header in [:h1, :h2, :h3, :h4, :h5, :h6]
     @eval function $(Symbol(header))(text::String)
         println("<" * $(string(header))* ">"  * " $text " * "</" * $(string(header))* ">")
     end
 end
h1("Hello world!")
# <h1> Hello world! </h1>
h3("Hello world!")
# <h3> Hello world! </h3>Built-in macros
以下是所有內建的 macros
julia> @
@MIME_str      @boundscheck    @edit           @html_str       @nospecialize   @text_str
@__DIR__       @cfunction      @elapsed        @inbounds       @polly          @threadcall
@__FILE__      @cmd            @enum           @info           @r_str          @time
@__LINE__      @code_llvm      @error          @inline         @raw_str        @timed
@__MODULE__    @code_lowered   @eval           @int128_str     @s_str          @timev
@__dot__       @code_native    @evalpoly       @isdefined      @show           @uint128_str
@allocated     @code_typed     @fastmath       @label          @simd           @v_str
@assert        @code_warntype  @functionloc    @less           @specialize     @view
@async         @debug          @generated      @macroexpand    @static         @views
@b_str         @deprecate      @gensym         @macroexpand1   @sync           @warn
@big_str       @doc            @goto           @noinline       @task           @which- @time 
 - 可取得執行某一段程式的耗費時間 
 - julia> function recursive_sum(n) if n == 0 return 0 else return n + recursive_sum(n-1) end end recursive_sum (generic function with 1 method) julia> @time recursive_sum(10000) 0.005359 seconds (3.22 k allocations: 183.158 KiB) 50005000
- @elapsed 
 - 類似 @time,但只有回傳時間 in Float64 
 - julia> @elapsed recursive_sum(10000) 3.0984e-5 julia> @elapsed recursive_sum(10000) 2.4597e-5
- @show 
 - 會回傳 expression 
 - julia> @show(println("hello world")) hello world println("hello world") = nothing julia> @show(:(println("hello world"))) $(Expr(:quote, :(println("hello world")))) = :(println("hello world")) :(println("hello world")) julia> @show(:(3*2)) $(Expr(:quote, :(3 * 2))) = :(3 * 2) :(3 * 2) julia> @show(3*2) 3 * 2 = 6 6 julia> @show(Int64) Int64 = Int64 Int64
- @which 
 - 如果有某個 function 有多個 methods,也就是 multiple dispatch 的功能,想知道會呼叫哪一個 method 
 - julia> function tripple(n::Int64) 3n end tripple (generic function with 1 method) julia> function tripple(n::Float64) 3n end tripple (generic function with 2 methods) julia> methods(tripple) # 2 methods for generic function "tripple": [1] tripple(n::Float64) in Main at REPL[11]:2 [2] tripple(n::Int64) in Main at REPL[10]:2 julia> @which tripple(10) tripple(n::Int64) in Main at REPL[10]:2 julia> @which tripple(10.0) tripple(n::Float64) in Main at REPL[11]:2
- @task 
 - 類似 coroutine,用來產生一個 task,而不是直接執行 
 - julia> say_hello() = println("hello world") say_hello (generic function with 1 method) julia> say_hello_task = @task say_hello() Task (runnable) @0x0000000113ed9210 julia> istaskstarted(say_hello_task) false julia> schedule(say_hello_task) hello world Task (queued) @0x0000000113ed9210 julia> yield() julia> istaskdone(say_hello_task) true
- @codellvm, @codelowered, @codetyped,@codenative, and @code_warntype 
 - 瞭解 code 在 julia 中的所有形式 
 - function fibonacci(n::Int64) if n < 2 n else fibonacci(n-1) + fibonacci(n-2) end end fibonacci(n::Int64) = n < 2 ? n : fibonacci(n-1) + fibonacci(n-2) # 轉換為 single static assignment,每個變數只會被 assigned 一次,在使用變數前都會先定義 @code_lowered fibonacci(10) # @code_typed fibonacci(10) @code_warntype fibonacci(10) # 使用 LLVM C++ API 產生 LLVM intermediate representation @code_llvm fibonacci(10) # binary code in memory @code_native fibonacci(10)
Type introspection
首先定義新的 type: Student,產生兩個物件
struct Student
   name::String
   age::Int64
end
alpha = Student("alpha",24)
beta = Student("beta",25)可檢查物件的資料型別,或是使用 isa 判斷是否為某個 function 產生的 type
julia> typeof(alpha)
Student
julia> isa(alpha, Student)
true
julia> alpha isa Student
truereflection
可在 runtime 查詢物件的 attributes。在 julia 產生 function 後,就可以查詢 function 有幾個參數,有哪些 methods。
function calculate_quad(a::Int64,b::Int64,c::Int64,x::Int64)
   return a*x^2 + b*x + c
end
calculate_quad(1,2,3,4)
function calculate_quad(a::Int64,b::Int64,c::Int64,x::Float64)
   return a*x^2 + b*x + c
end
calculate_quad(1,2,3,4.75)
julia> methods(calculate_quad)
# 2 methods for generic function "calculate_quad":
[1] calculate_quad(a::Int64, b::Int64, c::Int64, x::Float64) in Main at REPL[38]:2
[2] calculate_quad(a::Int64, b::Int64, c::Int64, x::Int64) in Main at REPL[36]:2julia> fieldnames(Student)
(:name, :age)
julia> Student.types
svec(String, Int64)
julia> typeof(Student.types)
Core.SimpleVector 
沒有留言:
張貼留言