99re热视频这里只精品,久久久天堂国产精品女人,国产av一区二区三区,久久久精品成人免费看片,99久久精品免费看国产一区二区三区

Julia 控制流

2022-02-25 16:47 更新

Julia 提供一系列控制流:

前五個控制流機制是高級編程語言的標準。但任務不是:它提供了非本地的控制流,便于在臨時暫停的計算中進行切換。在 Julia 中,異常處理和協(xié)同多任務都是使用的這個機制。

復合表達式

用一個表達式按照順序?qū)σ幌盗凶颖磉_式求值,并返回最后一個子表達式的值,有兩種方法:begin 塊和 (;) 鏈。 begin 塊的例子:

julia> z = begin
         x = 1
         y = 2
         x + y
       end
3

這個塊很短也很簡單,可以用 (;) 鏈語法將其放在一行上:

julia> z = (x = 1; y = 2; x + y)
3

這個語法在函數(shù)中的單行函數(shù)定義非常有用。 begin 塊也可以寫成單行, (;) 鏈也可以寫成多行:

julia> begin x = 1; y = 2; x + y end
3

julia> (x = 1;
        y = 2;
        x + y)
3

條件求值

一個 if-elseif-else 條件表達式的例子:

if x < y
  println("x is less than y")
elseif x > y
  println("x is greater than y")
else
  println("x is equal to y")
end

如果條件表達式 x < y 為真,相應的語句塊將會被執(zhí)行;否則就執(zhí)行條件表達式 x > y ,如果結(jié)果為真,相應的語句塊將被執(zhí)行;如果兩個表達式都是假,else 語句塊將被執(zhí)行。這是它用在實際中的例子:

julia> function test(x, y)
         if x < y
           println("x is less than y")
         elseif x > y
           println("x is greater than y")
         else
           println("x is equal to y")
         end
       end
test (generic function with 1 method)

julia> test(1, 2)
x is less than y

julia> test(2, 1)
x is greater than y

julia> test(1, 1)
x is equal to y

elseifelse 塊是可選的。

請注意,非常短的條件語句(一行)在 Julia 中是會經(jīng)常使用短的電路評估(Short-Circuit Evaluation)實現(xiàn)的,具體細節(jié)在下一節(jié)中進行概述。

如果條件表達式的值是除 truefalse 之外的值,會出錯:

julia> if 1
         println("true")
       end
ERROR: type: non-boolean (Int64) used in boolean context

“問號表達式”語法 ?:if-elseif-else 語法相關(guān),但是適用于單個表達式:

a ? b : c

? 之前的 a 是條件表達式,如果為 true ,就執(zhí)行 : 之前的 b 表達式,如果為 false ,就執(zhí)行 :c 表達式。

用問號表達式來重寫,可以使前面的例子更加緊湊。先看一個二選一的例子:

julia> x = 1; y = 2;

julia> println(x < y ? "less than" : "not less than")
less than

julia> x = 1; y = 0;

julia> println(x < y ? "less than" : "not less than")
not less than

三選一的例子需要鏈式調(diào)用問號表達式:

julia> test(x, y) = println(x < y ? "x is less than y"    :
                            x > y ? "x is greater than y" : "x is equal to y")
test (generic function with 1 method)

julia> test(1, 2)
x is less than y

julia> test(2, 1)
x is greater than y

julia> test(1, 1)
x is equal to y

鏈式問號表達式的結(jié)合規(guī)則是從右到左。

if-elseif-else 類似,: 前后的表達式,只有在對應條件表達式為 truefalse 時才執(zhí)行:

julia> v(x) = (println(x); x)
v (generic function with 1 method)

julia> 1 < 2 ? v("yes") : v("no")
yes
"yes"

julia> 1 > 2 ? v("yes") : v("no")
no
"no"

短路求值

&&|| 布爾運算符被稱為短路求值,它們連接一系列布爾表達式,僅計算最少的表達式來確定整個鏈的布爾值。這意味著: 在表達式 a && b 中,只有 atrue 時才計算子表達式 b 在表達式 a || b 中,只有 afalse 時才計算子表達式 b &&|| 都與右側(cè)結(jié)合,但 &&|| 優(yōu)先級高:

