82

I've some currently some Lua code using the following syntax:

if (foo == nil or foo == '') then
    foo = "some default value"
end

The goal of the if condition is to test foo is neither an empty string, neither a nil value.

Can this code be simplified in one if test instead two?

greatwolf
  • 20,287
  • 13
  • 71
  • 105
Uskiver
  • 845
  • 1
  • 6
  • 6
  • So, what are You testing exactly foo, bar or foo.bar? If You'd have `foo.bar`, You could have some function like `local function test(foo, bar) { if (foo.bar == nil or foo.bar == '') then foo.bar = "some default value" end end` which would do the trick. – Kamiccolo Oct 29 '13 at 17:17
  • foo = foo or "some default value" – doog abides Oct 29 '13 at 17:20
  • Assuming you don't want foo of 'false' to count either you could use `if (not foo) or (foo == '') then ...` but otherwise that's as short as it gets. – Etan Reisner Oct 29 '13 at 17:22
  • 2
    @doog abides, won't work. In Lua - `'' == true`, `nil == false`. – Kamiccolo Oct 29 '13 at 17:22
  • 9
    `foo = (foo or '')=='' and 'default value' or foo` – Egor Skriptunoff Oct 29 '13 at 17:41
  • @EgorSkriptunoff yeah, that works.. (bit horrible though) – doog abides Oct 29 '13 at 17:49
  • (Sorry Kamiccolo to have let an extraneous .bar, I edited the question to remove this artefact. Previous version used foo.bar everywhere, but I wanted to simplify the code sample.) – Uskiver Oct 29 '13 at 18:03
  • @EgorSkriptunoff It answers the question, but I bet the OP intent was for something more concise ie compact yet readable/expressive, not the opposite :) – Oliver Oct 30 '13 at 04:16

2 Answers2

98

One simple thing you could do is abstract the test inside a function.

local function isempty(s)
  return s == nil or s == ''
end

if isempty(foo) then
  foo = "default value"
end
hugomg
  • 68,213
  • 24
  • 160
  • 246
23

Can this code be simplified in one if test instead two?

nil and '' are different values. If you need to test that s is neither, IMO you should just compare against both, because it makes your intent the most clear.

That and a few alternatives, with their generated bytecode:

if not foo or foo == '' then end
     GETGLOBAL       0 -1    ; foo
     TEST            0 0 0
     JMP             3       ; to 7
     GETGLOBAL       0 -1    ; foo
     EQ              0 0 -2  ; - ""
     JMP             0       ; to 7

if foo == nil or foo == '' then end
    GETGLOBAL       0 -1    ; foo
    EQ              1 0 -2  ; - nil
    JMP             3       ; to 7
    GETGLOBAL       0 -1    ; foo
    EQ              0 0 -3  ; - ""
    JMP             0       ; to 7

if (foo or '') == '' then end
   GETGLOBAL       0 -1    ; foo
   TEST            0 0 1
   JMP             1       ; to 5
   LOADK           0 -2    ; ""
   EQ              0 0 -2  ; - ""
   JMP             0       ; to 7

The second is fastest in Lua 5.1 and 5.2 (on my machine anyway), but difference is tiny. I'd go with the first for clarity's sake.

Mud
  • 28,277
  • 11
  • 59
  • 92
  • 5
    The first and third one do not do the same thing as the second one when `foo` is `false`. – catwell Oct 30 '13 at 09:49
  • how about using `#foo == 0` to check for empty string instead of `foo == ''` - does it have a chance of being faster? – Nas Banov Dec 10 '15 at 03:52
  • 3
    That's not a bad idea. Lua strings maintain their length internally, so you wouldn't have to count characters. But Lua also interns strings, so checking for equality doesn't require looking at characters, either; you just check if the objects are the same. So both *should* be fast, but it turns out the code path to retrieve the string length is slightly longer than the code path to get the two object pointers off the VM stack to compare them, so `#foo == 0` is slower than `foo == ''` by a little in 5.1 and a lot in 5.2 and 5.3 (as determined by benchmarking on my machine). – Mud Dec 10 '15 at 07:30
  • @NasBanov, There are a few additional issues with the # operator. For once is it defined for tables as well, so for example `#{}==0`, `#{[2]='foo'}==0`, `#{a='b'}==0`. Then `#42` would raise an error; a behaviour probably not desired... Regarding the speed, the # operator could be overriden (in lua 5.2+) with some slow implementation as well, eg: `#setmetatable({}, {__len=function()require'socket'.sleep(3)return 7 end})`; and finally you'd probably be surprised, but there's no guaranteed order for the default # implementation for tables (it could even be O(n) on some cases). – fcr May 05 '17 at 00:44
  • @fcr - thank you, that's food for thought - though i think when i asked that i was implying knowing the value at hand to be a string, i.e. `#str_foo` – Nas Banov May 06 '17 at 00:05