Menu
CoddyTech

Mẫu Iterator

Thuộc phần Lập trình hướng đối tượng trong hành trình Lua của Coddy. Bài học 64 trên 70.

Khi lưu một tập hợp các mục bên trong một đối tượng, bạn thường muốn lặp qua chúng. Vòng lặp for tổng quát của Lua hoạt động với mọi hàm lặp: vòng lặp gọi hàm trước mỗi lần lặp và dừng khi hàm trả về nil.

Mẫu Iterator cung cấp cho đối tượng một phương thức trả về một hàm như vậy. Hàm giữ vị trí của mình trong một biến cục bộ, nên vị trí đó thuộc về một vòng lặp, chứ không phải tập hợp:

local Collection = {}
Collection.__index = Collection

function Collection:new()
    local obj = { items = {} }
    setmetatable(obj, Collection)
    return obj
end

function Collection:add(item)
    table.insert(self.items, item)
end

function Collection:iter()
    local index = 0
    return function()
        index = index + 1
        return self.items[index]
    end
end

Mỗi lần gọi bag:iter() sẽ tạo một hàm mới có index riêng, bắt đầu từ 0. Hàm này là một bao đóng: nó vẫn có thể dùng index và self sau khi iter trả về. Sau phần tử cuối cùng, self.items[index] là nil, và vòng lặp dừng lại:

local bag = Collection:new()
bag:add("apple")
bag:add("banana")

for item in bag:iter() do
    print(item)  -- apple, rồi đến banana
end

for item in bag:iter() do
    print(item)  -- lại là apple, rồi đến banana
end

Hãy giữ vị trí bên ngoài chính tập hợp. Nếu đối tượng lưu self.index và tự làm trình lặp, vòng lặp đầu tiên sẽ để chỉ số ở cuối, nên vòng lặp thứ hai sẽ không in ra gì. Nếu một vòng lặp dừng sớm bằng break, vòng lặp tiếp theo sẽ bắt đầu từ giữa.

challenge icon

Thử thách

Dễ

Hãy tạo một lớp Playlist để bạn có thể lặp qua bằng vòng lặp for tổng quát của Lua! Bạn sẽ triển khai mẫu Iterator bằng phương thức :iter(), phương thức này trả về một hàm lặp.

Bạn sẽ sắp xếp code trong hai tệp:

  • Playlist.lua: Tạo một lớp Playlist lưu trữ các bài hát. Lớp của bạn cần một hàm khởi tạo :new() để khởi tạo danh sách bài hát trống và một phương thức :addSong(title) để thêm bài hát vào danh sách. Quan trọng nhất, hãy thêm phương thức :iter() trả về một hàm mới mỗi khi được gọi. Hàm đó lưu vị trí của mình trong một biến cục bộ và trả về bài hát tiếp theo mỗi khi được gọi, hoặc nil khi không còn bài hát nào.
  • main.lua: Yêu cầu module Playlist và tạo một danh sách phát mới. Thêm bài hát vào danh sách dựa trên các đầu vào bạn nhận được, sau đó dùng vòng lặp for tổng quát với playlist:iter() để in tiêu đề mỗi bài hát trên một dòng riêng.

Vì mỗi lần gọi :iter() đều bắt đầu từ bài hát đầu tiên, bạn có thể lặp qua cùng một danh sách phát bao nhiêu lần tùy ý.

Bạn sẽ nhận được ba đầu vào, mỗi đầu vào là tiêu đề một bài hát cần thêm vào danh sách phát, theo thứ tự.

Ví dụ, nếu các đầu vào là Bohemian Rhapsody, Stairway to Heaven và Hotel California, đầu ra sẽ là:

Bohemian Rhapsody
Stairway to Heaven
Hotel California

Nếu các đầu vào là Yesterday, Imagine và Let It Be, đầu ra sẽ là:

Yesterday
Imagine
Let It Be

Tự làm thử

-- Nạp module Playlist bằng require
local Playlist = require('Playlist')

-- Đọc tên của ba bài hát từ đầu vào
local song1 = io.read()
local song2 = io.read()
local song3 = io.read()

-- TODO: Tạo một danh sách phát mới bằng Playlist:new()

-- TODO: Thêm ba bài hát vào danh sách phát bằng :addSong()

-- TODO: Dùng vòng lặp for tổng quát với playlist:iter()
-- và in tên mỗi bài hát trên một dòng riêng
-- Gợi ý: for song in playlist:iter() do ... end
quiz iconTự kiểm tra

Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.

Tất cả bài học trong Lập trình hướng đối tượng

Tự luyện tập: Trình biên dịch Lua online