julia> t(x) = (println(x); true)
t (generic function with 1 method)

julia> f(x) = (println(x); false)
f (generic function with 1 method)

julia> t(1) && t(2)
1
2
true

julia> t(1) && f(2)
1
2
false

julia> f(1) && t(2)
1
false

julia> f(1) && f(2)
1
false

julia> t(1) || t(2)
1
true

julia> t(1) || f(2)
1
true

julia> f(1) || t(2)
1
2
true

julia> f(1) || f(2)
1
2
false

這種方式在 Julia 里經(jīng)常作為 if 語句的一個簡潔的替代。 可以把 if <cond> <statement> end 寫成 <cond> && <statement> (讀作 <cond> *從而* <statement>)。 類似地, 可以把 if ! <cond> <statement> end 寫成 <cond> || <statement> (讀作 要不就 )。

例如, 遞歸階乘可以這樣寫:

julia> function factorial(n::Int)
           n >= 0 || error("n must be non-negative")
           n == 0 && return 1
           n * factorial(n-1)
       end
factorial (generic function with 1 method)

julia> factorial(5)
120

julia> factorial(0)
1

julia> factorial(-1)
ERROR: n must be non-negative
 in factorial at none:2

短路求值運算符,可以使用數(shù)學運算和基本函數(shù)中介紹的位布爾運算符 &|

julia> f(1) & t(2)
1
2
false

julia> t(1) | t(2)
1
2
true

&&|| 的運算對象也必須是布爾值( truefalse )。在任何地方使用一個非布爾值,除非最后一個進入連鎖條件的是一個錯誤:

julia> 1 && true
ERROR: type: non-boolean (Int64) used in boolean context

另一方面,任何類型的表達式可以使用在一個條件鏈的末端。根據(jù)前面的條件,它將被評估和返回:

julia> true && (x = rand(2,2))
2x2 Array{Float64,2}:
 0.768448  0.673959
 0.940515  0.395453

julia> false && (x = rand(2,2))
false

重復求值: 循環(huán)

有兩種循環(huán)表達式: while 循環(huán)和 for 循環(huán)。下面是 while 的例子:

julia> i = 1;

julia> while i <= 5
         println(i)
         i += 1
       end
1
2
3
4
5

上例也可以重寫為 for 循環(huán):

julia> for i = 1:5
         println(i)
       end
1
2
3
4
5

此處的 1:5 是一個 Range 對象,表示的是 1, 2, 3, 4, 5 序列。 for 循環(huán)遍歷這些數(shù),將其逐一賦給變量 i 。 while 循環(huán)和 for 循環(huán)的另一區(qū)別是變量的作用域。如果在其它作用域中沒有引入變量 i ,那么它僅存在于 for 循環(huán)中。不難驗證:

julia> for j = 1:5
         println(j)
       end
1
2
3
4
5

julia> j
ERROR: j not defined

有關(guān)變量作用域,詳見變量的作用域 。

通常,for 循環(huán)可以遍歷任意容器。這時,應使用另一個(但是完全等價的)關(guān)鍵詞 in ,而不是 = ,它使得代碼更易閱讀:

julia> for i in [1,4,0]
         println(i)
       end
1
4
0

julia> for s in ["foo","bar","baz"]
         println(s)
       end
foo
bar
baz

手冊中將介紹各種可迭代容器(詳見多維數(shù)組)。

有時要提前終止 whilefor 循環(huán)。可以通過關(guān)鍵詞 break 來實現(xiàn):

julia> i = 1;

julia> while true
         println(i)
         if i >= 5
           break
         end
         i += 1
       end
1
2
3
4
5

julia> for i = 1:1000
         println(i)
         if i >= 5
           break
         end
       end
1
2
3
4
5

有時需要中斷本次循環(huán),進行下一次循環(huán),這時可以用關(guān)鍵字 continue

julia> for i = 1:10
         if i % 3 != 0
           continue
         end
         println(i)
       end
3
6
9

多層 for 循環(huán)可以被重寫為一個外層循環(huán),迭代類似于笛卡爾乘積的形式:

julia> for i = 1:2, j = 3:4
         println((i, j))
       end
(1,3)
(1,4)
(2,3)
(2,4)

這種情況下用 break 可以直接跳出所有循環(huán)。

異常處理

當遇到意外條件時,函數(shù)可能無法給調(diào)用者返回一個合理值。這時,要么終止程序,打印診斷錯誤信息;要么程序員編寫異常處理。

內(nèi)置異常 Exception

如果程序遇到意外條件,異常將會被拋出。表中列出內(nèi)置異常。

Exception
ArgumentError
BoundsError
DivideError
DomainError
EOFError
ErrorException
InexactError
InterruptException
KeyError
LoadError
MemoryError
MethodError
OverflowError
ParseError
SystemError
TypeError
UndefRefError
UndefVarError

例如,當對負實數(shù)使用內(nèi)置的 sqrt 函數(shù)時,將拋出 DomainError()

julia> sqrt(-1)
ERROR: DomainError
sqrt will only return a complex result if called with a complex argument.
try sqrt(complex(x))
 in sqrt at math.jl:131

你可以使用下列方式定義你自己的異常:

julia> type MyCustomException <: Exception end

throw 函數(shù)

可以使用 throw 函數(shù)顯式創(chuàng)建異常。例如,某個函數(shù)只對非負數(shù)做了定義,如果參數(shù)為負數(shù),可以拋出 DomaineError 異常:

julia> f(x) = x>=0 ? exp(-x) : throw(DomainError())
f (generic function with 1 method)

julia> f(1)
0.36787944117144233

julia> f(-1)
ERROR: DomainError
 in f at none:1

注意,DomainError 使用時需要使用帶括號的形式,否則返回的并不是異常,而是異常的類型。必須帶括號才能返回 Exception 對象:

julia> typeof(DomainError()) <: Exception
true

julia> typeof(DomainError) <: Exception
false

另外,一些異常類型使用一個或更多個參數(shù)用來報告錯誤:

julia> throw(UndefVarError(:x))
ERROR: x not defined

這個機制能被簡單實現(xiàn),通過按照下列所示的 UndefVarError 方法自定義異常類型:

julia> type MyUndefVarError <: Exception
           var::Symbol
       end
julia> Base.showerror(io::IO, e::MyUndefVarError) = print(io, e.var, " not defined");

error 函數(shù)

error 函數(shù)用來產(chǎn)生 ErrorException ,阻斷程序的正常執(zhí)行。

如下改寫 sqrt 函數(shù),當參數(shù)為負數(shù)時,提示錯誤,立即停止執(zhí)行:

julia> fussy_sqrt(x) = x >= 0 ? sqrt(x) : error("negative x not allowed")
fussy_sqrt (generic function with 1 method)

julia> fussy_sqrt(2)
1.4142135623730951

julia> fussy_sqrt(-1)
ERROR: negative x not allowed
 in fussy_sqrt at none:1

當對負數(shù)調(diào)用 fussy_sqrt 時,它會立即返回,顯示錯誤信息:

julia> function verbose_fussy_sqrt(x)
         println("before fussy_sqrt")
         r = fussy_sqrt(x)
         println("after fussy_sqrt")
         return r
       end
verbose_fussy_sqrt (generic function with 1 method)

julia> verbose_fussy_sqrt(2)
before fussy_sqrt
after fussy_sqrt
1.4142135623730951

julia> verbose_fussy_sqrt(-1)
before fussy_sqrt
ERROR: negative x not allowed
 in verbose_fussy_sqrt at none:3

warninfo 函數(shù)

Julia 還提供一些函數(shù),用來向標準錯誤 I/O 輸出一些消息,但不拋出異常,因而并不會打斷程序的執(zhí)行:

julia> info("Hi"); 1+1
INFO: Hi
2

julia> warn("Hi"); 1+1
WARNING: Hi
2

