[Learning LUA] Lesson 3: Values and Operators is up!

Talk about creating Grimrock 1 levels and mods here. Warning: forum contains spoilers!
User avatar
SpiderFighter
Posts: 789
Joined: Thu Apr 12, 2012 4:15 pm

Day Two - Values, Operators, and Assignments

Post by SpiderFighter »

Day Two - Values, Operators, and Assignments

(As always, if I make any mistakes, please correct me so I'm not misleading anyone else. I'm collating information from many different sources, some of which undoubtedly refer to older versions of lua):

Picture the process of making soup from scratch: You have three pans on the stove; one to sautee your onions, garlic and meat (or, in my case, veggie crumbles), one for your broth, and the last for your pasta. When everything is ready, you'll combine all three pans into one large pot, and there's your soup. Now if you can think about making a pot of soup in comparison to writing a Lua script, the script is the final pot (into which everything has been combined), the variables are the individual pans, and values are the ingredients in each pan. Assignment is the process of placing those values into the variables (or ingredients into pans).

Now values by themselves aren't very useful, in the same way that diced onion is just diced onion. To be useful, we place them with operators and once we've done that, they become operands (in the same way that once the onions have been paired with high heat, they become sauteed). Yum!

Confused? Don't be. Consider the following (if you don't have Lua compiled on your own system, you can start a new map in the editor and just place the following in a script_entity; when you press play it will execute and display the results in the upper left corner of the preview window).

Code: Select all

myNumber = 45
print(myNumber)
In the above example, the value "45" is plugged into the variable "myNumber," using the operator "=." Since we've done that, we can now refer to "45" as the operand. This is also a good place to point out that Lua's equal sign (=) is a command to make two things equal, which differs slightly from the algebraic equal sign (as a statement of fact that two things are already equal). This means that we can easily change a variable, simply by changing the operand - even within the same chunk of code:

Code: Select all

myNumber = 45
print(myNumber)
myNumber = 263
print(myNumber)
Executing this results in two different returns from Lua, even though there is only one variable used ("myNumber"):
45
263

Multiple values can be assigned to multiple variables on a single line, by using a comma:

Code: Select all

myNumber, yourNumber, sumOfBothNumbers = 45, 263, 45 + 263
print(myNumber, yourNumber, sumOfBothNumbers)
When you execute that chunk, Lua will return the following:
45, 263, 308

Now, this does not mean that the operand to the right of an operator must always be a value. In fact, it can also be another variable:

Code: Select all

myNumber = 45         --variable = value
yourNumber = myNumber    --variable = variable
print(yourNumber)
Pretty nifty, yes?

Arithmetic Operators:
Let's go back to the chunk we saw above:

Code: Select all

myNumber, yourNumber, sumOfBothNumbers = 45, 263, 45 + 263
print(myNumber, yourNumber, sumOfBothNumbers)
In order to get the sum of 45 and 263, we used an addition sign, which is an arithmetic operator. There are other arithmetic operators of course, and they are separated into two categories: binary (a calculation involving two operands) and unary (which involves only one operand). Can you guess what they all are?

