4
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Lua言語でテーブルを用いてswith-case文を疑似的に実現する

Last updated at Posted at 2020-08-13

はじめに

バーチャルキャストでユーザーが自由に作成し、VR空間に持ち込むことが可能なアイテム、VCIアイテム
VCIアイテムはLua言語のスクリプトを組み込むことで、アイテムに機能を実装することが出来ます。
lua言語はスクリプト言語でかつ、動作も高速と非常によい言語ではあるのですが、swith-case文がない!!!

そのため、アイテムの名前やコメント内容で処理を分岐させる場合、以下のようにif文で処理を分岐させる必要があり、if-elseが大量になったり、ネストが深くなったり、複数箇所に同じ条件文を書く場合修正ミスが発生したりします。

function onMessage(sender, name, message)
    if name == "comment" then
        local str = nil

        -- カメラを制御するコメントが受信したら、VCI内の特定アイテムを操作する
        if message == "カメラ1" or message == "camera1" or message == "Camera1"  or message == "Cam1" or message == "cam1" then
            str = "camera1"
        elseif message == "カメラ2" or message == "camera2" or message == "Camera2"  or message == "Cam2" or message == "cam2" then
            str = "camera2"
            -- カメラ3,4,5についても同様のif文が必要
        end

        -- strがnilの場合(if文の条件にヒットしなかった)、処理しない
        if str ~= nil then
            src = cameraModule.GetPosition()

            local item = vci.assets.GetSubItem(str)
            dst = item.GetPosition()
            start = vci.me.UnscaledTime
        end
    end
end

判定条件が少ない場合は問題ありませんが、条件が増えたりすると可読性が悪化し、条件追加時にミスをする恐れがありますし、なにより美しくありません。

そこで、先日公開したこちらのアイテムに組み込んだスクリプトをベースにテーブルを用いてswith-case文を疑似的に実現する手法を説明します。

テーブルを使って、変数に値をセットする

前述のスクリプトをテーブルを用いて、書いた場合以下のようになります。

-- テーブルを使用し、キーと対応した値を書く
local CameraTbl = {
    ["カメラ1"] = "camera1", 
    ["Camera1"] = "camera1", 
    ["camera1"] = "camera1", 
    ["Cam1"] = "camera1", 
    ["cam1"] = "camera1", 

    ["カメラ2"] = "camera2", 
    ["Camera2"] = "camera2", 
    ["camera2"] = "camera2", 
    ["Cam2"] = "camera2", 
    ["cam2"] = "camera2", 
    -- カメラ3,4,5についても同様
}

function onMessage(sender, name, message)
    if name == "comment" then
        local str = CameraTbl[message]

        -- テーブルに存在しないキーを指定した場合、nilが返る(defaultの動作に相当)
        if str ~= nil then
            src = cameraModule.GetPosition()

            local item = vci.assets.GetSubItem(str)
            dst = item.GetPosition()
            start = vci.me.UnscaledTime
        end
    end
end
ほかの言語だとこんな感じ
String str

switch(message) {
    case :
    case "カメラ1" :
    case "Camera1" :
    case "camera1" :
    case "Cam1" :
    case "cam1" :
        str = "camera1";
        break;
    case "カメラ2" :
    case "Camera2" :
    case "camera2" :
    case "Cam2" :
    case "cam2" :
        str = "camera2";
        break;
    default :
       break;
}

function onMessage(sender, name, message)の関数内のはかなりすっきりしました。
テーブルを1つ用意しておけば、それを複数箇所から参照して動作する場合でも、テーブルのみを修正することになるため、ミスの可能性が減るかと思います。
また、テーブルについてはモジュールで別のソースに分離することも可能ですので、テーブルは別ソースに記述し、メインのソースからはアクセッサを通して参照することも可能です。

テーブルを使って、関数を呼び分ける

lua言語の変数は関数型をサポートしており、関数型の変数についてもテーブルに格納することが出来ます。
そのため、テーブルに関数型の値、テーブルを用いて引数テーブルを持つことでテーブルを用いて処理を分岐させることが可能です。

-- 使用可能なアイテム毎に関数型の値と引数のテーブルを持つ
local itemTbl = {
    ["eye"] = {["function"] = switchVisiable, ["args"] = nil }, 
    ["move"] = {["function"] = sendMsg, ["args"] = {"move"} }, 
    ["panel1"] = {["function"] = panelChange, ["args"] = {1} }, 
    ["panel2"] = {["function"] = panelChange, ["args"] = {2} }, 
    ["panel3"] = {["function"] = panelChange, ["args"] = {3} }, 
    ["panel4"] = {["function"] = panelChange, ["args"] = {4} }, 
    ["panel5"] = {["function"] = panelChange, ["args"] = {5} }, 
    ["xMinus"] = {["function"] = sendMsg, ["args"] = {"xMinus"} }, 
    ["yMinus"] = {["function"] = sendMsg, ["args"] = {"yMinus"} }, 
    ["zMinus"] = {["function"] = sendMsg, ["args"] = {"zMinus"} }, 
    ["xPlus"] = {["function"] = sendMsg, ["args"] = {"xPlus"} }, 
    ["yPlus"] = {["function"] = sendMsg, ["args"] = {"yPlus"} }, 
    ["zPlus"] = {["function"] = sendMsg, ["args"] = {"zPlus"} }, 
}

---[SubItemの所有権&Use状態]アイテムをグラッブしてグリップボタンを押すと呼ばれる。
---@param use string @押されたアイテムのSubItem名
function onUse(use)
    if itemTbl[use] then
        local func = itemTbl[use]["function"] 
        local args = itemTbl[use]["args"] 
        -- funcがnil(テーブルにキーがない)場合、使用しても何も起こらないアイテム
        if func ~= nil then
            if args == nil then
                func()
            elseif #args == 1 then
                func(args[1])
            end
        end
    end
end
テーブルを使わない場合だとこんな感じ
---[SubItemの所有権&Use状態]アイテムをグラッブしてグリップボタンを押すと呼ばれる。
---@param use string @押されたアイテムのSubItem名
function onUse(use)
    if use == "eye" then
        switchVisiable()
    elseif use == "panel1" or use == "panel2" or use == "panel3" or use == "panel4" or use == "panel5" then
        local args
        if use == "panel1" then
            args = 1
        elseif use == "panel2" then
            args = 2
        elseif use == "panel3" then
            args = 3
        elseif use == "panel4" then
            args = 4
        elseif use == "panel5" then
            args = 5
        end
        panelChange(args)
    elseif use == "move" or use == "xMinus" or use == "yMinus" or use == "zMinus" or use == "xPlus" or use == "yPlus" or use == "zPlus" then
        local args
        if use == "move" then
            args = "move"
        elseif use == "xMinus" then
            args = "xMinus"
        elseif use == "yMinus" then
            args = "yMinus"
        elseif use == "zMinus" then
            args = "zMinus"
        elseif use == "xPlus" then
            args = "xPlus"
        elseif use == "yPlus" then
            args = "yPlus"
        elseif use == "zPlus" then
            args = "zPlus"
        end
        sendMsg(args)
    end
end

複数の条件で呼び出す際のパラメータを変えつつ、呼び出す処理がかなり簡潔になりました。

最後に

今回の手法はpythonでswitch-case文を実現できないかと検索していた際に、こちらのページ(【Python入門】switch文の代わりに使える書き方)を見つけ、lua言語でも使用可能なtipsであると感じたのがきっかけです。
今回はコメントの内容やアイテム名を使用しての解説でしたが、時間をstring型に変換してタイムテーブルとして扱うなどluaのテーブル型は応用が利くため、色々トライしてみてください。

P.S.公開中のアイテムのスクリプトライセンスはMITですので、抜き出して改造を認めています。

4
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?