julia> error("Hi"); 1+1
ERROR: Hi
 in error at error.jl:21

try/catch 語句

try/catch 語句可以用于處理一部分預料中的異常 Exception 。例如,下面求平方根函數(shù)可以正確處理實數(shù)或者復數(shù):

julia> f(x) = try
         sqrt(x)
       catch
         sqrt(complex(x, 0))
       end
f (generic function with 1 method)

julia> f(1)
1.0

julia> f(-1)
0.0 + 1.0im

但是處理異常比正常采用分支來處理,會慢得多。

try/catch 語句使用時也可以把異常賦值給某個變量。例如:

julia> sqrt_second(x) = try
         sqrt(x[2])
       catch y
         if isa(y, DomainError)
           sqrt(complex(x[2], 0))
         elseif isa(y, BoundsError)
           sqrt(x)
         end
       end
sqrt_second (generic function with 1 method)

julia> sqrt_second([1 4])
2.0

julia> sqrt_second([1 -4])
0.0 + 2.0im

julia> sqrt_second(9)
3.0

julia> sqrt_second(-9)
ERROR: DomainError
 in sqrt_second at none:7

注意,跟在 catch 之后的符號會被解釋為一個異常的名稱,因此,需要注意的是,在單行中寫 try/catch 表達式時。下面的代碼將不會正常工作返回 x 的值為了防止發(fā)生錯誤:

try bad() catch x end

我們在 catch 后使用分號或插入換行來實現(xiàn):

try bad() catch; x end

try bad()
catch
  x
end

Julia 還提供了更高級的異常處理函數(shù) rethrow , backtracecatch_backtrace 。

finally 語句

在改變狀態(tài)或者使用文件等資源時,通常需要在操作執(zhí)行完成時做清理工作(比如關(guān)閉文件)。異常的存在使得這樣的任務變得復雜,因為異常會導致程序提前退出。關(guān)鍵字 finally 可以解決這樣的問題,無論程序是怎樣退出的,finally 語句總是會被執(zhí)行。

例如, 下面的程序說明了怎樣保證打開的文件總是會被關(guān)閉:

f = open("file")
try
    # operate on file f
finally
    close(f)
end

當程序執(zhí)行完 try 語句塊(例如因為執(zhí)行到 return 語句,或者只是正常完成),close 語句將會被執(zhí)行。如果 try 語句塊因為異常提前退出,異常將會繼續(xù)傳播。catch 語句可以和 tryfinally 一起使用。這時。finally 語句將會在 catch 處理完異常之后執(zhí)行。

任務(也稱為協(xié)程)

任務是一種允許計算靈活地掛起和恢復的控制流,有時也被稱為對稱協(xié)程、輕量級線程、協(xié)同多任務等。

如果一個計算(比如運行一個函數(shù))被設計為 Task,有可能因為切換到其它 Task 而被中斷。原先的 Task 在以后恢復時,會從原先中斷的地方繼續(xù)工作。切換任務不需要任何空間,同時可以有任意數(shù)量的任務切換,而不需要考慮堆棧問題。任務切換與函數(shù)調(diào)用不同,可以按照任何順序來進行。

任務比較適合生產(chǎn)者-消費者模式,一個過程用來生產(chǎn)值,另一個用來消費值。消費者不能簡單的調(diào)用生產(chǎn)者來得到值,因為兩者的執(zhí)行時間不一定協(xié)同。在任務中,兩者則可以正常運行。

Julia 提供了 produceconsume 函數(shù)來解決這個問題。生產(chǎn)者調(diào)用 produce 函數(shù)來生產(chǎn)值:

julia> function producer()
         produce("start")
         for n=1:4
           produce(2n)
         end
         produce("stop")
       end;

要消費生產(chǎn)的值,先對生產(chǎn)者調(diào)用 Task 函數(shù),然后對返回的對象重復調(diào)用 consume

julia> p = Task(producer);

julia> consume(p)
"start"

julia> consume(p)
2

julia> consume(p)
4

julia> consume(p)
6

julia> consume(p)
8

julia> consume(p)
"stop"

