サーチ…


構文

  • funcname = function(paramA、paramB、...)ボディ。 return exprlist end - 単純な関数
  • 関数funcname (paramA、paramB、...)本体。 return exprlist end - 上記のための短縮形
  • ローカルなfuncname = function(paramA、paramB、...)body; return exprlist end - ラムダ
  • ローカルfuncname ; funcname = function(paramA、paramB、...)ボディ。 return exprlist end - 再帰呼び出しを行うことができるラムダ
  • ローカル関数funcname (paramA、paramB、...)body; return exprlist end - 上記のための短縮形
  • funcname (paramA、paramB、...) - 関数を呼び出す
  • local var = varまたは "Default" - デフォルトパラメータ
  • return nil、 "エラーメッセージ" - エラーで中止する標準的な方法

備考

関数は通常function a(b,c) ... endで設定され、まれに変数を無名関数( a = function(a,b) ... end )に設定すると設定されます。関数をパラメータとして渡すときは逆ですが、匿名関数が主に使用され、通常関数は頻繁には使用されません。

関数の定義

function add(a, b)
    return a + b
end
-- creates a function called add, which returns the sum of it's two arguments

構文を見てみましょう。まず、 functionキーワードが表示されます。まあ、それはかなり説明的です。次に、 add識別子が表示されます。名前。我々は議論(a, b)見ることができます。これらは何でも構いません。彼らはローカルです。関数本体の内部でのみアクセスできます。最後までスキップしてみましょう...まあ、 end !その中間にあるのは関数本体です。呼び出されたときに実行されたコード。 returnキーワードは、関数が実際に有用な出力を与えるようにするものです。それがなければ、この関数は何も返しません。これはnilを返すことと等価です。もちろんこれはIOとやりとりするもの、例えば以下のような場合に便利です。

function printHello(name)
    print("Hello, " .. name .. "!");
end 

その関数では、return文は使用しませんでした。

関数は条件付きで値を返すこともできます。つまり、関数は何も返さない(nil)か値を返すという選択肢があります。これは次の例で実証されています。

function add(a, b)
    if (a + b <= 100) then
        return a + b -- Returns a value
    else
        print("This function doesn't return values over 100!") -- Returns nil
    end
end

次のように、関数がコンマで区切って複数の値を返すことも可能です。

function doOperations(a, b)
    return a+b, a-b, a*b
end

added, subbed, multiplied = doOperations(4,2)

関数はローカル宣言することもできます

do
    local function add(a, b) return a+b end
    print(add(1,2)) --> prints 3
end
print(add(2, 2)) --> exits with error, because 'add' is not defined here

テーブルに保存することもできます:

tab = {function(a,b) return a+b end}
(tab[1])(1, 2) --> returns 3

関数を呼び出す

関数は、呼び出すことができれば便利です。関数を呼び出すには、次の構文が使用されます。

print("Hello, World!")

私たちはprint関数を呼び出していprint 。引数"Hello, World"を使用します。明らかなように、これはHello, Worldを出力ストリームに出力します。他の変数と同様に、戻り値にアクセスできます。

local added = add(10, 50) -- 60

変数は、関数のパラメータでも受け入れられます。

local a = 10
local b = 60

local c = add(a, b)

print(c)

表や文字列を期待する関数は、文法的な砂糖を使って呼び出すことができます。呼び出しを囲む括弧は省略できます。

print"Hello, world!"
for k, v in pairs{"Hello, world!"} do print(k, v) end

匿名関数

匿名関数の作成

匿名関数は、名前を持たないことを除けば、通常のLua関数と同じです。

doThrice(function()
    print("Hello!")
end)

ご覧のように、この関数はprintaddような名前には割り当てられません。無名関数を作成するには、名前を省略するだけです。これらの関数は引数を取ることもできます。

構文的な砂糖を理解する

次のコードを理解することが重要です

function double(x)
    return x * 2
end

実際には

double = function(x)
    return x * 2
end

しかし、関数が直接変数に代入されるので 、上記の関数は匿名ではありません

関数はファーストクラスの値です

これは、関数が数値や文字列などの従来の値と同じ権限を持つ値であることを意味します。関数は変数、テーブル、引数として渡すことができ、他の関数から返すことができます。

