idiomatic table iteration

classic Classic list List threaded Threaded
11 messages Options
Reply | Threaded
Open this post in threaded view
|

idiomatic table iteration

Patrick Donnelly
So as far as I've seen, most C code uses the Lua manual's example code
structure to iterate tables. Namely:

lua_pushnil(L);
while (lua_next(L, idx) != 0) {
  /* code here */
  lua_pop(L, 1); /* pop value */
}

This structure goes back to the Lua 4.0 manual [1].

I noticed that the C for loop results in a more natural construction:

for (lua_pushnil(L); lua_next(L, idx); lua_pop(L, 1)) {
   /* code here always has the key/value pair on the stack */
}

One of the advantages I see is that the for loop code block must
follow good stack discipline (not leaving anything on the stack or
incidentally removing the value) because the lua_pop must always pop
the value. For example, this code is not very flexible to future
changes:

lua_pushnil(L);
while (lua_next(L, idx)) {
  lua_pushboolean(L, 1);
  lua_rawset(L, someidx); /* value removed by rawset */
}

So does anyone use the for loop construct? I appreciate the manual
version is more clear in how it works but it is taken as the idiomatic
way to iterate tables. (In fact, the Lua source code uses that style
in the base libraries.)

[1] https://www.lua.org/manual/4.0/manual.html#5.12

--
Patrick Donnelly

Reply | Threaded
Open this post in threaded view
|

Re: idiomatic table iteration

Daurnimator
On 25 August 2016 at 10:56, Patrick Donnelly <[hidden email]> wrote:

> So as far as I've seen, most C code uses the Lua manual's example code
> structure to iterate tables. Namely:
>
> lua_pushnil(L);
> while (lua_next(L, idx) != 0) {
>   /* code here */
>   lua_pop(L, 1); /* pop value */
> }
>
> This structure goes back to the Lua 4.0 manual [1].
>
> I noticed that the C for loop results in a more natural construction:
>
> for (lua_pushnil(L); lua_next(L, idx); lua_pop(L, 1)) {
>    /* code here always has the key/value pair on the stack */
> }
>
> One of the advantages I see is that the for loop code block must
> follow good stack discipline (not leaving anything on the stack or
> incidentally removing the value) because the lua_pop must always pop
> the value. For example, this code is not very flexible to future
> changes:
>
> lua_pushnil(L);
> while (lua_next(L, idx)) {
>   lua_pushboolean(L, 1);
>   lua_rawset(L, someidx); /* value removed by rawset */
> }
>
> So does anyone use the for loop construct? I appreciate the manual
> version is more clear in how it works but it is taken as the idiomatic
> way to iterate tables. (In fact, the Lua source code uses that style
> in the base libraries.)
>
> [1] https://www.lua.org/manual/4.0/manual.html#5.12
>
> --
> Patrick Donnelly
>

Neither of those styles take into account __pairs. If we want to
spread new idioms, I suggest we pick one that will respect __pairs.

That said, lua_pop is really just lua_gettop + lua_settop. To reduce
the need for stack discipline we can just use settop instead, perhaps
something like:

for (int base_idx = (lua_pushnil(L), lua_gettop(L)); lua_next(L, idx);
lua_settop(L, baseidx))

I'm not sure if this is correct (will C evaluate the pushnil/gettop in
a defined order?).

Reply | Threaded
Open this post in threaded view
|

Re: idiomatic table iteration

Coda Highland
On Wed, Aug 24, 2016 at 6:07 PM, Daurnimator <[hidden email]> wrote:

> On 25 August 2016 at 10:56, Patrick Donnelly <[hidden email]> wrote:
>> So as far as I've seen, most C code uses the Lua manual's example code
>> structure to iterate tables. Namely:
>>
>> lua_pushnil(L);
>> while (lua_next(L, idx) != 0) {
>>   /* code here */
>>   lua_pop(L, 1); /* pop value */
>> }
>>
>> This structure goes back to the Lua 4.0 manual [1].
>>
>> I noticed that the C for loop results in a more natural construction:
>>
>> for (lua_pushnil(L); lua_next(L, idx); lua_pop(L, 1)) {
>>    /* code here always has the key/value pair on the stack */
>> }
>>
>> One of the advantages I see is that the for loop code block must
>> follow good stack discipline (not leaving anything on the stack or
>> incidentally removing the value) because the lua_pop must always pop
>> the value. For example, this code is not very flexible to future
>> changes:
>>
>> lua_pushnil(L);
>> while (lua_next(L, idx)) {
>>   lua_pushboolean(L, 1);
>>   lua_rawset(L, someidx); /* value removed by rawset */
>> }
>>
>> So does anyone use the for loop construct? I appreciate the manual
>> version is more clear in how it works but it is taken as the idiomatic
>> way to iterate tables. (In fact, the Lua source code uses that style
>> in the base libraries.)
>>
>> [1] https://www.lua.org/manual/4.0/manual.html#5.12
>>
>> --
>> Patrick Donnelly
>>
>
> Neither of those styles take into account __pairs. If we want to
> spread new idioms, I suggest we pick one that will respect __pairs.
>
> That said, lua_pop is really just lua_gettop + lua_settop. To reduce
> the need for stack discipline we can just use settop instead, perhaps
> something like:
>
> for (int base_idx = (lua_pushnil(L), lua_gettop(L)); lua_next(L, idx);
> lua_settop(L, baseidx))
>
> I'm not sure if this is correct (will C evaluate the pushnil/gettop in
> a defined order?).
>

Yes, the comma operator defines a sequence point, so it's guaranteed
to evaluate left-to-right. (The syntactic commas in function lists,
however, do not define sequence points, IIRC.)

/s/ Adam

Reply | Threaded
Open this post in threaded view
|

Re: idiomatic table iteration

Viacheslav Usov
In reply to this post by Patrick Donnelly
On Thu, Aug 25, 2016 at 2:56 AM, Patrick Donnelly <[hidden email]> wrote:

> One of the advantages I see is that the for loop code block must follow good stack discipline

In a particular project I deal with, I'd say that half the table iteration code is complicated, with stack use heavily commented. And many of those complicated cases have nothing to pop at the end of the iteration, so the for-form would just look strange.

I'd say the while-from is idiomatic, and the consistency of its use makes those complicated cases easier to understand.

Cheers,
V.
Reply | Threaded
Open this post in threaded view
|

Re: idiomatic table iteration

Patrick Donnelly
In reply to this post by Daurnimator
On Wed, Aug 24, 2016 at 9:07 PM, Daurnimator <[hidden email]> wrote:
> Neither of those styles take into account __pairs. If we want to
> spread new idioms, I suggest we pick one that will respect __pairs.

Sure, but it's still good to talk about iteration with plain lua_next :)

> That said, lua_pop is really just lua_gettop + lua_settop. To reduce
> the need for stack discipline we can just use settop instead, perhaps
> something like:
>
> for (int base_idx = (lua_pushnil(L), lua_gettop(L)); lua_next(L, idx);
> lua_settop(L, baseidx))

Eh... that's not ANSI C and I still don't like that it encourages
sloppy stack discipline.

--
Patrick Donnelly

Reply | Threaded
Open this post in threaded view
|

Re: idiomatic table iteration

Chris Emerson
On Fri, Aug 26, 2016 at 10:01:38AM -0400, Patrick Donnelly wrote:
> On Wed, Aug 24, 2016 at 9:07 PM, Daurnimator <[hidden email]> wrote:
> > for (int base_idx = (lua_pushnil(L), lua_gettop(L)); lua_next(L, idx);
> > lua_settop(L, baseidx))
>
> Eh... that's not ANSI C and I still don't like that it encourages
> sloppy stack discipline.

What's not ANSI C about that?

Chris

Reply | Threaded
Open this post in threaded view
|

Re: idiomatic table iteration

Guilherme Salazar
On Fri, Aug 26, 2016 at 11:23 AM, Chris Emerson
<[hidden email]> wrote:
> What's not ANSI C about that?


The first clause of the 'for' cannot be a declaration.

Reply | Threaded
Open this post in threaded view
|

Re: idiomatic table iteration

Chris Emerson
On Fri, Aug 26, 2016 at 11:47:05AM -0300, Guilherme Salazar wrote:
> On Fri, Aug 26, 2016 at 11:23 AM, Chris Emerson
> <[hidden email]> wrote:
> > What's not ANSI C about that?
>
> The first clause of the 'for' cannot be a declaration.

It can since at least C99.

Chris

Reply | Threaded
Open this post in threaded view
|

Re: idiomatic table iteration

Guilherme Salazar
On Fri, Aug 26, 2016 at 12:01 PM, Chris Emerson
<[hidden email]> wrote:
> It can since at least C99.

"ANSI C" usually refers to the original version of the standard (C89).

Reply | Threaded
Open this post in threaded view
|

Re: idiomatic table iteration

Chris Emerson
On Fri, Aug 26, 2016 at 12:03:15PM -0300, Guilherme Salazar wrote:
> On Fri, Aug 26, 2016 at 12:01 PM, Chris Emerson
> <[hidden email]> wrote:
> > It can since at least C99.
>
> "ANSI C" usually refers to the original version of the standard (C89).

Ok, I guess it depends on context - I've always taken "ANSI C" to mean
"post K&R C", but I accept that there is more than one interpretation in
use.

According to Wikipedia (https://en.wikipedia.org/wiki/ANSI_C), ANSI have
withdrawn everything before C11.  I was unable to navigate ANSI's own site
to verify this.  :-)

Regards,

Chris


Reply | Threaded
Open this post in threaded view
|

Re: idiomatic table iteration

Ką Mykolas
According to the same Wiki article:
> "Historically, the names referred specifically to the original and best-supported version of the standard (known as C89 or C90). " ;)

On Fri, Aug 26, 2016 at 7:00 PM, Chris Emerson <[hidden email]> wrote:
On Fri, Aug 26, 2016 at 12:03:15PM -0300, Guilherme Salazar wrote:
> On Fri, Aug 26, 2016 at 12:01 PM, Chris Emerson
> <[hidden email]> wrote:
> > It can since at least C99.
>
> "ANSI C" usually refers to the original version of the standard (C89).
 
According to Wikipedia (https://en.wikipedia.org/wiki/ANSI_C), ANSI have
withdrawn everything before C11.  I was unable to navigate ANSI's own site
to verify this.  :-)