Arithmetic Operator (Binary):
SpoilerShow
They are + (addition), - (subtraction), * (multiplication), / (division), % (modulo), and ^ (exponentiation)
Arithmetic Operator (Unary) (Hint: There's only one!):
SpoilerShow
It's - (the same subtraction operator as used in binary , but used to negate a numeric value, such as subtracting a value from 0)
The modulo operator basically divides two numbers and ouputs the remainder. Examples of these arithmetic operators are:

Code: Select all

print(2 + 2)
print(4 - 2)
print(2 * 2)
print(4 / 2)
print(10 % 3)  --divides 10 by 3 and returns the remainder
print(6 ^ 4)  --this is  the same as 6 * 6 * 6 * 6
print(-(-15))  --note that negating a negative number returns a positive
Returns from Lua if executed:
SpoilerShow
4
2
4
2
1
1296
15
Relational Operators:
While arithmetic operators deal with the manipulation of values, relational operators simply compare them. Because of this, all of these operators will produce a boolean result.

(Remember what boolean means?)
SpoilerShow
Boolean values are either true or false.
Relational operators are:
  • < less than
    > greater than
    == equal to
    ~= not equal to
    <= less than or equal to
    >= greater than or equal to

Code: Select all

print(3 < 4)
print(3 > 4)
print(3 == 4)
print(3 ~= 4)
print(3 <= 4)
print(3 >= 4)
Returns from Lua if executed:
SpoilerShow
true
false
false
true
true
false
Note that while <, >, <=, and >= look at the order of the two values, == and ~= simply determine whether or not they are equal. Added by Ryeath_Greystalk : <= , >=, ~= are not reversible. Found this out the hard way. => or =< will get you an error.

Logical Operators:
Logical operators in Lua are: and, or, and not. Remember that Lua considers a value to always be true unless it is declared false or nil? We can see this in action with these three operators. All three can produce boolean results, but only one does so at all times, so we'll look at that one first.

not: As stated, the not operator will always result in either true or false. Placing two nots (not not) before a true value always results in true, while placing two nots before a false value always results in false:

Code: Select all

print(not true)
print(not false)
print(not nil)
print(not not true)
print(not not false)
print(not not nil)
print(not "Grimrock is amazing!")
Returns from Lua if executed:
SpoilerShow
false
true
true
true
false
false
false --because Grimrock really is amazing! :)
and and or: These operators only look at their second operand when they need to, in order to avoid causing run-time errors (this is called short-cut evaluation, but it's not necessary you know this). And will return its first argument if the value is false or nil, while or will return its first argument if the value is not false or nil; otherwise, both operators will return their second argument. What this means is that the result is not always boolean:

Code: Select all

print(8 and 15)
print(8 or 15)
print(nil and 8)
print(nil or 8)
print(true and false)
print(true or false)
Looking at the returns from Lua upon execution can help clarify these:
SpoilerShow
15 --and looks at the first argument, sees it as true, and returns the second
8 --or looks at the first argument, sees it as true, and returns it without looking at the second argument
nil --put another way, if the operand to the left of and is false, the result is always false
8
false
true --put another way, if the operand to the left of or is true the result is always true
So, how can this be useful? Let's say you want to create a puzzle where two things need to be present on alcoves before a door opens. By using the and operator, Lua will look at the first operand, see if it exists (is true), then move on to check the second operand. If false, it will go no further; if true it will execute the rest of the chunk (open the door).

That's it for today. I hope you're finding these useful!
Last edited by SpiderFighter on Wed Apr 03, 2013 7:41 pm, edited 3 times in total.
User avatar
SpiderFighter
Posts: 789
Joined: Thu Apr 12, 2012 4:15 pm

Re: Correction to "Case sensitive language" bit.

Post by SpiderFighter »

Alcator wrote:
3. Lua is case-sensitive. The word Goromorg is not the same as goromorg. Words reserved for use within lua can be used as variables simply by changing the case (Then instead of then, for example).
All right, while the paragraph is technically correct, it also represents a programming sin so great that it has to be exorcised immediately:
Please note, that I'm not dictating what to do here; I'm only saying if they wanted to do this, they could. I definitely agree that you should pick one method and stick with it. Good habits early on mean easier troubleshooting down the road, whether it's you debugging your own code, or someone else (the example of "Then vs then" was simply to show that case difference mattered, not that you should name something specifically "then," but I'll be more careful to choose more meeaningful terms from now on).

Good to see you adding to the conversation!
Ryeath_Greystalk
Posts: 366
Joined: Tue Jan 15, 2013 3:26 am
Location: Oregon

Re: [Learning LUA] Lesson 3: Values and Operators is up!

Post by Ryeath_Greystalk »

Thanks for newest installment SpiderFighter, all though you almost lost me at the onions and garlic(bleech).

I just wanted add a couple things,

<= , >=, ~= are not reversible. Found this out the hard way. => or =< will get you an error.

One thing I've learned while working on my mod is that numbers that may appear equal on the surface may not, in fact be equal. I was going to post a link to the forum topic but decided to copy / paste the wonderful explanation provided by Xanathar.

Code: Select all

This problem is common to every programming language, and to some extension even to Excel and old old versions of the windows calculator :)

The numbers in Lua are all binary floating point - which basically means they are binary numbers with a "decimal" (*) point and some huge (but limited) number of digits , and, depending on the values, some numbers do not have a representation with a limited number of digits (we call those numbers "periodic" as they have a infinitly repeating pattern of digits after the decimal point).

The same happens with "our" decimal numbers. If you want to write 3 * (1/3) and you only use 3 significant digits, you get 3 * 0.333 which is 0.999 which is different from the correct result (=1).

The point is that many more numbers are periodic when written in binary - for example 0.1 in binary has an infinite numbers of digits. However note that when the numbers are printed they are often rounded to have a better readability: so you write 0.1 you read 0.1 but it really is 0.999999999998769 or 0.10000000000000000012 or whatever the value is.