これを実証するために、 "half"関数も作成します:

half = function(x)
    return x / 2
end

ですから、 halfdouble 2つの変数があり、どちらも関数を値として含みます。与えられた2つの関数に数値4を供給し、両方の結果の合計を計算する関数を作成したければどうでしょうか?

sumOfTwoFunctions(double, half, 4)ようにこの関数を呼びたいと思うでしょう。これはdouble関数、 half関数、および整数4を自分の関数に送ります。

function sumOfTwoFunctions(firstFunction, secondFunction, input)
    return firstFunction(input) + secondFunction(input)
end

上記のsumOfTwoFunctions関数は、関数が引数内でどのように渡され、別の名前でアクセスされるかを示しています。

デフォルトパラメータ

function sayHello(name)
    print("Hello, " .. name .. "!")
end

その関数は単純な関数であり、うまく機能します。しかしsayHello()呼び出すとどうなるでしょうか?

stdin:2: attempt to concatenate local 'name' (a nil value)
stack traceback:
    stdin:2: in function 'sayHello'
    stdin:1: in main chunk
    [C]: in ?

それはまさに素晴らしいことではありません。これを修正するには2つの方法があります:

  1. あなたはすぐに関数から戻ります:

    function sayHello(name)
      if not (type(name) == "string") then
        return nil, "argument #1: expected string, got " .. type(name)
      end -- Bail out if there's no name.
      -- in lua it is a convention to return nil followed by an error message on error
    
      print("Hello, " .. name .. "!") -- Normal behavior if name exists.
    end
    
  2. デフォルトのパラメータを設定します

    これを行うには、単純にこの単純な式を使用します

function sayHello(name)
    name = name or "Jack" -- Jack is the default, 
                          -- but if the parameter name is given, 
                          -- name will be used instead
    print("Hello, " .. name .. "!")
end

イディオムのname = name or "Jack"はLuaの短絡のために、 or Luaで短絡して動作します。 or左側の項目がnilまたはfalse以外の場合、右辺は決して評価されません。一方、場合sayHello 、その後、パラメータなしで呼び出されたnameになりますnil 、及びその文字列は"Jack"に割り当てられるname 。 (ブール場合は、このイディオムは、そのため、動作しないことに注意してくださいfalse問題のパラメータの妥当な値です。)

複数の結果

Luaの関数は複数の結果を返すことができます。

例えば:

function triple(x)
    return x, x, x
end

関数を呼び出すときに、これらの値を保存するには、次の構文を使用する必要があります。

local a, b, c = triple(5)

この場合、 a = b = c = 5となる。また、変数リストの目的の場所で変数_を使用して、返された値を無視することもできます。

local a, _, c = triple(5)

この場合、2番目に返される値は無視されます。戻り値を変数に代入しないで無視することもできます:

local a = triple(5)

変数aは最初の戻り値が割り当てられ、残りの2つは破棄されます。

関数によってさまざまな量の結果が返されると、その中の関数を実行することで、それらをすべてテーブルに格納することができます。

local results = {triple(5)}

このようにして、 resultsテーブルを反復して、関数が何を返すかを調べることができます。

注意

これは、いくつかのケースでは驚くかもしれません。たとえば、次のようになります。

local t = {}
table.insert(t, string.gsub("  hi", "^%s*(.*)$", "%1")) --> bad argument #2 to 'insert' (number expected, got string)

これは、 string.gsubが2つの値を返すために発生します。指定された文字列、置換されたパターンの出現回数、発生した一致の総数です。

これを解決するには、次のように中間変数を使用するかput ()を呼び出します。

table.insert(t, (string.gsub("  hi", "^%s*(.*)$", "%1"))) --> works. t = {"hi"}

これは呼び出しの最初の結果だけを取得し、残りは無視します。

変数の数

可変引数

名前付き引数

local function A(name, age, hobby)
    print(name .. "is " .. age .. " years old and likes " .. hobby)
end
A("john", "eating", 23) --> prints 'john is eating years old and likes 23'
-- oops, seems we got the order of the arguments wrong...
-- this happens a lot, specially with long functions that take a lot of arguments
-- and where the order doesn't follow any particular logic

local function B(tab)
    print(tab.name .. "is " .. tab.age .. " years old and likes " .. tab.hobby)
