next

さて、あと ltable.c で見ていない関数は luaH_next だ。これは lua の next 関数に対応する(APIlua_next はけっきょく luaH_next を呼んでいる)。 next は、引数としてテーブルとインデックスを取る。インデックスを省略すると「最初の」キーと値のペアを返す。インデックスを与えると、そのインデックスをキーとして、その「次の」キーと値のペアを返す。以上を前提として、 luaH_next は次のようになる。

static int luaH_index (lua_State *L, Table *t, StkId key) {
  int i;
  if (ttisnil(key)) return -1;  /* first iteration */
  i = arrayindex(key);
  if (0 <= i && i <= t->sizearray) {  /* is `key' inside array part? */
    return i-1;  /* yes; that's the index (corrected to C) */
  }
  else {
    const TObject *v = luaH_get(t, key);
    if (v == &luaO_nilobject)
      luaG_runerror(L, "invalid key for `next'");
    i = cast(int, (cast(const lu_byte *, v) -
                   cast(const lu_byte *, gval(gnode(t, 0)))) / sizeof(Node));
    return i + t->sizearray;  /* hash elements are numbered after array ones */
  }
}

int luaH_next (lua_State *L, Table *t, StkId key) {
  int i = luaH_index(L, t, key);  /* find original element */
  for (i++; i < t->sizearray; i++) {  /* try first array part */
    if (!ttisnil(&t->array[i])) {  /* a non-nil value? */
      setnvalue(key, cast(lua_Number, i+1));
      setobj2s(key+1, &t->array[i]);
      return 1;
    }
  }
  for (i -= t->sizearray; i < sizenode(t); i++) {  /* then hash part */
    if (!ttisnil(gval(gnode(t, i)))) {  /* a non-nil value? */
      setobj2s(key, gkey(gnode(t, i)));
      setobj2s(key+1, gval(gnode(t, i)));
      return 1;
    }
  }
  return 0;  /* no more elements */
}

引数として与えられた key は luaH_index でインデックスを計算される。インデックスが省略されるときには key は nil になっていて、そのときには -1 が返る。あとは配列要素ならそのインデックスが、配列の範囲外であったりそもそも数値じゃなければハッシュ部のインデックスが返される。ここでハッシュ部か配列部かを区別するために sizearray が足されている。

luaH_next では、この結果を受けて、配列部のとき(-1またはsizearrayより小さいとき)には配列パートを調べて最小のものを返す。見つからなければ次はハッシュパートを小さい方から順番に辿っていって、一番小さい「キーのあるノード」を発見したらそれを返すようにしている。値の返し方はあとで、たぶん ldo か lvm を調べるときにわかるようになると思う。