ヘルプ:Lua

出典: 謎の百科事典もどき『エンペディア(Enpedia)』
Luaモジュールから転送)
ナビゲーションに移動 検索に移動

このヘルプページはエンペディアに実装されているLua (mw:Extension:Scribunto英語)) を用いてモジュール名前空間にLuaソースを書くためのヘルプ文書です。

この文書はC#PHPjavascriptのどれかを自分でオリジナルのソースが書ける程度には習得しており、またMediaWikiテンプレートの作成にかなり手馴れている前提で多くの事柄が書かれているので、完全な初心者向けではないかもしれません(2016年4月現在)。

Mediawiki用Luaモジュールの日本語ヘルプ執筆協力のお願い

もし貴方がMediawikiのLuaモジュール作成に手馴れているか、または勉強している最中ならば、このページのどこかに貴方の知識の断片を起稿し、Mediawiki用のLuaに関する日本語情報を集約することに協力して頂けると幸いに思います。

概要[編集]

Luaモジュールは単体では何も動作しない、プログラムを直書きしたモジュール名前空間の1ページとしてMediaWiki上に置かれます。これを呼び出す際はモジュール以外の名前空間上に

  • {{#invoke:モジュール名 | ファンクション名(あれば) | 引数(あれば)}}

と記述することで、Luaモジュールに書かれたソースを実行することが出来ます。

例を挙げると、例えばモジュール:HelloWorldを呼び出す際は

  • {{#invoke:HelloWorld|hello}}

と記述してやることで

  • Hello, world!

という実行結果を得ることが出来ます。どうです、そろそろHello Worldに親しみを感じて来たでしょう?

練習場所[編集]

Luaモジュールはモジュール名前空間以外では動作しませんので、モジュールのテスト専用ページとしてモジュール:サンドボックスが用意されています。モジュール:サンドボックスに直接書くのではなく、モジュール:サンドボックスの更に下位に自分の名前を使ったサブページを置いて使用して下さい(モジュール:サンドボックス/貴方の利用者名)。

なお、エンペディアは小規模サーバーで運用されていることから過度の逐次編集はサーバーに負荷を与えやすい関係上、逐次編集がどうしても必須となるLuaモジュールのテストはエンペディア外のExtension:Scribuntoを実装しているMediaWikiサーバーで行うことも検討して下さい。ウィキペディアのモジュールテスト専用サーバーとして test2.wikipedia.org英語 が用意されておりますので、こちらの使用を強くお勧めします

モジュール名前空間ではページを作成すると必ず/docのドキュメント空間がページ上に呼び出されるようになっており、ここにモジュール呼び出し・実行文をあらかじめ書いて置くことで、モジュールの変更と実行結果を同じページ上で一度に見ることが出来ます。

エラー表示と注意点[編集]

もしモジュールソースの書き方が誤っていた場合、保存ボタンを押しても保存されず、ページ上部に赤文字でエラーメッセージが表示されます。親切なんだか不親切なんだか分からない仕様です。ここはぐっと堪えてエラーメッセージから原因を探って下さい。ごく簡単にはエラーメッセージをググってみるのがお勧めですが、まぁ大抵ググっても(一般的なLuaとMediaWiki上のLuaモジュールでは挙動に多少違いがあることから)さっぱり原因特定に至らないことが多く理由がよくわかりませんのでリファレンスマニュアルの英語と格闘して下さい。

まぁたぶん、よく見るであろう「Nilなんちゃらうんぬんかんぬん」は大抵「○○テーブルが定義されてないので戻り値がNil(空っぽ)だよ」というエラーです。

先ほどのモジュール:HelloWorldのページ内容を書き出すと以下のように記述されています。

--は実行されないコメントを表しています。

local p = {} -- 最初に設定するテーブルブロックです。

function p.main( frame ) -- function関数で設定するブロックです。
    return "Hello world" -- これがこのmain (frame )関数の実行結果です。
end -- p.main関数の終了を示します。

return p -- このreturnでテーブルp(内部関数を内包)の内容を全て戻すことになります。

デバッグコンソール[編集]

モジュール名前空間内の編集画面には「デバッグコンソール」と呼ばれる、専用のデバッグ機能があります。これを活用することによって、いちいち保存することなく結果を得ることができます。デバッグコンソールでは、編集画面の内容が更新されると自動的にページの遷移なくコードが読み直されます。必要であれば、mw.log(), mw.dumpObject()などを出力のほしい場所に挿入しましょう。ただし、コンソール内の結果とウィキページでの出力結果が必ずしも等しいとは限りません(#invoke から呼ばれる引数群、frame.args 周辺のメタテーブル扱いで違う挙動が起きます)。

手順
  1. コードを書く(コードがなければコンソールに出力することもできません)
  2. デバッグ用コード mw.log() を出力のほしい場所に挿入します
  3. デバッグコンソールで関数を呼びます(例: mw.log(p.main()), mw.log(p.main({ "a","b","c" }))
  4. 結果が出ます
  5. 編集画面内で編集
  6. デバッグコンソールにカーソルを合わせると、内容が自動的に「編集中のもの」に読み直されます(ページ非遷移)
  7. デバッグコンソールで関数を呼びます
  8. デバッグが終わったら、任意でデバッグ用コードを取り除くかコメントアウトすることができます(しなくても問題はありません)
例(初級・引数なし)
local p = {}
local text = "Hello world"
function p.main( frame )
	return text
end
return p

デバッグコンソール内で mw.log(p.main()) と記述し、Enter キーで実行されます。

例(上級・実践・引数あり)

元のコード

local p = {}
function p.main( frame )
	local args = require( 'Module:Arguments' ).getArgs( frame, { wrappers = 'Template:HelloWorld', removeBlanks = false } )
	local retval = ''
	for key,val in pairs( args ) do
		retval = retval .. key .. ':' .. val .. "<br>\n"
	end
	return retval 
end
return p

デバッグ用コード(args の内容を見る)

local p = {}
function p.main( frame )
	local args = require( 'Module:Arguments' ).getArgs( frame, { wrappers = 'Template:HelloWorld', removeBlanks = false } )
	local retval = ''
	for key,val in pairs( args ) do
		retval = retval .. key .. ':' .. val .. "<br>\n"
	end
	mw.log(mw.dumpObject(args)) -- 【追加】args の中身を展開
	return retval 
end
return p

デバッグ用コードに書き換え、デバッグコンソール内で mw.log(p.main({'a','b','c'})) と記述し、Enter キーで実行されます。

デバッグコンソールでは動くが、ウィキページ内では動かない例
local p = {}
function p.main( frame )
	local args = frame.args
	local retval = ''
	for i=1,#args do
		retval = retval .. i .. ':' .. args[i] .. "<br>\n"
	end
	mw.log(mw.dumpObject(args))
	return retval 
end
return p

デバッグコンソール内で mw.log(p.main({args={'X','Y','Z'}})) と記述し、Enter キーで実行されます。ただし、ウィキページ内では {{#invoke:MODULE_NAME|main|X|Y|Z}} で呼びますが、この際、frame.args はメタテーブルなるものであるため、#args が原因となって、動きません。デバッグコンソール内では通常のテーブルとして渡しているため、こちらは作動します。ではどうするか? → #invoke から受け取った引数のループ処理

引数を渡す[編集]

Luaテンプレートを介してLuaモジュールへ引数を渡すことが出来ます。

詳細は「mw:Lua_reference_manual#frame.args」を参照

モジュールへ渡した引数をLuaモジュール内で扱う際は変数に再配列した方が後々楽です。

モジュール内の一部分だけを返す[編集]

local p = {}

function p.main( frame )
    return "Hello world"
end

function p.super( frame )
    return "Hello world super"
end

function p.special( frame )
    return "Hello world special"
end

return p

このようなソースをテンプレートから呼び出し関数を指定して呼び出した場合、例えば{{#invoke:HelloWorld|super}}とすると「Hello world super」を、{{#invoke:HelloWorld|special}}とすると「Hello world special」を表示します。

このように、1つのモジュール内に複数の実行結果を記述することも出来ます。また、Functionから始まる関数ブロックは他のfunctionブロックに干渉しません。他の関数ブロックにも同じ変数を利用したい場合はグローバル変数を使ったりなんだりしてごそごそ頑張ります(作者が)。

モジュール内で別のモジュールを呼び出す[編集]

function p.main(frame)
	local args = require('Module:HelloWorld').main( { args = { '渡す引数1', '渡す引数2'... } } );
	return args
end

argsに「モジュール:HelloWorld」内の「main」関数の結果を代入、argsを返す。

モジュール内でテンプレートを実行[編集]

  • テンプレート側の記述

{{#invoke:ほげほげ|main|ほにゃらら}}の記述

local p = {}

function p.main( frame )
  -- 引数を受け取る
  local hoge = frame.args[1];

  -- テンプレートを実行する
  local temp = frame:expandTemplate{ title = 'Sup', args = { hoge } }

  -- 返り値
  return '実行結果' ..temp
end

return p
結果

実行結果ほにゃらら

詳細は「mw:Extension:Scribunto/Lua_reference_manual#frame:expandTemplate」を参照

モジュールからタグやテンプレートを呼び出す[編集]

Luaのモジュール内では通常、タグやマジックワードテンプレートなどをそのまま実行することが出来ない(モジュール内のテキストに{{テンプレート}}を書いてもテンプレートとして実行されずそのまま単なる文字列として出力されてしまう)ため、あらかじめ用意されているmw系関数またはframe系関数を利用して実行を行います。

変数[編集]

Luaでは全ての変数は一個のテーブルとして扱われるため、数値変換関数のtonumber()、文字列変換関数のtostring()またはmw.allToString()、配列はテーブルを利用して擬似配列として扱います。テーブルを作る要素は{}で括ります。

また、Luaの配列テーブルは必ず 1 から開始されます。単独の文字列または数値変数であっても、Luaは1個のテーブル(t = { hoge }</nowiki>)として認識しています。

また、変数名は半角英数字が文頭でなければならず、半角数字のみの変数は作れません。変数の大文字と小文字は区別され、同じ変数としては扱われません。例えば、A123a123は別の変数として扱われます。

変数に使用出来ない単語[編集]

変数に使用出来ない単語は以下の一覧です。これらは単独で命令文として使用される固有文字です。一部の文字を大文字にすることで使用出来なくもないですが、紛らわしくなるため止めといた方が無難です。

and elseif if not true break end in or until
do false local repeat while else for nil return function then

この他、下記で述べる演算子で使用される文字列も変数名には使用出来ません。

文字列[編集]

文末セミコロン、コメントアウト[編集]

文末セミコロン「 ; 」
C#、PHPなどでお馴染みの「;」ですが、Luaでもこれはチャンクと呼ばれる行の要素終了を表す記号として扱われています。が。特に注記して文末に置かなくてもエラーを吐きませんので、全て省略可能です。
コメントアウト
--と記述した場所から行末まではコメントアウトとなり実行されません。複数行に跨る場合は--[[でコメントアウト開始、]]でコメントアウト終了を表します。複数行に跨らなくても、行末までコメントアウトされてしまっては困る場合にはこちらを使います。
困ったことに
--[[ コメントアウト開始
t = '<td style="text-align: right">[[ほげほげ]]';
]] -- コメントアウト終了しようとしている行
と書くと、末尾の]]ではなくほげほげ]]のMediawiki記法部分に反応してそこでコメントアウトが終了してしまい、文末]]を生の命令文と解釈してエラーを吐いてしまいます。こういう局面での対処法も用意されていて、--[=[]=]で囲むことで]]に反応しなくなります。
--[=[ コメントアウト開始
t = '<td style="text-align: right">[[ほげほげ]]';
]=] -- コメントアウト終了
さらに=]=]を含む部分をコメントアウトしたい場合は--[==[]==]で囲み、]==]を含む部分をコメントアウトしたい場合は--[===[]===]で囲み……と、いくらでも長くすることができます。