end
local john = {name="john", hobby="golf", age="over 9000", comment="plays too much golf"}
B(john)
--> will print 'John is over 9000 years old and likes golf'
-- I also added a 'comment' argument just to show that excess arguments are ignored by the function

B({name = "tim"}) -- can also be written as
B{name = "tim"} -- to avoid cluttering the code
--> both will print 'tim is nil years old and likes nil'
-- remember to check for missing arguments and deal with them

function C(tab)
    if not tab.age then return nil, "age not defined" end
    tab.hobby = tab.hobby or "nothing"
    -- print stuff
end

-- note that if we later decide to do a 'person' class
-- we just need to make sure that this class has the three fields
-- age, hobby and name, and it will be compatible with these functions

-- example:
local john = ClassPerson.new("John", 20, "golf") -- some sort of constructor
john.address = "some place" -- modify the object
john:do_something("information") -- call some function of the object
C(john) -- this works because objects are *usually* implemented as tables

引数の型のチェック

一部の関数は、特定の型の引数に対してのみ機能します。

function foo(tab)
    return tab.bar
end
--> returns nil if tab has no field bar, which is acceptable
--> returns 'attempt to index a number value' if tab is, for example, 3
--> which is unacceptable

function kungfoo(tab)
    if type(tab) ~= "table" then
        return nil, "take your useless " .. type(tab) .." somewhere else!"
    end

    return tab.bar
end

これにはいくつかの意味があります。

print(kungfoo(20)) --> prints 'nil, take your useless number somewhere else!'

if kungfoo(20) then print "good" else print "bad" end --> prints bad

foo = kungfoo(20) or "bar" --> sets foo to "bar"

これで、必要なパラメータで関数を呼び出すことができ、プログラムをクラッシュさせません。

-- if we actually WANT to abort execution on error, we can still do
result = assert(kungfoo({bar=20})) --> this will return 20
result = assert(kungfoo(20)) --> this will throw an error

だから、特定のクラスのインスタンスを持つ何かをする関数があればどうでしょうか?これは難しいです。クラスとオブジェクトは通常はテーブルなので、 type関数は'table'を返し'table'

local Class = {data="important"}
local meta = {__index=Class}

function Class.new()
    return setmetatable({}, meta)
end
-- this is just a very basic implementation of an object class in lua

object = Class.new()
fake = {}

print(type(object)), print(type(fake)) --> prints 'table' twice

解決策:メタテーブルを比較する

-- continuation of previous code snippet
Class.is_instance(tab)
    return getmetatable(tab) == meta
end

Class.is_instance(object) --> returns true
Class.is_instance(fake) --> returns false
Class.is_instance(Class) --> returns false
Class.is_instance("a string") --> returns false, doesn't crash the program
Class.is_instance(nil) --> also returns false, doesn't crash either

閉鎖

do
    local tab = {1, 2, 3}
    function closure()
        for key, value in ipairs(tab) do
            print(key, "I can still see you")
        end
    end
    closure()
    --> 1 I can still see you
    --> 2 I can still see you
    --> 3 I can still see you
end

print(tab) --> nil
-- tab is out of scope

closure()
--> 1 I can still see you
--> 2 I can still see you
--> 3 I can still see you
-- the function can still see tab

典型的な使用例

function new_adder(number)
    return function(input)
        return input + number
    end
end
add_3 = new_adder(3)
print(add_3(2)) --> prints 5

より高度な使用例

function base64.newDecoder(str) -- Decoder factory
    if #str ~= 64 then return nil, "string must be 64 characters long!" end

    local tab = {}
    local counter = 0
    for c in str:gmatch"." do
        tab[string.byte(c)] = counter
        counter = counter + 1
    end

    return function(str)
        local result = ""

        for abcd in str:gmatch"..?.?.?" do
            local a, b, c, d = string.byte(abcd,1,-1)
            a, b, c, d = tab[a], tab[b] or 0, tab[c] or 0, tab[d] or 0
            result = result .. (
                string.char( ((a<<2)+(b>>4))%256 ) ..
                string.char( ((b<<4)+(c>>2))%256 ) ..
                string.char( ((c<<6)+d)%256 )
            )
        end
        return result
    end
end


Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow