0Pricing
Lua Academy · レッスン

算術メタメソッド

メタメソッドで+、-、*、/などの演算子をオーバーロードします。

「算術メタメソッド」はCoddyKit上の無料Lua Academyレッスンです。 これはレッスン3/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはLua Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Lua Academyコースには全4レッスンが含まれています。

メタメソッド名

Luaは、すべての算術演算子に対してメタメソッドを定義しています。Luaがテーブルに対する算術演算を検出すると、テーブルのメタテーブルから対応するメタメソッドを探します。両方のオペランドがテーブルの場合は、まず左オペランドのメタテーブルを確認します。

-- Operator -> Metamethod
-- a + b    -> __add
-- a - b    -> __sub
-- a * b    -> __mul
-- a / b    -> __div
-- a % b    -> __mod
-- a ^ b    -> __pow
-- a // b   -> __idiv
-- -a       -> __unm (unary minus)
-- #a       -> __len
print("Metamethods defined for all Lua operators")

ベクトル加算のための__add

ベクトルテーブルに__addを定義すると、+を使えるようになります。メタメソッドは2つのオペランドを受け取り、新しい値を返します。両方のオペランドがテーブルの場合もあれば、一方が数値の場合もあるため、メタメソッドで両方のケースを処理してください。

local Vec = {}
Vec.__index = Vec

Vec.__add = function(a, b)
  return setmetatable({x=a.x+b.x, y=a.y+b.y}, Vec)
end

Vec.__tostring = function(v)
  return string.format("Vec(%g,%g)", v.x, v.y)
end

function Vec.new(x,y) return setmetatable({x=x,y=y},Vec) end

local v1 = Vec.new(1,2)
local v2 = Vec.new(3,4)
print(tostring(v1 + v2))   -- Vec(4,6)

__sub、__mul、__div

__sub、__mul、__divも同様に定義します。乗算では、メタメソッド内でオペランドの型を確認し、table*tableとtable*numberの両方を処理してください。

local Vec = {}
Vec.__index = Vec
local function V(x,y) return setmetatable({x=x,y=y},Vec) end
Vec.__sub = function(a,b) return V(a.x-b.x, a.y-b.y) end
Vec.__mul = function(a,b)
  if type(a)=="number" then return V(a*b.x, a*b.y)
  elseif type(b)=="number" then return V(a.x*b, a.y*b)
  else return a.x*b.x + a.y*b.y  -- dot product
  end
end
Vec.__tostring=function(v) return "("..v.x..","..v.y..")" end

local v = V(3,4)
print(tostring(v * 2))    -- (6,8)
print(tostring(V(1,0) - V(0,1)))  -- (1,-1)

__unm単項マイナス

__unmは単項マイナスのメタメソッドで、Luaが-objを評価したときに呼び出されます。オペランドを受け取り(整合性のため同じ値が2回渡されます)、符号を反転した値を返します。

local Vec = {}
Vec.__index = Vec
local function V(x,y) return setmetatable({x=x,y=y},Vec) end
Vec.__unm = function(a) return V(-a.x, -a.y) end
Vec.__tostring = function(v)
  return string.format("(%g,%g)", v.x, v.y)
end

local v = V(3, -4)
print(tostring(-v))    -- (-3,4)
print(tostring(-(-v))) -- (3,-4)

カスタム長のための__len

__lenは、rawな整数列がないテーブルに対して#objを評価したときに呼び出されます。集合、キュー、重み付きコンテナなどのカスタム型で意味のある「長さ」を返すように定義できます。

local Bag = {}
Bag.__index = Bag
Bag.__len = function(b) return b._count end

function Bag.new()
  return setmetatable({_count=0, _items={}}, Bag)
end
function Bag:add(item, qty)
  self._items[item] = (self._items[item] or 0) + (qty or 1)
  self._count = self._count + (qty or 1)
end

