オブジェクトの加算
CoddyのLuaジャーニー「Object Oriented Programming」セクションの一部 — レッスン 26/70。
__tostring や __eq のようなメタメソッドによって、オブジェクトの振る舞いをカスタマイズできることをすでに見てきました。ここでは、加算を皮切りに、これを算術演算へと拡張していきます。
__add メタメソッドは、2つのオブジェクト間で + 演算子を使用したときに何が起こるかを定義します。Luaが a + b を検出すると、いずれかのオペランドのメタテーブル内で __add を探します。
local Vector = {}
Vector.__index = Vector
function Vector:new(x, y)
local obj = {x = x, y = y}
setmetatable(obj, Vector)
return obj
end
function Vector.__add(v1, v2)
return Vector:new(v1.x + v2.x, v1.y + v2.y)
end__add はコロンではなくドットで定義されることに注意してください。これは両方のオペランドを引数として受け取り、元のいずれかを変更するのではなく、新しいオブジェクトを返す必要があります。これにより、操作の予測可能性と安全性が保たれます。
local a = Vector:new(3, 4)
local b = Vector:new(1, 2)
local c = a + b
print(c.x, c.y) -- 出力: 4 6重要なポイントは、__add がオブジェクト内だけでなく、メタテーブル自体(ここでは Vector)に含まれている必要があるということです。すべてのインスタンスに対して Vector をメタテーブルとして設定しているため、それらはすべて自動的にこの動作を共有します。
チャレンジ
簡単+ 演算子を使用して加算をサポートする Vector クラスを作成しましょう。これは演算子オーバーロードへの第一歩です。演算子オーバーロードを使用することで、オブジェクトを Lua の組み込み演算子と自然に連携させることができます。
コードは 2 つのファイルに分けて構成します:
Vector.lua:xとyコンポーネントを持つ Vector クラスを定義します。:new(x, y)コンストラクタを含め、2 つのベクトルを加算するとコンポーネントが合算された新しいベクトルが作成されるように__addメタメソッドを実装してください。__addは(コロン構文ではなく)ドット構文を使用し、両方のオペランドを引数として受け取ることに注意してください。main.lua: Vector モジュールをrequireし、ベクトルのインスタンスを作成して、それらを加算すると正しい結果が得られることを示します。
4 つの入力を受け取ります:
- 1 つ目のベクトルの x コンポーネント
- 1 つ目のベクトルの y コンポーネント
- 2 つ目のベクトルの x コンポーネント
- 2 つ目のベクトルの y コンポーネント
メインファイルで、与えられたコンポーネントを使用して 2 つのベクトルを作成し、+ 演算子を使ってそれらを加算し、結果のベクトルの x と y の値を別々の行に出力してください。
例えば、入力が 3, 4, 1, 2 の場合、出力は以下のようになります:
4
61 つ目のベクトル (3, 4) と 2 つ目のベクトル (1, 2) を足すと、新しいベクトル (4, 6) になります。
チートシート
__add メタメソッドは、2つのオブジェクト間で + 演算子を使用したときに何が起こるかを定義します。
Lua が a + b を見つけると、いずれかのオペランドのメタテーブルから __add を探します。
local Vector = {}
Vector.__index = Vector
function Vector:new(x, y)
local obj = {x = x, y = y}
setmetatable(obj, Vector)
return obj
end
function Vector.__add(v1, v2)
return Vector:new(v1.x + v2.x, v1.y + v2.y)
end__add はコロンではなく、ドットで定義されます。これは両方のオペランドを引数として受け取り、元のいずれかを変更するのではなく、新しいオブジェクトを返す必要があります。
local a = Vector:new(3, 4)
local b = Vector:new(1, 2)
local c = a + b
print(c.x, c.y) -- 出力: 4 6__add はオブジェクト内だけでなく、メタテーブル自体に含まれている必要があります。
自分で試してみよう
-- Vectorモジュールを読み込む
local Vector = require('Vector')
-- 入力を読み込む
local x1 = tonumber(io.read())
local y1 = tonumber(io.read())
local x2 = tonumber(io.read())
local y2 = tonumber(io.read())
-- TODO: 入力値を使用して2つのVectorインスタンスを作成する
-- TODO: +演算子を使用して2つのベクトルを加算する
-- TODO: 結果のベクトルのx値とy値を別々の行に出力する
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。