2

My question is on functions like pairs(). Functions like this return multiple values like this:

function foo()
     x = bar()
     y = x*2
     return x, y
end

x, y = foo()

Is there a way to get just the second value without bothering with the first? Why would this be preferable to simply returning a table?

It seemed to me like this question probably already existed on this site, but I couldn't find it. Sorry if it's a duplicate.

  • You were right: [Getting multiple values from a function without creating a variables in LUA](https://stackoverflow.com/questions/59379596/getting-multiple-values-from-a-function-without-creating-a-variables-in-lua) the context and naming is slightly different, probably that's why you didn't find it. – Aki Jul 23 '20 at 09:44

3 Answers3

10

Using Underscores

It is idiomatic in Lua to discard unwanted values by using an underscore:

_, y = foo()

When you encounter something like x, y = foo() in source code, you expect that both x and y will be used, but the underscore convention communicates the intent that the value assigned to _ is not needed and will not be used.

This is the method that seems most common, and is most used in Programming in Lua. This idiom shows up frequently in loops using iterators. For example, the ipairs iterator function returns both an index and a value, but you may only be interested in the value. The usual idiom for this is:

for _, v in ipairs(t) do
   -- some stuff using v
end

Using select

The select function takes an initial argument which may be a number or the string "#", and an arbitrary number of additional arguments. When the initial argument is a number N, all additional arguments starting from the Nth are returned. Since Lua discards unassigned values, this can be used to select a single argument:

y = select(2, foo())

OP example function returns two values, but for functions that return more than two values:

y, z = select(2, bar())

and

_, y, z = bar()

both assign the second and third return values of bar to y and z, respectively.

The underscore method is less verbose, and more clear in my opinion. Using select also adds the overhead of an additional function call. There are cases in which using select makes more sense, e.g., when you want to programmatically select a return value. Which method you choose is largely a matter of taste.

Wrapping Return Values in a Table

Sometimes it makes sense to wrap multiple return values in a table, but as a general rule this is a bad idea. Wrapping values in a table inside of the function means that a new table must be created, and this takes time; in a loop this can mean significantly slower code.

You might have a case where it helps clarify your code to return a table, e.g., a function stats that returns the mean, median, and mode of some data. In this case you might want to collect those statistics in a table instead of returning them as separate values.

If the values returned from a function make sense as a table, return a table.

Final Thoughts

Use the method that makes your code the most clear. For my money, the underscore method is the default way to do this. If you have a good reason to use one of the other approaches, follow your bliss.

ad absurdum
  • 19,498
  • 5
  • 37
  • 60
  • 1
    Note that you can also do `_x, y = foo()`; the `_` indicates that `_x` is unused, but you're still giving it a meaningful name so the user knows *what* it is you're discarding. – DarkWiiPlayer Jul 23 '20 at 07:11
6

You can use the builtin select-Function for that:

function test()
    return "Hello", "World"
end

local x = select(2, test())
print(x) -- prints World

This is also shown in the documentation

Felix
  • 2,256
  • 2
  • 15
  • 35
1

Hello

I like to return a table for multiple results. Like debug.getinfo() or other functions.

several_results=function() return {os.date(), os.time()} end

tab=several_results()

print(#tab) -- How many keys?

print(tab[1], tab[2]) -- For both values

print(tab[2]) -- Get value only from second key

print(tab[#tab]) -- For the value of last key

-- And a simple loop...
for incr=1,#tab do print(tab[incr]) end

But you have to care about the Len ( # ) of a table that depends on the content. Keys that are named and can be accessed with a dot are not countable.

tab={}
tab['name']='Arthur Dent'
print(#tab, tab.name)
-- puts out: 0 Arthur Dent
-- Same with Zero and negative numbers
tab={[0]=0,[-1]=-1}
print(#tab, tab[0], tab[-1])
-- puts out: 0 0 -1
koyaanisqatsi
  • 2,585
  • 2
  • 8
  • 15