オブジェクトの結合
CoddyのLuaジャーニー「Object Oriented Programming」セクションの一部 — レッスン 28/70。
Luaにおける..演算子は文字列を連結しますが、オブジェクトと文字列を組み合わせたい場合はどうすればよいでしょうか?__concatメタメソッドを使用すると、これがどのように動作すべきかを正確に定義できます。
Luaが a .. b を処理する際、一方のオペランドに __concat メタメソッドがある場合、Luaはその関数を両方の値を引数として呼び出します。これは、オブジェクトを文字列式に自然に馴染ませたい場合に便利です。
local Name = {}
Name.__index = Name
function Name:new(first, last)
local obj = {first = first, last = last}
setmetatable(obj, Name)
return obj
end
function Name.__concat(a, b)
if type(a) == "string" then
return a .. b.first .. " " .. b.last
else
return a.first .. " " .. a.last .. b
end
end関数がどちらのオペランドが文字列であるかを確認していることに注意してください。.. は "Hello " .. obj または obj .. "!" として使用できるため、メタメソッドは両方のケースを処理する必要があります。
local person = Name:new("Ada", "Lovelace")
print("Dr. " .. person) -- 出力: Dr. Ada Lovelace
print(person .. " PhD") -- 出力: Ada Lovelace PhD他の演算子メタメソッドと同様に、__concat はドット構文を使用し、両方のオペランドを引数として受け取ります。算術メタメソッドとの主な違いは、通常、オブジェクトと文字列という混合型(自身のオブジェクトと文字列)を扱う点です。
チャレンジ
簡単.. 演算子を使用して、文字列式の中に自然に溶け込むことができる Title クラスを作成しましょう。これは、敬称や接尾辞を個人の肩書きに加える場合など、オブジェクトと文字列を読みやすく直感的な方法で組み合わせたい場合に便利です。
コードは2つのファイルに分けて構成します:
Title.lua: タイトル文字列を格納するtextフィールドを持つ Title クラスを定義します。:new(text)コンストラクタを含め、__concatメタメソッドを実装してください。メタメソッドは、文字列がオブジェクトの前に来る場合("prefix" .. obj)と、文字列がオブジェクトの後に来る場合(obj .. "suffix")の両方のケースを処理する必要があります。結果として、文字列とタイトルのテキストを適切に結合させる必要があります。main.lua: Title モジュールをロードし、タイトルのインスタンスを作成して、両方向からの結合を実演します。
3つの入力を受け取ります:
- タイトルのテキスト(例: "Engineer")
- タイトルの前に置く接頭辞文字列(例: "Senior ")
- タイトルの後に置く接尾辞文字列(例: " III")
メインファイルで、与えられたテキストを使用して Title を作成します。次に、2つの結合を実行し、それぞれの結果を個別の行に出力してください:
- 接頭辞文字列とタイトルオブジェクトを結合する
- タイトルオブジェクトと接尾辞文字列を結合する
例えば、入力が Engineer、Senior 、 III の場合、出力は以下のようになります:
Senior Engineer
Engineer III最初の行は接頭辞とタイトルを組み合わせたものを、2行目はタイトルと接尾辞を組み合わせたものを表示しています。__concat メタメソッドによって、このような自然な文字列の融合が可能になります!
チートシート
__concat メタメソッドを使用すると、オブジェクトを扱う際の連結演算子 .. のカスタム動作を定義できます。
Lua が a .. b を検出し、いずれかのオペランドに __concat メタメソッドがある場合、両方の値を引数としてその関数を呼び出します。
基本的な実装
local MyClass = {}
MyClass.__index = MyClass
function MyClass:new(value)
local obj = {value = value}
setmetatable(obj, MyClass)
return obj
end
function MyClass.__concat(a, b)
if type(a) == "string" then
return a .. b.value
else
return a.value .. b
end
end両方のオペランド順序の処理
.. は "string" .. obj または obj .. "string" のように使用される可能性があるため、メタメソッドはどちらのオペランドが文字列であるかを確認し、両方のケースを処理する必要があります。
function Name.__concat(a, b)
if type(a) == "string" then
return a .. b.first .. " " .. b.last
else
return a.first .. " " .. a.last .. b
end
end使用例
local person = Name:new("Ada", "Lovelace")
print("Dr. " .. person) -- 出力: Dr. Ada Lovelace
print(person .. " PhD") -- 出力: Ada Lovelace PhD他の演算子メタメソッドと同様に、__concat はドット構文を使用し、両方のオペランドを引数として受け取ります。
自分で試してみよう
-- 入力を読み込む
local titleText = io.read()
local prefix = io.read()
local suffix = io.read()
-- Titleモジュールを読み込む
local Title = require('Title')
-- TODO: 与えられた titleText を使用して Title インスタンスを作成する
-- TODO: prefix と title オブジェクトを連結し、結果を表示する
-- TODO: title オブジェクトと suffix を連結し、結果を表示する
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。