學習軌道
/
Julia
Julia
/
練習
/
環狀緩衝區
環狀緩衝區

環狀緩衝區

困難

說明

環狀緩衝區、循環緩衝區或環形緩衝區是一種資料結構,它使用單一固定大小的緩衝區,就好像頭尾相連一樣。

環狀緩衝區一開始是空的,並有某個預先定義好的長度。 例如,這是一個有 7 個元素的緩衝區:

[ ][ ][ ][ ][ ][ ][ ]

假設把 1 寫入緩衝區的中間(在環狀緩衝區裡,實際的起始位置並不重要):

[ ][ ][ ][1][ ][ ][ ]

接著假設再加入兩個元素,也就是 2 和 3,它們會被附加在 1 之後:

[ ][ ][ ][1][2][3][ ]

如果接著從緩衝區移除兩個元素,被移除的會是緩衝區裡最舊的值。 在這個例子裡,被移除的兩個元素是 1 和 2,緩衝區裡只剩下一個 3:

[ ][ ][ ][ ][ ][3][ ]

如果緩衝區裡有 7 個元素,那它就完全滿了:

[5][6][7][8][9][3][4]

當緩衝區滿了,就會引發錯誤,提醒用戶端在有空位之前,後續的寫入都會被阻擋。

當緩衝區滿了,用戶端可以選擇用強制寫入來覆寫最舊的資料。 在這個例子裡,會再加入兩個元素,也就是 A 和 B,它們會覆寫掉 3 和 4:

[5][6][7][8][9][A][B]

3 和 4 已被 A 和 B 取代,於是 5 現在成了緩衝區裡最舊的資料。 最後,如果移除兩個元素,回傳的會是 5 和 6,於是緩衝區變成:

[ ][ ][7][8][9][A][B]

因為還有可用的空間,如果用戶端再次使用覆寫來存放 C 和 D,那麼會使用先前存放 5 和 6 的空間,而不是 7 和 8 的位置。 7 仍然是最舊的元素,而緩衝區又再度滿了。

[C][D][7][8][9][A][B]

任務

定義一個參數化的複合型別CircularBuffer{T},用來存放型別為T的元素,並撰寫一個建構子

CircularBuffer{T}(capacity::Integer) where {T} -> CircularBuffer{T}

建立一個最多可存放capacity個元素的實例。

擴充Base中的下列函式,讓它們能作用於CircularBuffer:

  • Base.push!(cb::CircularBuffer, item; overwrite::Bool=false):將元素item插入cb的尾端,然後回傳cb。如果cb已經滿了,當overwrite為false(預設值)時,就拋出BoundsError;否則,當overwrite為true時,移除第一個元素,以騰出空間給item。
  • Base.popfirst!(cb::CircularBuffer):移除並回傳cb的第一個元素。
  • Base.empty!(cb::CircularBuffer):移除cb中的所有元素,然後回傳空的cb。

加分任務

這個練習相當龐大,也可能很複雜,這讓引導變得更有挑戰性,也更花時間。為了幫你的導師省點力,請等到導師審閱過你練習第一部分的解法之後,再提交加分練習的程式碼。

擴充你的CircularBuffer,讓它能通過 DataStructures.jl套件中CircularBuffer的測試。這些測試已包含在這個 Exercism 練習所提供的測試中,但預設為停用;若要啟用這些測試,請在檔案或筆記本中加入頂層的那一行enable_bonus_tests = true。

若要通過這些測試,你需要將CircularBuffer宣告為AbstractVector的子型別,並定義兩個函式:

  • capacity(cb::CircularBuffer):回傳cb的容量。
  • isfull(cb::CircularBuffer):若cb已滿則回傳true。

接著,你必須確保Base中下列函式能正確地搭配CircularBuffer運作:append!、empty!、pop!、pushfirst、setindex!、collect、eltype、first、getindex、isempty、iterate、last、length和size。

提示:你不需要,也不應該,擴充所有這些函式!只要將CircularBuffer定義為AbstractVector的子型別,原本為AbstractVector定義的泛型函式就會接受CircularBuffer作為輸入。請參閱 Julia 手冊中關於介面的章節:

Julia 的許多威力與可擴充性,都來自一系列的非正式介面。只要為某個自訂型別擴充幾個特定的方法,該型別的物件不僅能獲得這些功能,還能被用在其他以泛型方式建立於這些行為之上的方法中。

你將需要翻閱 Julia 的Base模組原始碼,查看函式定義,並判斷該擴充哪些函式。若要找出某個函式呼叫對應的相關程式碼,你可以使用@which巨集,找出該函式呼叫所分派到的特定方法。它也會顯示該方法定義所在的檔案與行號(在透過 IJulia 使用的 Jupyter Notebook 中,它甚至會提供一個連到 GitHub 上相關程式碼的連結)。

如果你在 REPL 中工作,或許會偏好使用@edit巨集,在你的預設文字編輯器中開啟相關的檔案與行號。


出處

Wikipedia連結會在新視窗或分頁中開啟
透過 GitHub 編輯 連結會在新視窗或分頁中開啟
Julia Exercism

準備好開始 環狀緩衝區 了嗎?

註冊 Exercism,透過 35 個概念128 個練習 和真人引導來學習並精通 Julia,全部免費。

深入探索 環狀緩衝區!

這部影片中,我們來看看環狀緩衝區是什麼、用在哪裡,以及各種不同的實作方式,包括佇列、靜態與動態陣列、不可變資料結構,以及一個有趣的代理程式實作。