Right now, I've been making my for loops like this
local i
for i = 1, 10 do
--stuff
end
Since I thought you should try to keep things local for better performance and reduce risk from errors.
However, I've noticed that it is common to simply use.
for i = 1, 10 do
--stuff
end
Is using local preferred, or is omitting it pretty harmless?
(EDIT) There's no difference between the code samples you gave. However, be aware that the variable you defined with local i is not the same variable that is used within your for i = 1, 10 do loop. When the loop exits, the original value of i remains unchanged (that is, i == nil).
siffiejoe points out that loop control/counter variables are never accessible outside of the loop, even if the same variable name was defined in advance. Any references to the variable within the loop will use the loop value. Any references outside of the loop will use the original, or non-loop value.
For this reason, it's safe to reuse an existing variable name in a for statement without corrupting the original. If you want to access the counter variable after the loop, you can define an extra variable beforehand and update it at within the loop as follows (siffiejoe's example):
local j
for i = 1, 10 do
j = i
--[[ stuff ]]
end
print(j) -- j stores the last value of i before the loop exits
Documentation: Numeric for
Short answer: don't add local i before the for loop, because it's useless and confusing.
The for loop starts a new block, the control variable (i here) is already local to this block. Adding the local i is similar to:
local i
do
local i = 0
-- do something
end
Note that the i inside the block is a totally new variable, which shadows the i outside. When the block is over, the i outside the block gets its life back, but has no knowledge of what happened inside the block because the two variables have no relationship except having the same name.
The for loop control-structure in Lua have some particularities, it's not exactly the equivalent of a C for loop.
One of these particularities is that its index variable is controlled by the loop. The index var is itself local and modifying it inside the loop has in fact no effect. Example:
for i=1,5 do
io.write(("i: %d; "):format(i))
i = i + 10 -- this has no effect, as 'for' is in control of the index var.
end
Result:
i: 1; i: 2; i: 3; i: 4; i: 5;
Related
I have been looking at some Lua code recently and multiple times the author assigns a local variable, altering the local variable seemingly with the expected outcome of also altering the assigning variable as he does not do anything with the local variable after. Is this the case or do these changes not affect the original values.
Gene Construct
local gene = {}
gene.into = 0
gene.out = 0
gene.weight = 0.0
gene.enabled = true
gene.innovation = 0`
Code
function nodeMutate(genome)
if #genome.genes == 0 then
return
end
genome.maxneuron = genome.maxneuron + 1
local gene = genome.genes[math.random(1,#genome.genes)]
if not gene.enabled then
return
end
gene.enabled = false
local gene1 = copyGene(gene)
gene1.out = genome.maxneuron
gene1.weight = 1.0
gene1.innovation = newInnovation()
gene1.enabled = true
table.insert(genome.genes, gene1)
local gene2 = copyGene(gene)
gene2.into = genome.maxneuron
gene2.innovation = newInnovation()
gene2.enabled = true
table.insert(genome.genes, gene2)
end
Changes to gene may affect genome.genes[math.random(1,#genome.genes)] because gene is a reference. From the Lua Manual - Values and Types:
Tables, functions, threads, and (full) userdata values are objects: variables do not actually contain these values, only references to them. Assignment, parameter passing, and function returns always manipulate references to such values; these operations do not imply any kind of copy.
This means that when you assign a variable to an object you copy the reference to that object, not the object itself.
For example:
local a = {1,2,3}
local b = a
b[1] = 'a'
The table a now contains {'a',2,3} because b is a reference to a.
From my reading of the code I'd think the following:
local gene1 = copyGene(gene) -- local copy, distinct from original 'gene'
-- (modifications of 'gene1')
table.insert(genome.genes, gene1) -- add modified copy to 'genome.genes'
So I'd guess that copyGene produces a copy of gene, meaning that gene isn't modified (only gene1, gene2). References to these are held in local variables and these references indeed go out of scope at the end of the block. But the modified copy is added to the list (/ array / sequence, whatever you want to call it) genome.genes which does produce an externally visible effect (because that variable – genome – comes in from outside).
My higher-level impression of the code is that genome.genes is a list of genes that may or may not be enabled. Each run through this function randomly picks one of these genes and if it is enabled, (1) disables it and (2) adds two modified enabled copies to the genome.genes (which may then be the base for new copies in later runs).
When I use a loop, to access the variables outside of the loop they need to be initialised before you enter the loop. For example:
Y = Array{Int}()
for i = 1:end
Y = i
end
Since I have initialised Y before entering the loop, I can access it later by typing
Y
If I had not initialised it before entering the loop, typing Y would not have returned anything.
I want to extend this functionality to the output of the 'hist' function. I don't know how to set up the empty hist output before the loop. The only work around I have found is below.
yHistData = [hist(DataSet[1],Bins)]
for j = 2:NumberOfLayers
yHistData = [yHistData;hist(DataSet[j],Bins)]
end
Now when I access this later on by simply typing
yHistData
I get the correct values returned to me.
How can I initialise this hist data before entering the loop without defining it using the first value of the list I'm iterating over?
This can be done with a loop like follows:
yHistData = []
for j = 1:NumberOfLayers
push!(yHistData, hist(DataSet[j], Bins))
end
push! modifies the array by adding the specified element to the end. This increases code speed because we do not need to create copies of the array all the time. This code is nice and simple, and runs faster than yours. The return type, however, is now Array{Any, 1}, which can be improved.
Here I have typed the array so that the performance when using this array in the future is better. Without typing the array, the performance is sometimes better and sometimes worse than your code, depending on NumberOfLayers.
yHistData = Tuple{FloatRange{Float64},Array{Int64,1}}[]
for j = 1:NumberOfLayers
push!(yHistData, hist(DataSet[j], Bins))
end
Assuming length(DataSet) == NumberOfLayers, we can use anonymous functions to simplify the code even further:
yHistData = map(data -> hist(data, Bins), DataSet)
This solution is short, easy to read, and very fast on Julia 0.5. However, this version is not yet released. On 0.4, the currently released version, the performance of this version will be slower.
I've been told in Java that I should avoid modifying the original parameters such as
public int doStuff(int begin, int end) {
/* loop or something */
begin++; //bad
end--; //also bad
/* end loop */
return
}
instead, I should do something like
public int doStuff(int begin, int end) {
int myBegin = begin; //something like this
int myEnd = end;
/* stuff */
return
}
So, I've been doing this in lua
function do_stuff(begin, last)
local my_begin = begin
local my_last = last
--stuff
my_begin = my_begin + 1
my_last = my_last - 1
--stuff
end
But, I'm wondering if
function do_stuff(begin, last)
--stuff
begin = begin + 1
last = last - 1
--stuff
end
is also discouraged, or is it nice and concise?
There are no rules. Let taste, clarity, and need decide.
Nevetheless, a common idiom is to provide default values for parameters as in
function log(x,b)
b = b or 10
...
end
If you were told not to modify the parameters of functions, then there was probably a reasoning associated with that. Whatever that reasoning is would apply as much to Lua as to Java, since they have similar function argument semantics. Those reasons could be one or more of (but not limited to):
If you modify a parameter... you don't have it anymore. If you suddenly have a need for the original value you were passed, it's gone now.
Creating confusion, depending on how the parameters are named. The word "begin" suggests the beginning of something. If you change it, it isn't necessarily the beginning anymore, but merely the current element you're operating on.
Creating potential errors, if dealing with reference types (non-basic types in Java, tables and such in Lua). When you modify an object, you're changing it for everyone. Whereas incrementing an integer is just changing your local value. So if you're frequently modifying parameters, you still need to think about which ones you ought to be poking at and which ones you shouldn't be.
To put it another way, if you agreed with the suggestion for doing so in Java, then it applies just as much to Lua. If you didn't agree with the suggestion in Java, then you have no more reason to follow it under Lua.
In Lua functions, threads, tables and userdata types are passed by reference. So unless you have one of those you are working with a local copy anyway.
So in your example:
function do_stuff(begin, last)
--stuff
begin = begin + 1
last = last - 1
--stuff
end
begin and last are local non-reference variables in do_stuff's scope.
The only reason to make a copy of them is that you might want to store there initial value for later use. For that purpose you can either create a backup copy of the initial value or you create a working copy of it. Whatever you prefer.
Only make sure you know what is passed by reference and what by value so you avoid changing things you don't want to change and the other way around.
This question already has answers here:
Lua for loop reduce i? Weird behavior [duplicate]
(3 answers)
Closed 7 years ago.
im trying this in lua:
for i = 1, 10,1 do
print(i)
i = i+2
end
I would expect the following output:
1,4,7,10
However, it seems like i is getting not affected, so it gives me:
1,2,3,4,5,6,7,8,9,10
Can someone tell my a bit about the background concept and what is the right way to modify the counter variable?
As Colonel Thirty Two said, there is no way to modify a loop variable in Lua. Or rather more to the point, the loop counter in Lua is hidden from you. The variable i in your case is merely a copy of the counter's current value. So changing it does nothing; it will be overwritten by the actual hidden counter every time the loop cycles.
When you write a for loop in Lua, it always means exactly what it says. This is good, since it makes it abundantly clear when you're doing looping over a fixed sequence (whether a count or a set of data) and when you're doing something more complicated.
for is for fixed loops; if you want dynamic looping, you must use a while loop. That way, the reader of the code is aware that looping is not fixed; that it's under your control.
When using a Numeric for loop, you can change the increment by the third value, in your example you set it to 1.
To see what I mean:
for i = 1,10,3 do
print(i)
end
However this isn't always a practical solution, because often times you'll only want to modify the loop variable under specific conditions. When you wish to do this, you can use a while loop (or if you want your code to run at least once, a repeat loop):
local i = 1
while i < 10 do
print(i)
i = i + 1
end
Using a while loop you have full control over the condition, and any variables (be they global or upvalues).
All answers / comments so far only suggested while loops; here's two more ways of working around this problem:
If you always have the same step size, which just isn't 1, you can explicitly give the step size as in for i =start,end,stepdo … end, e.g. for i = 1, 10, 3 do … or for i = 10, 1, -1 do …. If you need varying step sizes, that won't work.
A "problem" with while-loops is that you always have to manually increment your counter and forgetting this in a sub-branch easily leads to infinite loops. I've seen the following pattern a few times:
local diff = 0
for i = 1, n do
i = i+diff
if i > n then break end
-- code here
-- and to change i for the next round, do something like
if some_condition then
diff = diff + 1 -- skip 1 forward
end
end
This way, you cannot forget incrementing i, and you still have the adjusted i available in your code. The deltas are also kept in a separate variable, so scanning this for bugs is relatively easy. (i autoincrements so must work, any assignment to i below the loop body's first line is an error, check whether you are/n't assigning diff, check branches, …)
I am trying to "Skip" a variable, by either never declaring it or just having it garbage collected immediately, but I don't know if it's possible.
Example:
function TestFunc()
return 1, 2
end
function SecondFunction()
local nodeclare, var = TestFunc()
end
Basically what I wanted was for "nodeclare" to not even exist. So if I did print(nodeclare, var) it would do nil, 2.
The same thing would be if I was doing a pairs loop and I didn't need to use the keyvalue.
Is there some special thing I can put as the variable name for this to happen? If say I was doing a pairs loop over 100 values, would that even have a signifigant impact?
First of all, variables are not garbage collected, objects are. In this case, there's nothing to garbage collect.
However, let's say that TestFunc was creating objects (say, tables):
function TestFunc()
return {1}, {2}
end
function SecondFunction()
local nodeclare, var = TestFunc()
end
Now nodeclare is referencing a table returned by TestFunc. That's an object, allocated on the heap, that we don't want hanging around forever.
That object will eventually be collected if there is nothing left referring to it. In your case, as soon as SecondFunction returns, the local nodeclare goes out of scope and goes away. As long as there's nothing else referencing that table, the table will be collected (during next collection cycle).
You can avoid declaring nodeclare entirely by skipping the first return value of TestFunc like this:
local var = select(2, TestFunc())
However, when you're talking about a temporary local variable, as in your example, you normally just create the temporary variable then ignore it. This avoids the overhead of the call to select. Sometimes you use a variable name that indicates it's trash:
local _, var = TestFunc()
If say I was doing a pairs loop over 100 values, would that even have a signifigant impact?
None whatsoever. You're just continually overwriting the value of a local variable.
What impact do you mean exactly? Memory? Performance?
According to the Programming in Lua book, you can sort of skip the second return value, but not ignore the first and use the second:
x,y = foo2() -- x='a', y='b'
x = foo2() -- x='a', 'b' is discarded
x,y,z = 10,foo2() -- x=10, y='a', z='b'