Is there an alternative to an if statement in Lua? - if-statement

I would like to know if there is a alternative to using a normal if statement in lua. For example, in java there are switch statements but I cant seem to find that in Lua

Lua lacks a C-style switch statement.
A simple version of a switch statement can be implemented using a table to map the case value to an action. This is very efficient in Lua since tables are hashed by key value which avoids repetitive if then ... elseif ... end statements.
action = {
[1] = function (x) print(1) end,
[2] = function (x) z = 5 end,
["nop"] = function (x) print(math.random()) end,
["my name"] = function (x) print("fred") end,
}

The frequently used pattern
local var; if condition then var = x else var = y end
can be shortened using an and-or "ternary" substitute if x is truthy:
local var = condition and x or y

if test == nil or test == false then return 0xBADEAFFE else return test end
Can be shorten up to...
return test or 0xBADEAFFEE
This works even where you dont can do: if ... then ... else ... end
Like in a function...
print(test or 0xBADEAFFE)
-- Output: 3135156222
...or fallback to a default if an argument is ommited...
function check(test)
local test = test or 0xBADEAFFE
return test
end
print(check())
-- Returns: 3135156222

Related

shortcut of multi-conditions with and in AL and Business Central?

Suppose we've procedure is_even return if the number given is even or not!
that's a good code:
if is_even(22) = true or test(1) = true or test(2) = true then
//...
And even that's a better:
if true in [ is_even(22), is_even(1), is_even(2) ] then
//..
But what if I would do that but with logic(and not or) like that
if is_even(22) = true and test(1) = true and test(2) = true then
//...
Better code would be?
I've tried to do that, So I think of do that with a function like in python all:
local procedure all(array_bools : ARRAY [3] OF Boolean): Boolean
var
bool: Boolean;
i: Integer;
begin
REPEAT
bool := array_bools[i];
if bool = false then
exit(false);
i := i + 1;
UNTIL i <> ARRAYLEN(array_bools);
exit(false);
end;
and it is not working as I expect
if all([is_even(22), is_even(1), is_even(2)]) = true then
I like to keep it simple and readable. I think your code is hard to read because you combine all these checks in to one. Instead i would write something like this:
local procedure CheckMyConditions(): Boolean
begin
if is_even(22) then
exit(true);
if test(1) then
exit(true);
if test(2) then
exit(true);
exit(false);
end;
Also this would perform better, because AL does not support lazy evaluation. This means every statement between "if" and "then" will be evaluated.
The best for this cae ist here:
if is_even(22) or test(1) or test(2) then

Resetting package.loaded after a test in Lua

I am developing unit tests in Lua and a major part of these tests is changing package.loaded. This goes from
a = require "parser"
a.b = nil
to
package.loaded["checker"] = function() return true end
And the situation where I modify an entry of package.loaded that was loaded before the test started.
I would like to reset package.loaded after every test. But I have a hard time. I tried wrapping require, which solves some problems, but does not solve the problem of the second and the third example. How can I properly make a savepoint of package.loaded right before the tests and reload that savepoint just before I start a new test? Or just roll back package.loaded to the one just after the interpeter started?
This is actually the answer of #EgorSkriptunoff, but he placed it as a comment.
For me, it was good enough to just make a shallow-copy of bot the package.loaded and the _G. Shallow-copy can be found here: http://lua-users.org/wiki/CopyTable:
function shallowcopy(orig)
local orig_type = type(orig)
local copy
if orig_type == 'table' then
copy = {}
for orig_key, orig_value in pairs(orig) do
copy[orig_key] = orig_value
end
else -- number, string, boolean, etc
copy = orig
end
return copy
end
The actual testing then looks like this:
local packageLoadedCopy = shallowcopy(package.loaded)
local gCopy = shallowcopy(_G)
for _, test in ipairs(tests) do
-- Do the actual test
-- Clean up the environment
local markDeletion = {}
local markModify = {}
for name in pairs(package.loaded) do
if not packageLoadedCopy[name] then
table.insert(markDeletion, name)
elseif packageLoadedCopy[name] ~= package.loaded[name] then
table.insert(markModify, name)
end
end
for _, name in pairs(markDeletion) do
package.loaded[name] = nil
end
for _, name in pairs(markModify) do
package.loaded[name] = packageLoadedCopy[name]
end
markDeletion, markModify = {}, {}
for name in pairs(_G) do
if not gCopy[name] then
table.insert(markDeletion, name)
elseif _G[name] ~= gCopy[name] then
table.insert(markModify, name)
end
end
for _, name in pairs(markDeletion) do
_G[name] = nil
end
for _, name in pairs(markModify) do
_G[name] = gCopy[name]
end
end
Do note that simply
package.loaded = packageLoadedCopy
_G = gCopy
Does not work, and I am not sure why.

Lua : attempt to index a nil value; avoiding errors in conditionals

Let's say I have a giant table, something like:
test.test[1].testing.test.test_test
The table isn't guaranteed to exist. Neither are the tables containing it. I would like to just be able to do:
if test.test[1].testing.test.test_test then
print("it exits!")
end
But of course, this would give me an "Attempt to index ? (a nil value)" error if any of the indices aren't yet defined. So many times, I'll end up doing something like this:
if test then
if test.test then
if test.test[1] then
if test.test[1].testing then -- and so on
Is there a better, less-tedious way to accomplish this?
You can write a function that takes a list of keys to look up and does whatever action you want if it finds the entry. Here's an example:
function forindices(f, table, indices)
local entry = table
for _,idx in ipairs(indices) do
if type(entry) == 'table' and entry[idx] then
entry = entry[idx]
else
entry = nil
break
end
end
if entry then
f()
end
end
test = {test = {{testing = {test = {test_test = 5}}}}}
-- prints "it exists"
forindices(function () print("it exists") end,
test,
{"test", 1, "testing", "test", "test_test"})
-- doesn't print
forindices(function () print("it exists") end,
test,
{"test", 1, "nope", "test", "test_test"})
As an aside, the functional programming concept that solves this kind of problem is the Maybe monad. You could probably solve this with a Lua implementation of monads, though it wouldn't be very nice since there's no syntactic sugar for it.
You can avoid raising errors by setting an __index metamethod for nil:
debug.setmetatable(nil, { __index=function () end })
print(test.test[1].testing.test.test_test)
test = {test = {{testing = {test = {test_test = 5}}}}}
print(test.test[1].testing.test.test_test)
You also use an empty table:
debug.setmetatable(nil, { __index={} })

In Lua what does an if statement with only one argument mean?

I've been taught to program in Java. Lua is new to me and I've tried to do my homework but am not sure what an if statement of the following nature means.
The code is as follows:
local function getMinHeight(self)
local minHeight = 0
for i=1, minimizedLines do
local line = select(9+i, self:GetRegions())
**if(line) then
minHeight = minHeight + line:GetHeight() + 2.5
end**
end
if(minHeight == 0) then
minHeight = select(2, self:GetFont()) + 2.5
end
return minHeight
end
The if statement with the ** before and after is the part I'm not sure about. I don't know what the if statement is checking. If the line is not nil? If the line exists? If what?
In Lua, anything that's not nil or false evaluates to true in a conditional.
If the line is not nil? If the line exists?
Yes to both, because they kinda mean the same thing.
The select function returns a specific argument from it's list of arguments. It's used primarily with ..., but in this case it's being used to select the (i+9)th value returned by self:GetRegions. If there is no such value (for instance, if GetRegions only returns 5 values), then select returns nil.
if(line) is checking to see that it got a value back from select.
if(line) is being used as a shortcut for if(line ~= nil), since nil evaluates to false in a conditional.
It's worth pointing out that this shortcut is not always appropriate. For instance, we can iterate all the values in a table like this:
key, val = next(lookup)
while key do
print(key, val)
key, val = next(lookup, key)
end
However, this will fail if one of the table's keys happens be false:
lookup = {
["fred"] = "Fred Flinstone",
[true] = "True",
[false] = "False",
}
So we have to explicitly check for nil:
key, val = next(lookup)
while key ~= nil do
print(key, val)
key, val = next(lookup, key)
end
As Mud says, in lua anything other than nil and false is considered truthy. So the if above will pass as long as line is not nil or false.
That said, it worries me a bit the way you have phrased the question - "an if with only one argument".
First, it's not called "argument" - it's called expression. And in most languages is always one. In java, for example, you could do something like this:
bool found = false
...
if(found) {
...
}
ifs only care about the final value of the expression; they don't care whether it's a single variable or a more complex construction.

Search for an item in a Lua list

If I have a list of items like this:
local items = { "apple", "orange", "pear", "banana" }
how do I check if "orange" is in this list?
In Python I could do:
if "orange" in items:
# do something
Is there an equivalent in Lua?
You could use something like a set from Programming in Lua:
function Set (list)
local set = {}
for _, l in ipairs(list) do set[l] = true end
return set
end
Then you could put your list in the Set and test for membership:
local items = Set { "apple", "orange", "pear", "banana" }
if items["orange"] then
-- do something
end
Or you could iterate over the list directly:
local items = { "apple", "orange", "pear", "banana" }
for _,v in pairs(items) do
if v == "orange" then
-- do something
break
end
end
Use the following representation instead:
local items = { apple=true, orange=true, pear=true, banana=true }
if items.apple then
...
end
You're seeing firsthand one of the cons of Lua having only one data structure---you have to roll your own. If you stick with Lua you will gradually accumulate a library of functions that manipulate tables in the way you like to do things. My library includes a list-to-set conversion and a higher-order list-searching function:
function table.set(t) -- set of list
local u = { }
for _, v in ipairs(t) do u[v] = true end
return u
end
function table.find(f, l) -- find element v of l satisfying f(v)
for _, v in ipairs(l) do
if f(v) then
return v
end
end
return nil
end
Write it however you want, but it's faster to iterate directly over the list, than to generate pairs() or ipairs()
#! /usr/bin/env lua
local items = { 'apple', 'orange', 'pear', 'banana' }
local function locate( table, value )
for i = 1, #table do
if table[i] == value then print( value ..' found' ) return true end
end
print( value ..' not found' ) return false
end
locate( items, 'orange' )
locate( items, 'car' )
orange found
car not found
Lua tables are more closely analogs of Python dictionaries rather than lists. The table you have create is essentially a 1-based indexed array of strings. Use any standard search algorithm to find out if a value is in the array. Another approach would be to store the values as table keys instead as shown in the set implementation of Jon Ericson's post.
This is a swiss-armyknife function you can use:
function table.find(t, val, recursive, metatables, keys, returnBool)
if (type(t) ~= "table") then
return nil
end
local checked = {}
local _findInTable
local _checkValue
_checkValue = function(v)
if (not checked[v]) then
if (v == val) then
return v
end
if (recursive and type(v) == "table") then
local r = _findInTable(v)
if (r ~= nil) then
return r
end
end
if (metatables) then
local r = _checkValue(getmetatable(v))
if (r ~= nil) then
return r
end
end
checked[v] = true
end
return nil
end
_findInTable = function(t)
for k,v in pairs(t) do
local r = _checkValue(t, v)
if (r ~= nil) then
return r
end
if (keys) then
r = _checkValue(t, k)
if (r ~= nil) then
return r
end
end
end
return nil
end
local r = _findInTable(t)
if (returnBool) then
return r ~= nil
end
return r
end
You can use it to check if a value exists:
local myFruit = "apple"
if (table.find({"apple", "pear", "berry"}, myFruit)) then
print(table.find({"apple", "pear", "berry"}, myFruit)) -- 1
You can use it to find the key:
local fruits = {
apple = {color="red"},
pear = {color="green"},
}
local myFruit = fruits.apple
local fruitName = table.find(fruits, myFruit)
print(fruitName) -- "apple"
I hope the recursive parameter speaks for itself.
The metatables parameter allows you to search metatables as well.
The keys parameter makes the function look for keys in the list. Of course that would be useless in Lua (you can just do fruits[key]) but together with recursive and metatables, it becomes handy.
The returnBool parameter is a safe-guard for when you have tables that have false as a key in a table (Yes that's possible: fruits = {false="apple"})
function valid(data, array)
local valid = {}
for i = 1, #array do
valid[array[i]] = true
end
if valid[data] then
return false
else
return true
end
end
Here's the function I use for checking if data is in an array.
Sort of solution using metatable...
local function preparetable(t)
setmetatable(t,{__newindex=function(self,k,v) rawset(self,v,true) end})
end
local workingtable={}
preparetable(workingtable)
table.insert(workingtable,123)
table.insert(workingtable,456)
if workingtable[456] then
...
end
The following representation can be used:
local items = {
["apple"]=true, ["orange"]=true, ["pear"]=true, ["banana"]=true
}
if items["apple"] then print("apple is a true value.") end
if not items["red"] then print("red is a false value.") end
Related output:
apple is a true value.
red is a false value.
You can also use the following code to check boolean validity:
local items = {
["apple"]=true, ["orange"]=true, ["pear"]=true, ["banana"]=true,
["red"]=false, ["blue"]=false, ["green"]=false
}
if items["yellow"] == nil then print("yellow is an inappropriate value.") end
if items["apple"] then print("apple is a true value.") end
if not items["red"] then print("red is a false value.") end
The output is:
yellow is an inappropriate value.
apple is a true value.
red is a false value.
Check Tables Tutorial for additional information.
function table.find(t,value)
if t and type(t)=="table" and value then
for _, v in ipairs (t) do
if v == value then
return true;
end
end
return false;
end
return false;
end
you can use this solution:
items = { 'a', 'b' }
for k,v in pairs(items) do
if v == 'a' then
--do something
else
--do something
end
end
or
items = {'a', 'b'}
for k,v in pairs(items) do
while v do
if v == 'a' then
return found
else
break
end
end
end
return nothing
A simple function can be used that :
returns nil, if the item is not found in table
returns index of item, if item is found in table
local items = { "apple", "orange", "pear", "banana" }
local function search_value (tbl, val)
for i = 1, #tbl do
if tbl[i] == val then
return i
end
end
return nil
end
print(search_value(items, "pear"))
print(search_value(items, "cherry"))
output of above code would be
3
nil