詳細は「mw:Extension:Scribunto/Lua_reference_manual#Comments」を参照

変数を繋ぐ、くっつける[編集]

変数の内容を繋ぐには連結演算子「..」(ドット2つ)を変数の前後に置いてくっつけます。

文字列を変数に入れる[編集]

文字列を記述する際は「''」(シングルクォーテーション2つ)または「""」(ダブルクォーテーション2つ)で括ります。スタイルシート前後の「""」の文字列とかぶるとめんどくさいことになるので、「''」の方を推奨しておきます。

例1 変数名なし
  • テンプレート側の記述

{{#invoke:ほげほげ|main|ほげほげa|ほげほげb|ほにゃらら}}

  • Lua側の記述
local p = {}

function p.main( frame )
  -- 引数を受け取る
  local hoge_a = frame.args[1];
  local hoge_b = frame.args[2];
  local honya = frame.args[3];

  -- 受け取った引数を繋げて投げ戻す
  return 'うひゃっほう' ..hoge_a ..'&nbsp;-&nbsp;' ..hoge_b ..'&nbsp;-&nbsp;' ..honya ..'<br />'
end

return p
結果

うひゃっほうほげほげa - ほげほげb - ほにゃらら

例2 変数名付き
  • テンプレート側の記述

{{#invoke:ほげほげ|main|a=ほげほげaaa|b=ほげほげbbb|h=ほにゃらら}}

  • Lua側の記述
local p = {}

function p.main( frame )
  -- 引数を受け取る
  local hoge_a = frame.args.a;
  local hoge_b = frame.args.b;
  local honya = frame.args.h;

  -- 受け取った引数を繋げて投げ戻す
  return 'もけけけ' ..hoge_a ..'&nbsp;-&nbsp;' ..hoge_b ..'&nbsp;-&nbsp;' ..honya ..'<br />'
end

return p
結果

もけけけほげほげaaa - ほげほげbbb - ほにゃらら

1バイト文字を扱う場合のみframe.args.??の書き方が使えます。2バイト文字(日本語ほか)変数を扱う場合はframe.args['名称']になります。

HTMLタグを関数化[編集]

HTMLタグはLuaの関数として作成することが出来ます。専用の関数mw.htmlが用意されており、mw.html.createと書きます。

作成例:
local div = mw.html.create( 'div' )
div
     :attr( 'id', 'testdiv' )
     :css( 'width', '100%' )
     :wikitext( 'Some text' )
     :tag( 'hr' )
return tostring( div )

この例で出力されるHTMLタグは

HTML
<div id="testdiv" style="width:100%;">Some text<hr /></div>

となります。

配列[編集]

詳細は「mw:Extension:Scribunto/Lua_reference_manual#Base functions」を参照

条件文[編集]

演算子[編集]

Luaの演算子はだいたい他の言語と同じです。ノットイコール(不等演算子)が違う程度?

算術演算子
A + B 加算(A + B)
A - B 減算(A - B)
- A 正負反転(-A)
A * B 乗算(A × B)
A / B 除算(A ÷ B)
A ^ B 累乗(AB
A % B 剰余(AをBで割った余り)
比較演算子
A == B A は B と 等しい(A = B)
A ~= B A は B と 異なる(A ≠ B)
A > B A は B より多い(A > B)
A < B A は B より少ない(A < B)
A >= B A は B より多いか等しい(A ≧ B)
A <= B A は B より少ないか等しい(A ≦ B)
論理演算子
A and B A および B
A or B A または B
not A A でない
連結
A .. B A と B を 連結する(文字列操作)
長さ演算子
# A 配列テーブル A の 長さ
= と == の違い[編集]

===は全く別の意味を持ちます。

A = B - AにBの内容を代入
A == B - AとBは等しい

この2つはよく混同されてエラーの原因になりやすいので注意して下さい。

エンペディアのモジュール名前空間ではこの手のエラー入力をすると行の左端に赤い×印が表示されます。

優先順位[編集]

上から下に向かって処理されます。同じ順位の場合は、左から順に処理されます。

  1. ^ 例外的に、右から順に処理されます。
  2. not # - (単項演算子: 減算ではなく正負反転の場合)
  3. * / %
  4. + -
  5. .. 例外的に、右から順に処理されます。
  6. < > <= >= ~= ==
  7. and
  8. or

- 3 ^ 2

この場合は、「 - 3 」よりも「 3 ^ 2 」の計算が優先され、「-9」が返されます。

3 * - 2 - 10

この場合、正負反転- 2 、乗算の 3 * (-2)除算(-6) - 10 の順に処理され、「-16」が返されます。

a == 2 * 4 or b == 8 and ( c >= 7 + 2 or d == 24 ) and e <= 9 - 1

この場合、まずは全ての算術演算と比較演算が行われます。そのあと、括弧で囲まれた右のor、左のand、右のand、左のorの順に判定が行われます。

nil

nil は演算子ではなく「テーブル未定義の場合に返される文字」で、空変数とは別物として扱われます。

ループ処理[編集]

ループ処理に使える命令にはfor, while, repeatrepeat/until)があり、ループから抜けるための命令としてbreakがあります。

for[編集]

for文は初期値、終了値、増加量を指定してループさせます。

for i = 1, 3, 1 do -- iを1から開始、3回目までループ、1ずつ加算(1ずつ加算は省略可)
	t = t ..i ..'回目<br />' -- ローカル変数 t にループ回数数値 + 文字列をくっつける
end

while[編集]

whileは条件がTrueの間ずっとループします。条件設定間違えてうっかり無限ループをやらかしたことがある人は素直に挙手。

while  i <= 3 -- iが10より小さい間ずっとループ
	t = t ..i ..'回目<br />' -- ローカル変数 t にループ回数数値 + 文字列をくっつける
	i = i + 1; -- 条件判定用の変数 i を 1 ずつ加算する
end

repeat[編集]

repeat文はループの終端にあるuntil文で指定した式の値がfalseの間だけループされます。

repeat
	a = a + 1;
until i == z;

break[編集]

ループ中に処理を抜けるための文としてbreak文があります。使用に際して注意するのは、break文はブロックの終端にしか書けないため、if文と組み合わせるなどして

if a == b then
	break
end

のように必ずbreakの後にendを置かなければエラーが出ます。強制的にその場所でループを終了させたい場合は

do
	break
end

のように書きます。

pairs と ipairs[編集]

ざっと説明するとpairsが配列の数値+文字列変数込み、ipairsが数値変数のみ。

実行結果[編集]

上記の例は全て、表示は

1回目
2回目
3回目

…となりますが、実際の変数 t の内容は1回目<br />2回目<br />3回目<br />となっています。改行コード\nなどもエスケープシーケンスとして代入出来ます。

テーブル要素数分の処理[編集]

通常のテーブル要素数を取得するには、テーブル変数名に # をつけるだけです(メタテーブルは使用不可)。

local testTbl = {'a','b','c'}
local p = {}
local retval = ''

function p.main()
	for i = 1, #testTbl do
		retval = retval .. i .. ':' .. testTbl[i] .. "<br>\n"
	end
	mw.log(mw.dumpObject(testTbl))
	return retval
end

return p
table#1 {
  "a",
  "b",
  "c",
}
1:a<br>
2:b<br>
3:c<br>
  • ウィキページ:

1:a
2:b
3:c

foreach[編集]

jawp:Foreach文

local testTbl = {'A','B','C'}
local p = {}
local retval = ''

function p.main()
	for key,val in pairs( testTbl ) do
		retval = retval .. key .. ':' .. val  .. "<br>\n"
	end
	mw.log(mw.dumpObject(testTbl))
	return retval
end

return p
table#1 {
  "A",
  "B",
  "C",
}
1:A<br>
2:B<br>
3:C<br>
  • ウィキページ:

1:A
2:B
3:C

invoke から受け取った引数のループ処理[編集]

{{#invoke:MODULE_NAME|main|X|Y|Z}} で呼ぶことを想定。#foreach のごとく。

local p = {}
local retval = ''

function p.main(frame)
	local args = frame.args
	for key,val in pairs( args ) do
		retval = retval .. key .. ':' .. val  .. "<br>\n"
	end
	mw.log(mw.dumpObject(args))
	return retval
end

return p
table#1 {
  "X",
  "Y",
  "Z",
}
1:X<br>
2:Y<br>
3:Z<br>
  • ウィキページ {{#invoke:MODULE_NAME|main|X|Y|Z}}:

1:X
2:Y
3:Z

ループと処理速度[編集]

処理が複雑でないモジュールではあまり神経質になる必要はないのですが、for文、if文を大量に使用し複数回数の連続ループ判定文のような似たような処理の連続は、処理を連続しない内容のものと比較すると確実に負荷が増え、処理速度が落ちます。

ループ判定を繰り返さずに処理を軽くする方法はたくさんありますので、各自の健闘を祈ります。

関数[編集]

下記に関数と簡単な説明を列挙しておきますが、参考文献は全て英語ですので辞書片手に頑張るか、google翻訳大先生とお友達になって下さい英語

  • frame:preprocess( 'テキスト' ) / フレーム・プリプロセス
指定した文字列をウィキマークアップとして実行します。モジュール処理ではないので複雑なテンプレートやウィキマークアップ文字列を渡すとモジュール全体の処理速度を落とします。
単純なウィキマークアップならほとんどコレ1つで賄えます。

詳細は「mw:Extension:Scribunto/Lua_reference_manual#frame:preprocess」を参照

  • frame:expandTemplate{ title = 'テンプレート名', args = { '渡す引数' } } / フレーム・エキスパンドテンプレート
指定したテンプレートに引数を含めて処理をぶん投げます。モジュール内からテンプレートを呼び出す形になるので、全体の処理速度がテンプレート実行速度に依存します。
渡したい引数が複数ある場合はargs = { 1 = 'ほげほげ', title = 'ほにゃらら' }のようにテーブルの形でぶん投げます。

詳細は「mw:Extension:Scribunto/Lua_reference_manual#frame:expandTemplate」を参照

  • frame:extensionTag{ name = タグ名, content = タグを実行するテキスト, args = 渡す引数テキストまたはテーブル } / フレーム・エクステンションタグ
指定したタグを実行します。渡す引数が複数なら上記expandTemplateと同じくテーブル化します。マジックワード系のタグがだいたいコレで代用出来ます。

詳細は「mw:Extension:Scribunto/Lua_reference_manual#frame:extensionTag」を参照

  • frame:callParserFunction{ name = 関数名, args = { 渡す引数テーブル } } / フレーム・コールパーサーファンクション
Mediawikiに用意されている様々なパーサーファンクション(パーサー関数)を実行します。具体的には#time#ifなどの、mw:パーサー関数mw:Manual:パーサー関数)で述べられている動的関数のことです。

詳細は「mw:Extension:Scribunto/Lua_reference_manual#frame:callParserFunction」を参照

Mediawiki上での制限[編集]

MediaWikiではmw:Extension:Scribuntoを通してLuaの高速かつ軽快な動作特徴をテンプレートに応用し利点を甘受してサーバーの負荷を軽減しつつテンプレート動作を軽快にすることが出来ますが、MediaWiki上で動作させるにあたっていくつかの機能が実装されていません。

代表的なものとしてはC言語を勉強したなら最初にお目にかかった命令文であろう「Hello World!」を表示させるために使用したPrint()のほか、Unicode文字の互換性に起因するいくつかの機能が使用できません。これらはmw:Extension:Scribunto/Lua_reference_manual#Differences_from_standard_Lua英語にまとめられています。

詳細は「mw:Extension:Scribunto/Lua_reference_manual#Differences_from_standard_Lua」を参照

エンペディア利用者のメモ帳[編集]

コメントを書きこむ
  • メモ invokeから渡された引数は文字列として扱われるため、数値として扱う際には注意が必要である。1+"1"→2 だが、1=="1"→false、1>"1"はエラーとなる。--かにふとん (トーク) 2016年7月30日 (土) 00:35 (JST)
  • メモ falseとして扱われるのはnilとfalseのみで、それ以外のあらゆる値はtrueとして扱われる。そのため以下のようなjawp:短絡評価が行われる。andは左がnilかfalseなら左を返し、それ以外なら右を返す。orは左がnilかfalseなら右を返し、それ以外なら左を返す。「arg or nil」という形で用いられるのはこの挙動によるもの。--かにふとん (トーク) 2016年7月30日 (土) 00:35 (JST)
利用者のメモ書き、みたいな感じで末文に節設けてちょこちょこ追記したらいいんじゃないですかね。内容加筆するほどまとまってはいないけどこういう感じで理解している、とか、こういう便利な使い方がある、程度の軽い気持ちで。
実際に仕事として使っている識者が見て誤りがあれば修正してくれるかもですし。ここでいちばんの識者は恐らくRxyさんでしょうけど。「きりきりと勉強したまい学習者諸君フハハハハハ」とか思って見てる可能性が脳裏をふとよぎる。--Nami-ja(凪海) 会話 / 履歴 2016年7月30日 (土) 14:11 (JST)
  • メモ

label = ({ a01 = '123', a02 = '456', a03 = '789', })[args] if label then return label end

…のテーブルを作ると、argsの中身がlabel[args]と一致した場合の引数値のみが生き残り、他は抹消されるので条件文をループせずに済むとかね。--Nami-ja(凪海) 会話 / 履歴 2016年7月30日 (土) 14:11 (JST)

参考文献[編集]

関連項目[編集]

外部リンク[編集]