【Roblox】関数の定義と呼び出しにおける「.(ドット)」と「:(コロン)」の違い

Roblox

はじめに

この記事では Luau で関数を定義するときや呼び出すときに「.(ドット)」を使用する場合と「:(コロン)」を使用する場合の違いについて解説します。

公式ドキュメントでは以下のように説明されており、ドットを使用して定義したり、呼び出したりする関数は「関数」、コロンを使用して定義したり、呼び出したりする関数は「メソッド」と区別されていますが、この記事では少し複雑になってしまうので「メソッド」も「関数」と呼称します。

Methods are functions that are members of an object, such as a class or table. They expect the object itself (self) as the first argument. When you call a method, use the colon notation (:) instead of dot notation (.) to pass self as the first argument automatically.

Functions
  • この記事で解説するドットとコロンの違いや特徴は Luau 独自のものではなく、Lua 自体のものです。
  • この記事ではドットとコロンの違いや特徴について、テーブルで関数を定義した場合のみの解説を行っていますが、これらは Instance とその派生クラスで定義されている関数においても同じです。

ドットとコロンの違い

関数を定義するときの違い

まずは関数を定義するときの違いから解説します。

結論からいうとコロンを使用して定義した関数はそのテーブル自身を self として自動的に受け取り、参照することができます。
より厳密にいうと「function {テーブル名}:{関数名}() end」は「function {テーブル名}.{関数名}(self) end」のシンタックスシュガーです。

例えば以下のようなコードを記述したとします。

local sampleTable = {
    sampleKey = "Hello World",
}

function sampleTable.dotFunction()
    print(self.sampleKey)
end

function sampleTable:colonFunction()
    print(self.sampleKey)
end

このコードではまず、sampleKey という名前の Key と「Hello World」という string 型の Value を持つ sampleTable というテーブルを定義しています。
その後、ドットを使用して dotFunction() という関数を、コロンを使用して colonFunction() という関数を sampleTable で定義しています。
このコードの dotFunction() と colonFunction() はどちらも self.sampleKey の Value をログに出力しようとしています。
この self というのは sampleTable 自身のことです。

しかし、このコードでは dotFunction() でのみ「Type Error: Unknown global ‘self’; consider assigning to it first」というエラーが発生します。
つまり、self が何者なのかということを colonFunction() は知っていますが、dotFunction() は知らないという状態になっています。

先ほどのコードではドットを使用して定義した dotFunction() に self という名前の引数を追加してあげるとエラーが発生しなくなりますが、dotFunction() の呼び出し側がそのテーブルを明示的に渡してあげる必要があります。

-- 定義
function sampleTable.dotFunction(self)
    print(self.sampleKey)
end

-- 呼び出し
sampleTable.dotFunction(sampleTable)

関数を呼び出すときの違い

次は関数を呼び出すときの違いについて解説します。

こちらも結論からいうとコロンを使用して関数を呼び出すと、その関数の第一引数にそのテーブル自身を渡すことができます。
こちらもより厳密にいうと「{テーブル名}:{関数名}()」は「{テーブル名}.{関数名}({テーブル名})」のシンタックスシュガーです。

ドットを使用して定義した関数を呼び出すときの違い

例えば先ほどのコードのようにして sampleTable と dotFunction() を定義します。
dotFunction() はドットを使用して定義されており、self 引数が sampleTable であることを期待しています。

local sampleTable = {
    sampleKey = "Hello World",
}

function sampleTable.dotFunction(self)
    print(self.sampleKey)
end

以下のようにして引数に何も渡すことなく dotFunction() を呼び出すと、dotFunction() 内の self が nil になるため、「attempt to index nil with ‘sampleKey’」というエラーが発生します。

sampleTable.dotFunction()

dotFunction() を呼び出す際、以下のようにしてそれの引数に sampleTable を明示的に渡してあげると先ほどのエラーは発生しなくなります。

sampleTable.dotFunction(sampleTable)

コロンを使用して dotFunction() を呼び出すと、これと全く同じ動作をします。
以下のコードでは dotFunction() の引数に何も渡していないように見えますが、「{テーブル名}:{関数名}()」は「{テーブル名}.{関数名}({テーブル名})」のシンタックスシュガーなので、実際には dotFunction() の self 引数にそれのテーブルを渡しており、処理を実行してもエラーは発生しません。

sampleTable:dotFunction()

ちなみにコロンを使用した関数の呼び出しは「その関数の第一引数にそれのテーブルを渡す」というだけなので、ドットを使用して定義した関数の第一引数の名前は「self」である必要はありません。
例えば以下のコードのように dotFunction() の第一引数の名前を「self」から「sampleParameter」に変えても問題なく動作します。

local sampleTable = {
    sampleKey = "Hello World",
}

function sampleTable.dotFunction(sampleParameter)
    print(sampleParameter.sampleKey)
end

sampleTable:dotFunction()

ただし、公式ドキュメントに記載されているようにこの dotFunction() に別の引数を追加するときは必ず第二引数以降に続ける必要があります。
(その関数がコロンを使用して呼び出された場合、第一引数は必ずそれのテーブルになるため。)

You can define parameters for a method, but you need to list them after the self parameter.

Functions

コロンを使用して定義した関数を呼び出すときの違い

次は以下のコードのようにして sampleTable と colonFunction() を定義します。
colonFunction() はコロンを使用して定義されており、sampleTable を self として自動的に受け取ります。

local sampleTable = {
    sampleKey = "Hello World",
}

function sampleTable:colonFunction()
    print(self.sampleKey)
end

「ドットを使用して定義した関数は必ずドットを使用して呼び出し、コロンを使用して定義した関数は必ずコロンを使用して呼び出す」というわけではないので、この colonFunction() はドットを使用して以下のように呼び出すことができます。

sampleTable.colonFunction(sampleTable)

こちらも以下のように引数に何も渡すことなく colonFunction() を呼び出すと colonFunction() 内の self が nil になるため、「attempt to index nil with ‘sampleKey’」というエラーが発生します。

sampleTable.colonFunction()

そして、こちらもまた以下のようにコロンを使用して colonFunction() を呼び出すと self に sampleTable が代入され、エラーが発生しなくなります。

sampleTable:colonFunction()

基本的にコロンを使用して定義した関数はコロンを使用して呼び出すのですが、「sampleTable.colonFunction」のようにドットを使用して記述しなくてはいけない場合もあります。
それは無名関数(匿名関数)を挟まなくても任意の引数と共に関数を渡せる pcall() や task.delay() などを使用するときです。

例えば pcall() を使用して無名関数を挟み、先ほどの colonFunction() を保護モードで実行するときは以下のようにコロンを使用して記述することができます。

local success, errorMessage = pcall(function()
    sampleTable:colonFunction()
end)

しかし、pcall() は無名関数を挟まずに任意の引数と共に関数を渡すことができます。
ところが、コロンを使用して以下のように記述するとエラーになります。

local success, errorMessage = pcall(sampleTable:colonFunction)

pcall() の第一引数には関数そのものを渡す必要があるため、以下のようにドットを使用して記述し、第二引数に sampleTable を渡してあげます。

local success, errorMessage = pcall(sampleTable.colonFunction, sampleTable)

今回の例での pcall() ではその関数に渡したい引数を第三引数以降に記述するため、colonFunction() に引数を追加した場合は以下のようなコードになります。
このコードの処理を実行するとログに「Hello World Roblox」と出力されます。

local sampleTable = {
    sampleKey = "Hello World",
}

function sampleTable:colonFunction(sampleParameter)
    print(`{self.sampleKey} {sampleParameter}`)
end

local success, errorMessage = pcall(sampleTable.colonFunction, sampleTable, "Roblox")

最後に

参考

お問い合わせ

    タイトルとURLをコピーしました