The following code snippet:
for weight, item in itemlist do
weight_total=weight_total+weight
end
is causing the error "attempt to call table value" on the first line in that snippet. Why?
Itemlist is a table of tables of weights and strings, like such:
local itemlist = {
{4,"weapon_pistol"},
{2,"weapon_357"},
...
Nothing is being called as far as I can tell; why is this error coming up?
The generic for expects 3 arguments: a callable value, some value which is repeatedly passed to it, and the key where the iteration shall start.
Stock lua does not call pairs on the first value passed to for if that's not callable, though some derivatives do.
Thus, you must use ipairs(itemlist), pairs(itemlist), next, itemlist or whatever you want (the last two have identical behavior, and are what most derivatives do).
As an example, an iterator unpacking the value sequence:
function awesome_next(t, k)
k, t = next(t, k)
if not t then return end
return k, table.unpack(t)
end
for k, a, b, c, d in awesome_next, t do
end
Related
Writing a function that takes a generic for loop iterator consisting of the iterator function, the invariant state & the loop control variable to return the value the control variable has in the last iteration is straightforward:
function iterator_last_value(iterator, state, control_var)
local last
for value in iterator, state, control_var do
last = value
end
return last
end
print(iterator_last_value(("hello world"):gmatch"%a+")) -- world
This could be easily extended to support arbitrary constant numbers of arguments up to Lua's local register limit. We can also add vararg iterator return value support by always storing the last vararg in a table; this requires us to get rid of Lua's for loop syntactic sugar:
function iterator_last(iterator, state, control_var)
local last = {}
local last_n = 0
local function iter(...)
local control_var = ...
if control_var == nil then
return table.unpack(last, 1, last_n)
end
last = {...}
last_n = select("#", ...)
return iter(iterator(state, control_var))
end
return iter(iterator(state, control_var))
end
print(iterator_last(ipairs{"a", "b", "c"})) -- 3, c
which works well but creates a garbage table every iteration. If we replace
last = {...}
last_n = select("#", ...)
with
last_n = select("#", ...)
for i = 1, last_n do
last[i] = select(i, ...)
end
we can get away with reusing one table - presumably at the cost of manually filling the table using select being less efficient than {...}, but creating significantly fewer garbage tables (only one garbage table per call to iterator_last).
Is it possible to implement a variadic return value iterator_last without storing a vararg with significant overhead using a table, coroutine or the like, leaving it on the stack and only passing the varargs around through function calls? I conjure that this is not possible, but have been unable to prove or disprove it.
I am writing a chess program and has to see whether certain value are empty. I tried
aPiece is an array of CommandButtons. cmdSquare is a control array of shapes.
Private aPiece(63) As CommandButton
...
For p = 0 To 63
If IsEmpty(aPiece(p)) Then
aPiece(p).Left = cmdSquare(p).Left
aPiece(p).Top = cmdSquare(p).Top
End If
Next p
All variable are declared and is seem to be IsEmpty function which is not working.
The IsEmpty method only returns meaningful information for variants. Since the array contains objects, you need to check like this:
If aPiece(p) Is Nothing Then
However, this seems like only part of the answer. The above logic is saying "If there is no piece in my array then update it's location". That doesn't make sense to me and will generate an error. You also need to add Not like below:
For p = 0 To 1
If Not aPiece(p) Is Nothing Then
aPiece(p).Left = cmdSquare(p).Left
aPiece(p).Top = cmdSquare(p).Top
End If
Next p
It depends on the datatype of aPiece. If it is a variant and you haven't assigned a value to it, IsEmpty will return true. However, if it is a String, Date, Integer, etc. those are automatically initialized (String will be an empty string, Integer will be 0) so IsEmpty will return false.
A pretty good reference is this page: IsEmpty Function - Visual Basic 6.0
I'm trying to use Matlab to implement the MDO algorithm, which requires me to sort an array of objects of a custom-defined mdoVertex class by their degree, and then delete the one with the smallest degree value. My first attempt was this:
for i = 1:m
if graph(i).degree < minDegree
minDegree = graph(i).degree;
elimObject = graph(i);
end
end
Matlab is complaining that elimObject, or the object to be eliminated after the loop executes, is an undefined function or variable. How, then, can I keep track of not only the current smallest degree the loop has encountered, but also which object it corresponded to? 'graph' is the name of the array holding all of my vertex objects.
I suspect that you're somehow trying to call clear on the object returned from your function. Or is it just a few lines of code in a script? I'm guessing here. In any event, calling clear won't work. As you've noticed, clear expects to be given a variable name.
But in this case, you're not trying to delete a variable, you're trying to remove an element from an array. For that, you do arrayname(indextodelete) = [];
So I think that you want...
minDegree = inf; % See what I did there? I defined the variable, and I did it in such a way that I KNOW that the first vertex will satisfy the condition.
for i = 1:length(graph) % Properly loop over the entire graph
if graph(i).degree < minDegree % The first vertex will definitely satisfy this. Maybe another one (or more) will later!
minDegree = graph(i).degree;
minDegreeIndex = i; % Don't record the value, just remember WHERE it is in the array.
end
end
graph(minDegreeIndex) = []; % Now, remove the element that you identified from the array!
(By the way, you never showed us how you tried to eliminate elimObject. I assume that you called clear (the object that you identified)? You shouldn't make us guess; show us.)
myitem.inject({}) {|a,b| a[b.one] = b.two; a}
Where:
myitem is a class which holds an array or pair objects (pair objects have two fields in them one and two)
I am not sure what the above code is supposed to do?
Starting with an empty map, set its value for the b.one key to b.two.
In other words, for every item in the "myitem" collection, create a map entry. The key will be an item's "one" value. That map entry's value will be the item's "two" value.
The block given to "inject" receives two parameters. The first is the "accumulator". It's initial value in this case is the empty map passed to "inject". The second parameter is the current item in the collection. In this case, each item in the collection.
The block must return whatever will be used as the next accumulator value, in this case, the map. We want to keep using the same map, so when we're done, the "inject" method will return the map with all the key/value pairs.
Without saving the results of the inject it's a bit worthless.
It's a pedantic way of writing
h = {}
myitem.each { |b| h[b.one] = b.two }
or to be closer to your original code
a = {}
mytem.each { |b| a[b.one] = b.two }
(I personnaly hate this pattern (and people who use it) as it needs the ; a at the end, losing all the functional aspect of inject. (Using a side-effect function inside a 'functional pattern', and then realizing that the later function (a[..]) doesn't return the expecting object is just wrong, IMO).
Inject is normal use to 'fold' a list into a result like
[1,2,3].inject(0) { |sum, x| sum+x }
=> 6 # (0+1+2+3)
here sum is the result of the last call to the block, x is each value on the list and 0 is the initial value of sum.
[2,3].inject(10) { |p,x| p*x }
=> 60 # 10*2*3
etc ...
Hash[my_item.map {|object| [object.one, object.two]}]
is another way to do it.
What is the "best" way to determine the number of elements in an array in VBScript?
UBound() tells you how many slots have been allocated for the array, but not how many are filled--depending on the situation, those may or may not be the same numbers.
First off there is no predefined identifier called vbUndefined as the currently accepted answer appears to imply. That code only works when there is not an Option Explicit at the top of the script. If you are not yet using Option Explicit then start doing so, it will save you all manner of grief.
The value you could use in place of vbUndefined is Empty, e.g.,:-
If arr(x) = Empty Then ...
Empty is a predefined identify and is the default value of a variable or array element that has not yet had a value assigned to it.
However there is Gotcha to watch out for. The following statements all display true:-
MsgBox 0 = Empty
MsgBox "" = Empty
MsgBox CDate("30 Dec 1899") = True
Hence if you expect any of these values to be a valid defined value of an array element then comparing to Empty doesn't cut it.
If you really want to be sure that the element is truely "undefined" that is "empty" use the IsEmpty function:-
If IsEmpty(arr(x)) Then
IsEmpty will only return true if the parameter it actually properly Empty.
There is also another issue, Null is a possible value that can be held in an array or variable. However:-
MsgBox Null = Empty
Is a runtime error, "invalid use of null" and :-
MsgBox IsEmpty(Null)
is false. So you need to decide if Null means undefined or is a valid value. If Null also means undefined you need your If statement to look like:-
If IsEmpty(arr(x)) Or IsNull(arr(x)) Then ....
You might be able to waive this if you know a Null will never be assigned into the array.
I'm not aware of a non-iterative method to do this, so here's the iterative method:
Function countEmptySlots(arr)
Dim x, c
c = 0
For x = 0 To ubound(arr)
If arr(x) = vbUndefined Then c = c + 1
Next
countEmptySlots = c
End Function
As Spencer Ruport says, it's probably better to keep track yourself to begin with.
There's nothing built in to tell you what elements are filled. The best way is to keep track of this yourself using a count variable as you add/remove elements from the array.