robm
robm

Reputation: 1393

lua test and assign inside loop control

Is there / please suggest a syntax to achieve a compact 'test and assign' in lua?

Consider this segment from luasql examples ( http://keplerproject.org/luasql/examples.html )

-- retrieve a cursor
cur = assert (con:execute"SELECT name, email from people")
-- print all rows, the rows will be indexed by field names
row = cur:fetch ({}, "a")
while row do
  print(string.format("Name: %s, E-mail: %s", row.name, row.email))
  -- reusing the table of results
  row = cur:fetch (row, "a")
end

I am learning lua, and really struggling to accept the duplicated call to cur:fetch(). I see that repeat/until trivially fixes the issue, but then it seems I have to test twice:

repeat
  row = cur:fetch ({}, "a")
  if row then
    print ...
  end
until nil == row

I consider this less error prone for the case of 'row = ...' getting more complex, but still seems inelegant.

Upvotes: 3

Views: 1267

Answers (3)

kikito
kikito

Reputation: 52621

You can simplify the first loop to this:

local row = {}
while row do
  row = cur:fetch(row, "a")
end

EDIT: The sample page hits at a possible solution using iterators and a for loop, like @doukremt is suggesting. Unfortunately it also contains a couple errors (it assumes that the rows get "unpacked" in the second example but not in the first, does not declare everything local, and omits more parenthesis than it's sane to in a sample code). Here's how you can transform a cursor in an iterator:

local iterate = function(cur)
  return function() return cur:fetch() end
end

Here's how you use it:

local cur = assert(con:execute("SELECT name, email from people"))

for row in iterate(cur) do
  print(string.format ("%s: %s", row.name, row.email))
end

I must warn you that iterators are in general more expensive than while/repeat loops. Execute performance tests if you are worried about speed.

Upvotes: 4

lhf
lhf

Reputation: 72312

Try

while true do
  local row = cur:fetch ({}, "a")
  if row then
    print ...
  else
    break
  end
end

Upvotes: 3

michaelmeyer
michaelmeyer

Reputation: 8205

Just use a for loop:

row = {}
for row  in cur:fetch(row, "a") do
    -- ...
end

The call to fetch() returns a pointer to the table you pass as argument, so you could as well do:

row = {}
for foobar in cur:fetch(row, "a") ...

The important point is that fetch() returns nil when there are no more rows, so you just need to check for that, which a for loop does implicitly.

Upvotes: 3

Related Questions