lua unpackトラップ


例を見てみましょう.
local ary_with_hole = {1, 3, 5, 7, 9}
print(#ary_with_hole) -- => 5,       

ary_with_hole[4] = nil
print(#ary_with_hole) -- => 3,   4?!

ary_with_hole[2] = nil
print(#ary_with_hole) -- => 1,    !

ary_with_hole[1] = nil
print(#ary_with_hole) -- => 0,      0  ?

Lua内部実装の詳細を参照すると、伝達された配列にnil値の空洞がある場合、#オペレータが返す数値は実際のサイズを反映しません.
Lua 5.1 manualの説を直接引用する(Lua 5.2とLuaJITも同様の定義):
https://www.lua.org/manual/5.... The Length Operator
The length of a table t is defined to be any integer index n such that t[n] is not nil and t[n+1] is nil; moreover, if t[1] is nil, n can be zero. ...If the array has "holes"(that is, nil values between other non-nil values), then #t can be any of the indices that directly precedes a nil value (that is, it may consider any such nil value as the end of the array).
もし私にドキュメントを書かせてくれれば、私は必ず上の段落を太くして、大きい番号を調整して、赤をマークします.これは本当に穴があいているからです.
簡単に言えば、Luaのtableの長さの定義は他の言語とは違います.tableの長さは、最初の値がnilの整数キーとして定義される(通常のように要素の数に等価ではない).array-like tableに空洞がある場合、任意のnil値の前のインデックスは#オペレータが返す値である可能性があります.
例では、5 3 1 0のような減少があるのは、Luaがnil値を探す際に、概ね後から半分ずつ探す方式を採用しているからである.nil値の位置を変更すると、表示される結果は大きく異なります.要するに、nil付き配列の「長さ」は、中の要素の数を反映するものではありません.
数日前、プロジェクトでオンライン火災を救う必要がある問題が発生しました.追跡されたのはunpackのnil配列が原因です.unpack(table)が返す値はtable[1], ..., table[#table]です.#tableで返される値が実際の要素の数に等しくない場合、unpackで返される値は遮断されます.これは明らかに予想外の結果をもたらす.
バンドnil配列の長さによって影響されるのは、#およびunpackの動作に加えて、table.getnipairsなどを含む.
解決方法は2つあります.
  • nilを有する可能性のある配列の処理方法を変更する.このような配列を,キーがちょうど整数のrecord−like tableとして扱う.例えばipairではなくpairを用いて反復します.このようにするには制限がある.Luaは動的言語であり、タイプタグなどの構文がないため、どの関数が「nil付きの可能性のある配列」を返しているのかを見分けるには、注釈に明記する以外に方法がないようです.実際、プロジェクトのベテラン運転手がcode reviewをしない限り、内情を知らない新人が穴を開けるのは避けられない.
  • はNullObject patternを採用し、空のデータについてはnilではなく、他の約束俗成、場所に応じて適切な空の値を採用する.ここにはfalseまたはngxの2つの適切な候補がある.null.前者はnilと同じように偽の値です.したがって、呼び出しコードは、変動することなくres or default_valueのような方法を維持することができる.falseも可能な戻り値である場合、ngxを用いることができる.null.でもngxに注意してください.nullは真の値であり、呼び出しコードは、返される値がngx.nullに等しいかどうかを判断し、返される値が空であるかどうかを判断する必要がある.