local bag = Bag.new()
bag:add("apple", 3)
bag:add("banana", 2)
print(#bag)   -- 5

__modと__pow

__modは%演算子を処理し、__powは^を処理します。これらはあまり一般的ではありませんが、大整数、分数、剰余演算オブジェクトなどのカスタム数値型に役立ちます。

local Modular = {}
Modular.__index = Modular
local M = 1000000007

local function mod(n)
  return setmetatable({n = n % M}, Modular)
end

Modular.__add = function(a,b) return mod(a.n + b.n) end
Modular.__mul = function(a,b) return mod(a.n * b.n) end
Modular.__pow = function(a,b)
  local r, base, exp = mod(1), a, b
  while exp > 0 do
    if exp%2==1 then r = r*base end
    base = base*base; exp = math.floor(exp/2)
  end
  return r
end

print((mod(2) ^ 30).n)   -- 73741817 (2^30 mod 1e9+7)

__concat

少なくとも一方のオペランドがこのメタメソッドを持っている場合、..演算子によって__concatが呼び出されます。これにより、カスタム型の連結を定義したり、流暢なDSLを作成したりできます。

local Builder = {}
Builder.__index = Builder
Builder.__concat = function(a, b)
  local result = {}
  for _, v in ipairs(a._parts) do result[#result+1] = v end
  if type(b) == "string" then result[#result+1] = b
  else for _, v in ipairs(b._parts) do result[#result+1] = v end end
  return setmetatable({_parts=result}, Builder)
end
Builder.build = function(b) return table.concat(b._parts) end

local function S(s) return setmetatable({_parts={s}},Builder) end
print((S("Hello") .. ", " .. S("World") .. "!"):build())

比較メタメソッド

__eqはテーブルに対する==を定義します(両方のオペランドが同じメタテーブルを持つ場合にのみ呼び出されます)。__ltは<を、__leは<=を定義します。注意:~=は__eqを使用し、>は引数を逆にした__ltを使用し、>=は__leを使用します。

local Vec = {}
Vec.__index = Vec
local function V(x,y) return setmetatable({x=x,y=y},Vec) end

Vec.__eq = function(a,b) return a.x==b.x and a.y==b.y end
Vec.__lt = function(a,b)
  return (a.x^2+a.y^2) < (b.x^2+b.y^2)  -- by magnitude
end

print(V(1,2) == V(1,2))  -- true
print(V(1,2) == V(1,3))  -- false
print(V(1,0) < V(2,0))   -- true (1 < 2)

メタメソッドの連鎖

メタメソッドは通常、新しいテーブルなどの値を返すため、演算を自然に連鎖させられます。各演算子が新しいオブジェクトを返し、次の演算子がその結果を使用します。これにより、カスタム型で流暢な算術式を記述できます。

local Vec = {}
Vec.__index = Vec
local function V(x,y) return setmetatable({x=x,y=y},Vec) end
Vec.__add = function(a,b) return V(a.x+b.x,a.y+b.y) end
Vec.__mul = function(a,b)
  if type(b)=="number" then return V(a.x*b,a.y*b) end
  return V(a.x*b.x+a.y*b.y, 0)   -- simplified
end
Vec.__tostring=function(v) return "("..v.x..","..v.y..")" end

local result = V(1,0) + V(0,1) * 3
print(tostring(result))   -- (1,3)

異なる型の演算

vec * 2と書く場合、左オペランドはテーブルです。2 * vecと書く場合、左オペランドは数値でメタメソッドを持たないため、Luaは右オペランドのメタテーブルから__mulを探します。メタメソッドでは両方の順序を処理してください。

local Vec = {}
Vec.__index = Vec
local function V(x,y) return setmetatable({x=x,y=y},Vec) end
Vec.__tostring = function(v) return "("..v.x..","..v.y..")" end

Vec.__mul = function(a, b)
  if type(a) == "number" then
    return V(a*b.x, a*b.y)   -- scalar * vec
  elseif type(b) == "number" then
    return V(a.x*b, a.y*b)   -- vec * scalar
  end
  return a.x*b.x + a.y*b.y  -- dot product
end

print(tostring(V(1,2) * 3))  -- (3,6)
print(tostring(3 * V(1,2)))  -- (3,6)

確認問題

-myTableによって呼び出されるメタメソッドはどれですか?

まとめ:算術メタメソッド

まとめ:

  • __add __sub __mul __div __mod __pow __idiv — 二項算術
  • __unm — 単項マイナス、__len — #演算子
  • __concat — ..演算子
  • __eq __lt __le — 比較演算子
  • メタメソッド内で異なる型(数値/テーブル)を処理します

よくある質問

「算術メタメソッド」レッスンは無料ですか?

はい。「算術メタメソッド」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Lua Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Lua Academyコースには全4レッスンが含まれています。

「算術メタメソッド」で何を学びますか?

メタメソッドで+、-、*、/などの演算子をオーバーロードします。 ブラウザで直接実行するハンズオンコードでLua Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Lua Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのLua Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/4です。

「算術メタメソッド」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このLua Academyレッスンでコードを書いて実行できますか?

はい。すべてのLua Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. メタテーブル入門
  2. __indexと__newindex
  3. 算術メタメソッド
  4. __tostringと__len
← Lua Academyに戻る