So a golden rule in every programming language is : "when comparing two floating point numbers, never check that they are equal, but check that their difference is below a given threshold". (**)


For example:

Wrong:
if (a == b) then ...

Right:
if (math.abs(a - b) < 0.00001) then ...


(*) = I guess "decimal" point is misleading as they are binary numbers, but hey who cares.

(**) = The only exception is if those two numbers are integers and they come from only integer operations (for example, you can safely compare the count of items in two sacks using ==, since counting is integer in nature).
User avatar
SpiderFighter
Posts: 789
Joined: Thu Apr 12, 2012 4:15 pm

Re: [Learning LUA] Lesson 3: Values and Operators is up!

Post by SpiderFighter »

Ryeath_Greystalk wrote:<= , >=, ~= are not reversible. Found this out the hard way. => or =< will get you an error.
Thanks for the post, Ryeath. I've added the above bit in the lesson (with credit, of course).

The great thing about this thread is that the scope is so broad that I can't possibly cover every single little thing about one topic in a single post. I'm glad that it gets people thinking about their own journey with Lua and offering their own advice and comments.
Alcator
Posts: 37
Joined: Fri Apr 13, 2012 9:59 am

Re: Day Two - Values, Operators, and Assignments

Post by Alcator »

SpiderFighter wrote: and and or: These operators only look at their second operand when they need to, in order to avoid causing run-time errors (this is called short-cut evaluation, but it's not necessary you know this). And will return its first argument if the value is false or nil, while or will return its first argument if the value is not false or nil; otherwise, both operators will return their second argument. What this means is that the result is not always boolean:

Code: Select all

print(8 and 15)
print(8 or 15)
print(nil and 8)
print(nil or 8)
print(true and false)
print(true or false)
Looking at the returns from Lua upon execution can help clarify these:
SpoilerShow
15 --and looks at the first argument, sees it as true, and returns the second
8 --or looks at the first argument, sees it as true, and returns it without looking at the second argument
nil --put another way, if the operand to the left of and is false, the result is always false
8
false
true --put another way, if the operand to the left of or is true the result is always true
So, how can this be useful? Let's say you want to create a puzzle where two things need to be present on alcoves before a door opens. By using the and operator, Lua will look at the first operand, see if it exists (is true), then move on to check the second operand. If false, it will go no further; if true it will execute the rest of the chunk (open the door).
This whole section is super confusing.
Why ON EARTH would you ever want to know the RESULT of "8 and 15" ??? What does that even mean? It's not like you're trying to use those numbers as boolean flags (8 = 1000 and 15 = 1111). This will confuse hell of a lot of people. Your own example in the later section even goes against what you've given initially as the "theory examples" -- in your real-life useful example, you talk about alcoves, but before that, you're proverbially add apples and pears.

What I was missing in the section about variables and assigning values to them is the term "Expression". This is so important that it cannot be skipped and will have to be explained, especially since it can really mess with people's minds due to the LAZY evaluation (and not "short-cut evaluation").

A function call, or a comparison, or a value assignment are expressions:

Code: Select all

45                           is an expression that has the value 45.
42 + 3                     is an expression that has the value 45.
myVariable = 45       is an assignment; after this, "myVariable" will hold the value of the expression [u]45[/u]. It is an expression, and the value of this expression is the new value of the variable, in this case, 45.
myVariable == 45     is a comparison, which compares [u]the value in[/u] myVariable with [u]the value of[/u] 45. If the two values are the same, then the value of this expression will be true, otherwise, it will be false.

this can of course be mixed:
shouldOpenDoor = (myVariable == 45)        this expression consists of assignment and comparison. If myVariable equals 45, then shouldOpenDoor will newly acquire the value "true", otherwise, it will acquire the value "false".

Before, you heard about "short-cut" evaluation, which is in fact "lazy evaluation". LAZY evaluation, as its name implies, only evaluates those expressions that it absolutely must in order to be able to ascertain how to evaluate the current expression. And here comes the potential source of problems if you try to be super efficient in your code.
Imagine this snippet:

Code: Select all

local x, y
if (x = myBooleanVariable) and (y = findEntity("torch")) then
  -- do something
  y:setFuel(500)
else
  -- do something else
  y:setFuel(2500)
end
The programmer intended the following to happen: assign the value of myBooleanVariable to x, then run the function findEntity with the parameter "torch", and assign the result of that function to the variable y. If x becomes TRUE and a "torch" is found, then the programmer intended for the first section of the code to take place ("-- do something", let's say set the fuel of the torch to 500 units), otherwise, he or she intended to "-- do something else", in this case, set the fuel to 2500.
However, this code will fail badly the first time "myBooleanVariable" is false, exactly because of LAZY evaluation:
The program will look at "x = myBooleanVariable" and think "A ha! myBooleanVariable is false, I put x = false. This is an "AND" logical operator, so it could ever evaluate as true if the first part of it was true, but it's not. I don't need to lose time with the second bracket!"
As such, the "y = findEntity("torch")" code will not even be executed, and therefore, y will NOT contain a reference to a torch. As such, when the line
y:setFuel(2500) is executed, it will fail, because y is not an object with the method setFuel.

Fortunately, if you do not go wild in your scripting, you will probably never do things like this. But, since this is grimrock modding forum, you SHOULD be aware that this type of evaluation is actually being used quite intensively in the game, and in fact, all games which are trying to squeeze a little more power from the game system must use "tricks" like this to be efficient.

The thing could have been of course programmed in a much more conservative way:

Code: Select all

local x, y
  x = myBooleanVariable
  y = findEntity("torch")
if (x) and (y ~= nil) then
  -- do something
  y:setFuel(500)
elseif (y ~= nil)
  -- do something else
  y:setFuel(2500)
else
  -- no torch, and x is false.
end
User avatar
petri
Posts: 1917
Joined: Thu Mar 01, 2012 4:58 pm
Location: Finland

Re: [Learning LUA] Lesson 3: Values and Operators is up!

Post by petri »

Well, it's also called short-circuit evaluation (http://en.wikipedia.org/wiki/Short-circuit_evaluation).

btw. you can't mix expressions and statements like that in Lua. So, for example
if x = findEntity("torch") then print("tada") end

Produces the following error: "XXX.lua:1: 'then' expected near '='"

It would work in C but not in Lua because an assignment is not an expression in Lua.
User avatar
SpiderFighter
Posts: 789
Joined: Thu Apr 12, 2012 4:15 pm

Re: Day Two - Values, Operators, and Assignments

Post by SpiderFighter »

@Alcator: I think you may be missing the point of this thread. I'm learning as I go, and every text I see geared toward people who are just starting out has examples exactly like the ones I gave. In fact, (referring to "Why ON EARTH would you ever want to know"), all I did was plug in different numbers to examples given in the very well respected books and sites I listed in the OP.
Alcator wrote:This whole section is super confusing.
Why ON EARTH would you ever want to know the RESULT of "8 and 15" ??? What does that even mean? It's not like you're trying to use those numbers as boolean flags (8 = 1000 and 15 = 1111). This will confuse hell of a lot of people. Your own example in the later section even goes against what you've given initially as the "theory examples" -- in your real-life useful example, you talk about alcoves, but before that, you're proverbially add apples and pears."
How is adding two whole numbers "apples and pears?" Off the point, but I can see adding those numbers as ticks on timers, off the top of my head. In any case, I'm not trying to find the result of 8 and 15; it's an example to show the order of how short-cut evaluation works, nothing more. You have to be able to understand what something means before you can actually apply it, no? Likewise, I only mentioned the alcoves example so that people can begin to understand how these things can be applied to the game. As we get deeper into Lua, I expect there will be more useful examples given but, at this point in time we're barely out of the "Hello, World!" stage.
Alcator wrote:What does that even mean? It's not like you're trying to use those numbers as boolean flags (8 = 1000 and 15 = 1111). This will confuse hell of a lot of people.
No, again...I'm a NEWBIE. It would be useful if you could explain how your boolean flags example works. What does mean? How did you arrive at those numbers? At the moment, I'm not as advanced or as knowledgable as you are. Baby steps. We must crawl before we can stand, and you seem to think I should already be at a full run.

Look at this (emphasis is my own):
msyblade wrote: I'm gonna try to explain the true benefits of this thread idea.
A: It is going to be in chronological order, people wanting to learn, are going to get lessons in the order they were learned by you, from the floor up. This is as opposed to how most of us here learned, doing far too complex functions by copy/pasting, and trying to troubleshoot when it all looks like greek. Then learning the more basic principles later, and puking on yourself when you see how you butchered it earlier on.
B: The language. Dont get me wrong, in about 4 pages the language here is going to sound like advanced nano-physics (As each property is defined and analyzed), like most detailed scripting discussions. However, here it is going to progress from plain english, into script talk, gradually. All in the same thread! We can SEE the metamorphosis occur.
C: We are going to make mistakes (for instance, I'm not familiar with the ; use either.) But NOONE be discouraged by this, it will create a discussion back and forth, completely breaking down and analyzing what went wrong, and how it can be more easily understood. This will prove to be a valuable occurence when compared to the message that was 7 lines long with no responses. Some ppl might not "get" it on the first pass.
The underlined sentances really do relay the intention of this thread nicely.
Alcator wrote:What I was missing in the section about variables and assigning values to them is the term "Expression". This is so important that it cannot be skipped and will have to be explained, especially since it can really mess with people's minds due to the LAZY evaluation (and not "short-cut evaluation").
As I've said very clearly many times throughout the thread, If you feel like something is missing, then feel free to ADD to the conversation (without being disputatious). It does surprise me that you dismiss a term that originated with the creator of Lua (it's also called "short-circuit evaluation").
Alcator wrote:A function call, or a comparison, or a value assignment are expressions:
Honestly, the rest of your post might actually be useful but, as the above felt like a bit of a personal attack, I didn't bother to read it. If you want to contribute in a respectful manner, please do! It's obvious you have so much knowledge to share, and it could really be useful to the hundreds of people years from now who happen to stumble upon this thread.

EDIT to add:
petri wrote:Well, it's also called short-circuit evaluation (http://en.wikipedia.org/wiki/Short-circuit_evaluation).

btw. you can't mix expressions and statements like that in Lua. So, for example
if x = findEntity("torch") then print("tada") end

Produces the following error: "XXX.lua:1: 'then' expected near '='"

It would work in C but not in Lua because an assignment is not an expression in Lua.
Now, that's the way to correct someone respectfully, as well as add to the conversation.
Last edited by SpiderFighter on Thu Apr 04, 2013 4:13 pm, edited 15 times in total.
User avatar
Diarmuid
Posts: 807
Joined: Thu Nov 22, 2012 6:59 am
Location: Montreal, Canada
Contact:

Re: Day Two - Values, Operators, and Assignments

Post by Diarmuid »

Alcator wrote: Why ON EARTH would you ever want to know the RESULT of "8 and 15" ??? What does that even mean?
Well, I don't think I'm the least talented coder around, and I think spiderfigher's examples were really clear and helpful. Of coure you would'nt test 8 and 15 in a real situation, but shown like that it makes it clearer to understand how it works. It really did for me.

This is a thread for beginners, and a complex example full of true and falses like you did is doing nothing to vulgarize things at this level: even I have to look at your example in detail and study it to understand how it works - a complete beginner will not understand and just abandon after the first line of code.

spiderfighter is genuinely trying to help, I think in a good way, and he's doing great considering he's a beginner too. No need to adopt that agressive tone with him, we get you know a lot about programming. (And even then, check your sources, as petri pointed-out...)
User avatar
Xanathar
Posts: 629
Joined: Sun Apr 15, 2012 10:19 am
Location: Torino, Italy
Contact:

Re: [Learning LUA] Lesson 3: Values and Operators is up!

Post by Xanathar »

@Diarmuid (or really, to anyone wanting to learn this, but I understand this could be a bit overly technical and confusing for those learning the first steps):

Interestingly, this behaviour of "and" and "or" operators means that "or" behaves like a null-coercion operator does in other languages (for example the ?? operator in C#):

Code: Select all

> x = "ciao"
> print (x or "hello")
ciao
> x = nil
> print (x or "hello")
hello
This can come very useful for functions' default arguments, as they are nil if not specified (at least if false is not a valid value for the argument).

Code: Select all

function openEverything(x, y, level)
	for o in entitiesAt(level or party.level, x, y)
		if (o.open ~= nil) then
			o:open()
		end
	end
end
And stackoverflow seems to confirm this and expand on it: http://stackoverflow.com/questions/6022 ... -arguments

BTW - I thought of this after reading this thread - very useful!
Waking Violet (Steam, PS4, PSVita, Switch) : http://www.wakingviolet.com

The Sunset Gate [MOD]: viewtopic.php?f=14&t=5563

My preciousss: http://www.moonsharp.org
User avatar
Diarmuid
Posts: 807
Joined: Thu Nov 22, 2012 6:59 am
Location: Montreal, Canada
Contact:

Re: [Learning LUA] Lesson 3: Values and Operators is up!

Post by Diarmuid »

@Xanathar,

Yes, I was aware of this way of handling "default" values. I think I picked it by studyin jKos framework code, and I used this trick in many places in exsp.
Post Reply