可以在 for 循環(huán)中迭代任務,生產(chǎn)的值被賦值給循環(huán)變量:

julia> for x in Task(producer)
         println(x)
       end
start
2
4
6
8
stop

注意 Task() 函數(shù)的參數(shù),應為零參函數(shù)。生產(chǎn)者常常是參數(shù)化的,因此需要為其構(gòu)造零參匿名函數(shù) 。可以直接寫,也可以調(diào)用宏:

function mytask(myarg)
    ...
end

taskHdl = Task(() -> mytask(7))
# 也可以寫成
taskHdl = @task mytask(7)

produceconsume 但它并不在不同的 CPU 發(fā)起線程。我們將在并行計算中,討論真正的內(nèi)核線程。

核心任務操作

盡管 produceconsume 已經(jīng)闡釋了任務的本質(zhì),但是他們實際上是由庫函數(shù)調(diào)用更原始的函數(shù) yieldto 實現(xiàn)的。 yieldto(task,value) 掛起當前任務,切換到特定的 task , 并使這個 task 的最后一次 yeidlto 返回 \特定的 value。注意 yieldto 是唯一需要的操作來進行 ‘任務風格’的控制流;不需要調(diào)用和返回,我們只用在不同的任務之間切換即可。 這就是為什么這個特性被稱做 “對稱式協(xié)程”;每一個任務的切換都是用相同的機制。

yeildto 很強大, 但是大多數(shù)時候并不直接調(diào)用它。 當你從當前的任務切換走,你有可能會想切換回來, 但需要知道切換的時機和任務,這會需要相當?shù)膮f(xié)調(diào)。 例如,procude 需要保持某個狀態(tài)來記錄消費者。無需手動地記錄正在消費的任務讓 produceyieldto 更容易使用。

除此之外,為了高效地使用任務,其他一些基本的函數(shù)也同樣必須。current_task() 獲得當前運行任務的引用。istaskdone(t) 查詢?nèi)蝿帐欠窠K止。istaskstarted(t) 查詢?nèi)蝿帐欠駟印?code>task_local_storage 處理當前任務的鍵值儲存。

任務與事件

大多數(shù)任務的切換都是在等待像 I/O 請求這樣的事件的時候,并由標準庫的調(diào)度器完成。調(diào)度器記錄正在運行的任務的隊列,并執(zhí)行一個循環(huán)來根據(jù)外部事件(比如消息到達)重啟任務。

處理等待事件的基本函數(shù)是 wait。 有幾種對象實現(xiàn)了 wait,比如對于 Process 對象, wait 會等待它終止。更多的時候 wait 是隱式的,比如 wait 可以發(fā)生在調(diào)用 read 的時候,等待數(shù)據(jù)變得可用。

在所有的情況中, wait 最終會操作在一個負責將任務排隊和重啟的 Condition 對象上。當任務在 Condition 上調(diào)用 wait, 任務會被標記為不可運行,被加入到 Condition 的 隊列中,再切換至調(diào)度器。調(diào)度器會選取另一個任務來運行,或者等待外部事件。如果一切正常,最終一個事件句柄會在 Condition 上調(diào)用 notify,使正在等待的任務變得可以運行。

調(diào)用 Task 可以生成一個初始對調(diào)度器還未知的任務,這允許你用 yieldto 手動管理任務。不管怎樣,當這樣的任務正在等待事件時,事件一旦發(fā)生,它仍然會自動重啟。而且任何時候你都可以調(diào)用 schedule(task) 或者用宏 @schedule@async 來讓調(diào)度器來運行一個任務,根本不用去等待任何事件。(參見并行計算)

任務狀態(tài)

任務包含一個 state 域,它用來描述任務的執(zhí)行狀態(tài)。任務狀態(tài)取如下的幾種符號中的一種:

符號 意義
:runnable 任務正在運行,或可被切換到該任務
:waiting 等待一個特定事件從而阻塞
:queued 在調(diào)度程序的運行隊列中準備重新啟動
:done 成功執(zhí)行完畢
:failed 由于未處理的異常而終止


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號