*Luaリファレンス 要注意点 ~関数~ [#md26225b] **関数の定義は2種類 [#mb15204a] -functionを前に置く書き方(シンタックスシュガー) #sh(lua){{ function foo (x) return 2*x end }} -無名関数を変数に代入する書き方 #sh(lua){{ foo = function (x) return 2*x end }} と、ほぼ同じ意味。~ ~ **関数の定義の2種類の微妙な違い [#i5e3fb9c] -シンタックスシュガー(functionを前に置く書き方)は、 #sh(lua){{ local function foo (<params>) <body> end ↓ local foo foo = function (<params>) <body> end }} と展開されると考えてよい。~~ これは事前にfooを宣言しておくことで、~ 自身の再帰問題(先に宣言が完了していないと未定義となる問題)を避けるため。~ ~ 逆に、無名関数を変数に代入するやり方だと、 #sh(lua){{ local fact = function (n) if n == 0 then return 1 else return n*fact(n-1) end end }} こうなるが、~ これは、factの定義が完了しないうちに、factを呼び出しているため、エラーとなる。~ ~ 一方、 #sh(lua){{ local function foo (<params>) <body> end }} とシンタックスシュガーを使った場合は、~ #sh(lua){{ local fact fact = function (n) if n == 0 then return 1 else return n*fact(n-1) end end }} と展開されるため、問題はない。~ ~ よって、可能な限りシンタックスシュガーを利用するべきである。 ~ **関数はfunction型 [#l726d311] -関数はfunction型という変数にすぎないので、テーブルの要素などにすることも問題ない。 #sh(lua){{ abc = { function(x) return x+1 end, 2 } print(abc[1](3)) --> 4 }} **名前空間に関数を入れるかのような用法 [#gf359e08] -テーブルを名前空間替わりにし、そこに関数を追加していく #sh(lua){{ Lib = {} function Lib.foo (x, y) return x+y end function Lib.goo (x, y) return x+y end }} といった記述も可能である。 ~ **無名関数が良く登場するシーン [#l69ae55e] -他のスクリプト言語と同様、sort関数などで無名関数は良く登場する #sh(lua){{ network = { { name = "grauna", IP = "210.26.30.34"}, { name = "arraial", IP = "210.26.30.23"}, { name = "lua", IP = "210.26.23.12"}, { name = "gerain", IP = "210.26.30.20"}, } table.sort( network, function (a, b) return a.name > b.name end ) }} ~ **関数の( )の省略 [#cde1f2e1] -引数が1つで、文字列もしくは、テーブルコンストラクタの場合のみ( )を省略可能。~ #sh(lua){{ print "abc" ---> print("abc") f{x=10, y=20} ---> f({x=10, y=20}) type{} ---> type({}) }} ~ **呼び出す側の引数の数は、少なくとも多くともOK [#n34d21d4] -引数の数が多かったり、不足していると、勝手に合わせてくれる。~ (正直言って、これはあまり良くない仕様であろう。メリットもあるがデメリットも大きい) #sh(lua){{ function f(a, b) return a or b end f(3) -- b は nil となる。 f(3,4) f(3,4,5) -- 5 は 利用されない。 }} **関数とreturn [#ldeea1c7] -C,Perl,Pythonなどと異なり, LUAではreturnを「ブロックの最後]にしか記述出来ない。 #sh(lua){{ function abc(x) return ----> エラー。ブロックの最後じゃないのにreturnしている。 print(x) end }} -このため、デバッグ時等で、一時的にreturnしてしまいたい場合は、以下のようにブロック化する。 #sh(lua){{ function abc(x) do return end ----> OK。 print(x) end }} **複数の値のreturn と ( ) [#h3bd5bf6] -return ステートメントで''( )を使ってしまうと、値が1つしか返らないので注意!!'' #sh(lua){{ function abc(x, y) return (p(x)) -- p(x)が元来複数の値を返す関数だったとしても、( )をつけることで最初の1つに絞られてしまう。 end }} -複数の返り値を持つ関数を、安易に( )でくくってしまっても同様に値が1つに絞られてしまうので注意 #sh(lua){{ function abc(x,y) return x+10, y+10 -- 2つの値を返す、関数 end local a,b = ( abc(1,2) ) -- 安易にカッコでくくると値が最初の1だけになる。a=11, b=nil となってしまう。 print( abc(1,2) ) --> 11, 12 print( ( abc(1,2) ) ) --> 11 }} **複数の値をリストのように返す [#c8527815] -複数の値をリストのように返すことが可能。~ このあたりは、Perl,Pythonと同じ。特にPythonとほぼ同じ。 #sh(lua){{ function foo() return "a", "b" end x,y,z = 10, foo() --> 10, "a", "b" }} **何も返していない関数はnilを返していることと同じ [#s86d40b1] >> #sh(lua){{ function foo() end x = foo() --> nil 有効な値を返していない関数(return だけ含む)はnilを返した }} **複数の値を返す関数とテーブルのコンストラクタ [#a89f8706] -''関数をリスト内に配置した時、複数の値を返すのは「リストの最後にある場合のみ」''~ わかりにくい話なので、具体例で示す。~ なぜこのような仕様にしたのか理解に苦しむ、間違いやすい特性だと言える。~ #sh(lua){{ function foo() end function foo1() return "a" end function foo2() return "a", "b" end }} という、3つの関数があるとする。~ ~ この時、 #sh(lua){{ t = { foo0(), foo2(), 4 } ---> t = {nil, "a", "b", 4} と思うかもしれないが実際にはそうではない、 -- foo2()は「リストの最後」に位置しないので、値はひとつだけ返すので -- t = {nil, "a", 4 } となる。 }} ~ 一方、~ #sh(lua){{ t = { foo0(), 4, foo2() } ---> t = {nil, 4, "a", "b" } はfoo2はリストの最後に位置するので、そのまま複数の値が返される。 }} **複数の値を返す関数を、呼び出す際に1つだけの値としたい場合、( )で囲む [#ca95987b] - 関数を呼び出したもの全体をカッコで囲うことで、リストは先頭の値1つだけとなる。 #sh(lua){{ function foo2 return "a", "b", "c" end print( (foo2()) ) ---> "a" }} **unpack と 関数の引数 [#x7c8f535] -unpackを使うことにより、配列をリスト化することが可能となる。これはそのまま、関数の引数としても使える。 (Pythonの*[1,2] や*(1,2)などの展開指定と同じ) #sh(lua){{ function f(a, b) return a + b end arr = { 1, 2 } f(unpack(arr)) --> f( 1, 2 ) }} **「...」と可変長引数 [#n80e048e] -...は可変長の「リスト」とみなされる。よって{...}などとすると、このリストを元に配列を作れる。 #sh(lua){{ function add (...) local s = 0 for i, v in ipairs{...} do -- ipairs({...})なので( )は省略可能 s = s + v end return s end print(add(3,4,10,25,12)) --> 54 }} -可変数に利用可能という側面だけではなく、~ 「複数個所から関数を呼び出している際、そのいずれかに問題があるのではないかとトレースする際に、~ 呼び出し側が渡した全ての変数を把握するのに役立つ」~ #sh(lua){{ function foo_check (...) print("calling foo:", ...) return foo(...) end }} -その他、C言語のprintfや各種言語のsprintf系など、~ 可変長引数の関数をラッピングするのにも役立つ。 #sh(lua){{ function fwrite (fmt, ..) return io.write(string.format(fmt, ...)) end }} ~ **「...」とPerl的な値の渡し方 [#r347a150] -このようなことをする必要はないが理解の一環として #sh(lua){{ function foo (...) local a,b,c = ... end }} **「...」の一部を取り出す [#u3b81f72] -select(n, ...) で n番目以降の引数をリストとして返す。 -select('#', ...) で全ての総数を返す #sh(lua){{ for i=1; select('#', ... ) do local arg = select(i, ...) -- i番目のパラメタを取得 <ループのボディ> end }} **名前付き引数 [#e3e030fd] --(概ねPerlのhashを使った方法と同じ考え方) #sh(lua){{ f { old="temp.lua", new="temp1.lua" } --> f({ old="temp.lua", new="temp1.lua" }) -- ( )内にあるテーブルコンストラクタは( )の省略が可能なのだ function f (arg) --以下のような方法でアクセスが可能である。 local a = arg.old local b = arg.new end }} **末尾呼び出しとスタック [#c1a638ae] -Luaでは、「return f(x)」のような形でreturn すると~ 末尾呼び出し(スタック)排除が成立するため~ スタック積みすぎによるオーバーフローが起きない。~ (スタック積を伴う、call命令ではなく、jmp命令に変換されるため) #sh(lua){{ function abc(x) if (ほげほげ) return abc(x) end end }}