Hacker News new | past | comments | ask | show | jobs | submit login

This is a refreshing update for those of us that like having code which will always act in the same way across multiple invocations.

Now, if only Lua could follow the same path with their “tables” (“tables” is what Lua programmers call their form of Python’s “dictionaries” and Perl’s “hashes”).

I just spent eight hours earlier this week debugging Lua code which would run differently on different invocations of the same code.

The standard way to iterate in a table with Lua is like this:

  for key, value in pairs(foo) do
One problem: The order we get elements from the table “foo” is undefined, and it can change between different invocations of the same Lua code, even if the elements were put in the table in the same order. In order to fix things so that we can iterate a table in a consistent manner, this is my fix (public domain [1], if those who want to copy and paste it):

  function sorted_table_keys(t)
    local a = {}
    local b = 1
    for k,_ in pairs(t) do -- pairs() use OK; will sort 
      a[b] = k
      b = b + 1
    end
    table.sort(a, function(y,z) return tostring(y) < tostring(z) end)
    return a
  end
Then we iterate the table like this:

  for _, key in ipairs(sorted_table_keys(foo)) do
    local value = foo[key]
(In Lua, two dashes indicates a comment.)

Note that this code will not always sort in the same order all tables. If we have a table with the keys 1 (as a number) and "1" (as a string), iteration order is still undefined.

The code is open source, and is a procedural (“random”) map generator for Doom written mainly by Andrew Apted which I have added some features and fixed some bugs with. It’s here: https://github.com/samboy/ObHack and the issue is here: https://github.com/samboy/ObHack/issues/4

[1] The project I added this code to is GPL, but this function, which I wrote entirely by myself, is one I am donating to the public domain.




You can write an iterator yourself, no need to leave ?pairs idiom:

  function sortpairs(t)
    local keys = { }
    for key in pairs(t) do
      table.insert(keys, key)
    end
    table.sort(keys, function (a, b)
      return tostring(a) < tostring(b)
    end)
    local i = 1
    return function (t)
      local key = keys[i]
      i = i + 1
      if key ~= nil then
        return key, t[key], i-1
      end
    end, t
  end

  t = {a=10, c=20, d=30, b=40, 50}
  for k,v,i in sortpairs(t) do
    print(k, v, i)
  end


What Lua is lacking here (and why the above iterator function needs 17 lines) is the ability to have “for” go through a list (without converting the list in to values returned by an iterator function), which would let us quickly and easily sort lists that “for” can use. Something like:

  d = {"foo": 2, "bar": 1, "zoo": 4}
  for k in sorted(d.keys()):
    print k
(I’m not advocating Python here, since Perl has a similar way of using “for” to go through lists which can also be easily sorted)

However, with Lua, “for” only accepts a numeric range, or an iterator function, so customizing “for” requires understanding function closures: Understanding how a function, when called multiple times, stores variables altered in previous invocations of the function, and understanding how to give those variables initial values (usually in the “function factory” function which creates the function we use).

In other words, “for”, in most modern high-level languages, can be one of:

1. for variable in [something that specifies a numeric range]

2. for variable in [iterator function]

3. for variable in [list]

But Lua only has “something that specifies a numeric range” and “iterator function”; it can not natively go through a list.


You can convert a list first and then feed it to a simple iterator. I don’t fully understand what your exact real-code issues can be, but hope this snippet may help:

  function vs(t)
    local i = 0
    return function (t)
      i = i + 1
      return t[i]
    end, t
  end

  function sorted(t, cmp)
    table.sort(t, cmp or function (a, b)
      return tostring(a) < tostring(b)
    end)
    return t
  end

  function keys(t)
    local keys = { }
    for key in pairs(t) do
      table.insert(keys, key)
    end
    return keys
  end

  t = {a=10, c=20, d=30, b=40, 50}
  for k in vs(sorted(keys(t))) do
    print(k, t[k])
  end
I.e. if “natively” means strictly “for in t” that generates values, then no, Lua can’t do that. But if “for in vs(t)” is okay, then that vs() is the solution.


That looks good, and I think putting these in a prominent place of the Lua documentation (along with a notice that the code is public domain) would help us who are used to the AWK/Perl/Python/PHP way of having “for” natively traverse a list without needing a complicated list-to-iterator function that uses function closure (i.e. the iterator function remembers the value “i” -- I’m writing this for the lurkers because code like this can be difficult to follow).

One honest question: Is there any reason why the function factory (i.e. a function which returns a function) which converts a list (Actually, table with ascending integer indexes) in to an iterator Lua can use with “for” returns both the element and the entire table here? Here is the code I am asking about:

    return function (t)
      i = i + 1
      return t[i]
    end, t
I’m curious why we’re returning both the table element for the iterator and the entire table.




Applications are open for YC Summer 2020

Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact

Search: