サーチ…
前書き
Varargsは一般的に知られているように、関数が指定なしで任意の数の引数を取ることを可能にします。そのような関数に与えられたすべての引数は、 varargリストと呼ばれる単一の構造体にパッケージ化されています 。ルアには...
と書かれてい...
。 select()
関数を使用して、与えられた引数の数とそれらの引数の値を抽出する基本的な方法がありselect()
が、より高度な使用パターンは構造を完全に活用することができます。
構文
- ... - これを引数とする関数を、可変の関数として表示する
- select(what、...) - 'what'が1からvarargの要素数までの範囲の数値である場合、 'what'番目の要素をvarargの最後の要素に返します。インデックスが範囲外の場合、リターンはゼロになります。 'what'が文字列 '#'の場合、varargの要素数を返します。
備考
効率
varargリストは言語のPUC-Rio実装のリンクリストとして実装されます。つまり、インデックスはO(n)です。これは、以下の例のように、 select()
を使用してvarargの要素を反復処理することは、O(n ^ 2)操作であることを意味します。
for i = 1, select('#', ...) do
print(select(i, ...))
end
varargリストの要素を反復処理する場合は、まずリストを表にパックします。テーブルへのアクセスはO(1)なので、反復はO(n)です。または、もしあなたがそう思っているなら、高度な使い方のセクションのfoldr()
例を見てください。 O(n)のvarargリストを反復するために再帰を使用します。
シーケンス長の定義
varargは、varargの長さが明示的に渡された(または計算された)nilsを尊重する点で有用です。例えば。
function test(...)
return select('#', ...)
end
test() --> 0
test(nil, 1, nil) --> 3
ただし、この動作はテーブルの動作と競合します。ただし、length演算子#
は 'holes'(埋め込みnils)で動作しません。穴のあるテーブルの長さを計算することは定義されておらず、信頼できません。したがって、 ...
の値に応じて、 {...}
長さを取っても「正しい」答えが得られないことがあります。 Lua 5.2+では、 table.pack()
がこの欠点を処理するために導入されました(純粋なLuaでこの関数を実装する関数には関数があります)。
慣用的な使用
可変長はその長さを持ち歩くので、テーブルの穴に問題が発生するのを避けるために、それらをシーケンスとして使用します。これは、意図された使用法ではなく、Luaのリファレンス実装が最適化しないものでした。このような使用法は、例で検討されていますが、一般的に眉をひそめます。
基本
バリアント関数は、関数定義の引数リストに...
省略記号構文を使用して作成されます。
function id(...)
return
end
あなたはこの関数を呼び出した場合はid(1, 2, 3, 4, 5)
そして...
(AKA可変引数リスト)が値を含んでいます1, 2, 3, 4, 5
。
関数は必要な引数を取ることができ...
。
function head(x, ...)
return x
end
varargリストから要素を引き出す最も簡単な方法は、単に変数を変数に割り当てることです。
function head3(...)
local a, b, c = ...
return a, b, c
end
select()
も、要素の数を見つけてから要素を抽出するために使用することができる...
間接。
function my_print(...)
for i = 1, select('#', ...) do
io.write(tostring(select(i, ...)) .. '\t')
end
io.write '\n'
end
...
{...}
を使用することで、使い易さのためにテーブルにパックすることができます。これにより、すべての引数がテーブルの順次部分に配置されます。
table.pack(...)
は、varargリストをテーブルにパックするためにも使用できます。 table.pack(...)
の利点は、返されるテーブルのn
フィールドをselect('#', ...)
の値にselect('#', ...)
。引数リストにnilsが含まれている場合は、これは重要です(後述の「備考」を参照)。
function my_tablepack(...)
local t = {...}
t.n = select('#', ...)
return t
end
関数からvarargリストを返すこともできます。その結果、複数の返品が行われます。
function all_or_none(...)
local t = table.pack(...)
for i = 1, t.n do
if not t[i] then
return -- return none
end
end
return ... -- return all
end
高度な使用法
基本的な例で述べたように、変数のバウンド引数と可変引数リスト( ...
)を持つことができます。このファクトを使用して、他の言語(Haskellなど)と同じようにリストを再帰的に分離することができます。以下はfoldr()
実装で、これを利用しています。各再帰呼び出しは、varargリストの先頭をx
にバインドし、残りのリストを再帰呼び出しに渡します。これにより、引数が1つしかなくなるまでリストが破壊されます( select('#', ...) == 0
)。その後、以前に計算された結果で各値が関数引数f
適用されます。
function foldr(f, ...)
if select('#', ...) < 2 then return ... end
local function helper(x, ...)
if select('#', ...) == 0 then
return x
end
return f(x, helper(...))
end
return helper(...)
end
function sum(a, b)
return a + b
end
foldr(sum, 1, 2, 3, 4)
--> 10
このプログラミングスタイルを活用する他の関数定義は、第3号から第8号までここで見つけることができます。
ルアの唯一の慣用的なデータ構造がテーブルです。配列のどこにもnil
がない場合、テーブル長演算子は未定義です。テーブルとは異なり、varargリストは、基本的な例と備考のセクションで明示されているように、明示的なnil
尊重します(まだ読んでいなければ、そのセクションを読んでください)。少しの作業でも、varargリストは、突然変異のほかにテーブルができるすべての操作を実行できます。これにより、varargリストは不変のタプルを実装するのに適しています。
function tuple(...)
-- packages a vararg list into an easily passable value
local co = coroutine.wrap(function(...)
coroutine.yield()
while true do
coroutine.yield(...)
end
end)
co(...)
return co
end
local t = tuple((function() return 1, 2, nil, 4, 5 end)())
print(t()) --> 1 2 nil 4 5 | easily unpack for multiple args
local a, b, d = t() --> a = 1, b = 2, c = nil | destructure the tuple
print((select(4, t()))) --> 4 | index the tuple
print(select('#', t())) --> 5 | find the tuple arity (nil respecting)
local function change_index(tpl, i, v)
-- sets a value at an index in a tuple (non-mutating)
local function helper(n, x, ...)
if select('#', ...) == 0 then
if n == i then
return v
else
return x
end
else
if n == i then
return v, helper(n+1, ...)
else
return x, helper(n+1, ...)
end
end
end
return tuple(helper(1, tpl()))
end
local n = change_index(t, 3, 3)
print(t()) --> 1 2 nil 4 5
print(n()) --> 1 2 3 4 5
上記とテーブルの主な違いは、テーブルは変更可能であり、ポインタのセマンティクスがあることです。タプルにはこれらのプロパティがありません。さらに、タプルは、明示的なnil
を保持でき、決して定義されていない長さの操作を持つことができます。