aoitaku Advent Calendar 2016 : 6日目 - Lua

この記事は aoitaku Advent Calendar 2016 の 6 日目の記事です。

Swift の話の最後に出てきた言語の話は次回やるとして、番外編ということで Lua のことを書きます。

クラスがない言語

Lua はブラジル生まれのスクリプト言語で、主に C などで書かれたアプリケーションへの組み込み用途を目的にしています。とても軽量で組み込みやすく、それでて高速に動作するのが特徴です。

Lua は一応オブジェクト指向プログラミングをサポートしていますが、クラスという概念を持ちません。クラスを作る予約語も機能もありません。

それどころか Lua には表向きは配列すらありません。表向き、というのは、Lua にあるのはテーブルという機能で、キーに数値を使うと配列として扱われるようになります。
配列のようなものとして扱うための機能も一応あって、たとえばテーブルに対する長さ演算子 #t[n+1] == nil となるようなキー n を返すので、キーが連続する数値だけなら配列の個数を得るために使うことができます*1

なんでもテーブル

テーブルというのは JavaScript のオブジェクトのようなもので、キーと値のペアで構成されたデータ構造を持ちます。キーに数値を使った部分は配列のように扱うことができ、キーに文字列を使った部分は連想配列のように扱うことができます。

混ざってても大丈夫。

t = {}
t[1] = 1
t[2] = 2
t["foo"] = 3
t["bar"] = 4

print(#t) -- 2

キーに文字列を使った場合は . を使ってその要素にアクセスできます。

t = {}
t["foo"] = "bar"
print(t.foo) -- "bar"

これを使うとこういうことができます。

t = {}
t[1] = 1
t[2] = 2
t["length"] = function(self)
  return #self
end

print(t.length(t)) -- 2

キーが文字列の部分は長さ演算子に影響を与えないので、配列を想定したテーブルに関数を生やすとかいった使い方ができます。

self

Lua の関数呼び出しはちょっと面白くて、第一引数にレシーバ自身を渡すシンタックスシュガーがあります。

上の length 関数はこのように呼び出せます。

t:length() -- t.length(t) と同じ

まるでオブジェクト指向のメソッド呼び出しのようですね。
このように、テーブルをうまく使うことで配列やオブジェクトのようなものを実現できるようになっています。

メタテーブル

Lua にはメタテーブルといういわゆる演算子オーバーロードを行うための機能があります。
たとえばメタテーブルの __add というキーにメソッドを生やすと二項演算子 + の動作を指定することができます。

おもしろいのはメタテーブルに __index というキーにメソッドを生やすとメソッド探査をフックしたりできます。これを使うとコンポジションと委譲を使って継承っぽい動きを実現したりできます。言語として継承をサポートしていないのでむしろ柔軟に継承を実装できるでしょう。mix-in っぽいことももちろんできます。

引数が文字列やテーブル一個だけのときは関数呼び出しのカッコを省略できる

たとえばテーブルだと、

function ab(args)
  print(args.a, args.b)
end

ab{a="hoge", b="fuga"}

こんなふうに名前付き引数っぽい呼び出しができます。

文字列だとたとえばローカライゼーションとかそういうときに便利だと思います。
あとは複数行文字列リテラルに使うと外部 DSL っぽいものを作りやすいのではないでしょうか。

閑話休題

Lua について軽く紹介してきました。ぼく自身 Lua のコードはちょっと書いただけで、後はふだん使ってるシェルがスクリプトとして Lua を採用しているので Lua 使う機会ちょいちょいあるかな、くらいの感じですが、Lua の仕様を知ったときはかなり世界が広がったような気がします。
継承に頼らず、コンポジションと委譲をうまく使おうという発想も Lua に触れて以降身についた気がしますし、そのおかげで JavaScript に対する理解も深まったのではないかなと思っています。

さて、そういうわけで次回いよいよ JavaScript の話で、これが言語編の最終回になります。お楽しみに。

*1:途中に抜けがある場合の動作は不定となります