FYI: SQF is a programming language for the computer game series Arma.
The main data types of SQF are documented and the list does not include an hash table (or dictionary).
One way to have a hash table is to create a Game Logic in the mission.sqm (e.g. named logic1), and use setVariable and getVariable on it, e.g.
logic1 setVariable ["variable1", 1];
_a = logic1 getVariable "variable1";
However, this requires an extra array associated with it to track the list of used keys, e.g.
logic1Vars = [];
logic1 setVariable ["variable1", 1];
logic1Vars pushBack "variable1";
logic1 setVariable ["variable1", nil];
logic1Vars = logic1Vars - ["variable1"];
(or is there a way to get the list of variables?)
Another way (that is possible but I haven't tried) is to implement an hash table. That obviously takes an extra effort because implementing a good table is not easy.
But maybe I am missing something: is there an idiomatic way to have an hash table in SQF?
You can use allVariables to retrieve the Number of keys in a Namespace.
To create a Namespace you can use a Logic or a Location or a SimpleObject. See how CBA does it https://github.com/CBATeam/CBA_A3/blob/master/addons/common/fnc_createNamespace.sqf.
Generally a Location or SimpleObject is more Performance friendly than using a GameLogic. You should keep that in Mind.
But what you are probably searching for is the allVariables command which returns all Variables in a Namespace(Hashtable).
You can also use getVariable ARRAY to set a default Value in case the Namespace doesn't contain the Key you want to read.
CBA also has Hashes they behave like a Map. Not like a hashTable (The keys are not hashed) also it's SQF code instead of Engine code so it is slightly slower.
Also (don't have enough reputation to comment)
You don't need all of this:
_vars = _logic getVariable "_VARIABLES";
_vars pushBack "variable1";
_logic setVariable ["_VARIABLES", _vars];
_vars will be a reference to the Array and pushBack will add an element to that Array you are referring to. so pushBack is already modifying _VARIABLES. No need to set it again.
Though this is an old question I'd like to list a new answer that you can now natively create a HashMap in SQF since Arma 3 version 2.01 using the createHashMap command.
One way to create an hash table without having to create it in the mission.sqm is to create it scripting. Specifically, it is possible to write
allHashes = createGroup west; // somewhere once; `west` or `east` does not matter here.
_logic = allHashes createUnit ["LOGIC", [0,0,0], [], 0, "NONE"];
_logicVars = [];
this still requires the list of variables, and thus does not encapsulate the whole hash table in a single object. One way to achieve the hash table logic in a single object is to use
_logic = allHashes createUnit ["LOGIC", [0,0,0], [], 0, "NONE"];
_logic setVariable ["_VARIABLES", []];
and use
_logic setVariable ["variable1", 1];
_vars = _logic getVariable "_VARIABLES";
_vars pushBack "variable1";
_logic setVariable ["_VARIABLES", _vars];
This can be encapsulated in a function, but still needs 4 lines of code to get the whole thing...
Related
I basically don't look for an answer on how to do something but I found how to do it, yet want more information. Hope this kind of question is OK here.
Since I just discovered this the code of a game I'm modding I don't have any idea what should I google for.
In Lua, I can have for example:
Account = {balance = 0}
function Account.withdraw (v)
self.balance = self.balance - v
end
I can have (in another lua file)
function Account.withdrawBetter (v)
if self.balance > v then
self.balance = self.balance - v
end
end
....
--somewhere in some function, with an Account instance:
a1.withdraw = a1.withdrawBetter
`
What's the name for this "technique" so I can find some more information about it (possible pitfalls, performance considerations vs. override/overwrite, etc)? note I'm only changing withdraw for the particular instance (a1), not for every Account instance.
Bonus question: Any other oo programming languages with such facility?
Thanks
OO in Lua
First of all, it should be pointed out that Lua does not implement Object Oriented Programming; it has no concept of objects, classes, inheritance, etc.
If you want OOP in Lua, you have to implement it yourself. Usually this is done by creating a table that acts as a "class", storing the "instance methods", which are really just functions that accept the instance as its first argument.
Inheritance is then achieved by having the "constructor" (also just a function) create a new table and set its metatable to one with an __index field pointing to the class table. When indexing the "instance" with a key it doesn't have, it will then search for that key in the class instead.
In other words, an "instance" table may have no functions at all, but indexing it with, for example, "withdraw" will just try indexing the class instead.
Now, if we take a single "instance" table and add a withdraw field to it, Lua will see that it has that field and not bother looking it up in the class. You could say that this value shadows the one in the class table.
What's the name for this "technique"
It doesn't really have one, but you should definitely look into metatables.
In languages that do support this sort of thing, like in Ruby (see below) this is often done with singleton classes, meaning that they only have a single instance.
Performance considerations
Indexing tables, including metatables takes some time. If Lua finds a method in the instance table, then that's a single table lookup; if it doesn't, it then needs to first get the metatable and index that instead, and if that doesn't have it either and has its own metatable, the chain goes on like that.
So, in other words, this is actually faster. It does use up some more space, but not really that much (technically it could be quite a lot, but you really shouldn't worry about that. Nonetheless, here's where you can read up on that, if you want to).
Any other oo programming languages with such facility?
Yes, lots of 'em. Ruby is a good example, where you can do something like
array1 = [1, 2, 3]
array2 = [4, 5, 6]
def array1.foo
puts 'bar'
end
array1.foo # prints 'bar'
array2.foo # raises `NoMethodError`
I was actually looking for a way to pass by reference in AS3 but then it seemed that adobe and lots of people's understanding of pass by reference is different from what I have been taught at the university. I was taught java was pass by value and C++ allowed pass by reference.
I'm not trying to argue what pass by value and reference are. I just want to explain why I'm using pass by object in the question...
Back to the question, I would like to do something like:
public function swapCard(cardA:Cards, cardB:Cards) {
var temp:Cards = cardA;
cardA = cardB;
cardB = temp;
}
...
swapCard(c1, c2);
EDIT: adding two examples on how I'm using the swapCard function
1) in the process of swaping a card between player1 and player2's hand
swapCard(player1.hand[s], player2.hand[t]);
2) in the process of swaping a card between player1's hand and deck
swapCard(player1.hand[s], player1.deck[rand]);
In C++, we only need to add a symbol before the parameters to make it work (and we call THIS pass by reference). But in AS3, cardA and cardB are just pointers to the formal parameters. Here in the function, changing the pointers does not do anything to the formal parameters :(
I have been searching for hours but I couldn't find a way to without knowing all the properties of the Cards.
If I have to change the properties of the cards one by one then maybe I should change swapCard to a static function in class Cards? (because I don't want to expose everything to another class) I'm not sure if this is a good practice either. This is like adding a swap_cars function into class Cars. If I let this happen, what will be next? Wash car, lend car, rent car... I really want to keep the Cards class clean and holds only the details of the card. Is there a possible way to do this properly in AS3?
The kind of swap function that you're trying to implement is not possible in AS3. The input parameters are references to the input objects but the references themselves are passed by value. This means that inside the function you can change the cardA and cardB but those changes will not be visible outside the function.
Edit: I added this portion after you edited your question with sample usage.
It seems like you're trying to swap two objects in 2 different arrays at given array positions in each - you can create a function for this in AS3 but not the way you attempted.
One possible implementation is to pass the arrays themselves and the positions that you're trying to exchange; something like this:
// Assumes arrays and indices are correct.
public function SwapCards(playerHand:Array, playerCardIndex:int,
playerDeck:Array, playerDeckIndex:int):void
{
var tempCard:Card = playerHand[playerHandIndex];
playerHand[playerHandIndex] = playerDeck[playerDeckIndex];
playerDeck[playerDeckIndex] = tempCard;
}
Note that you still exchange references and the arrays themselves are still passed by reference (and the array references are passed by value - you could, if you wanted, change the arrays to new arrays inside this function but you wouldn't see new arrays outside). However, because the array parameters refer to the same arrays inside and outside the function, you can make changes to the contents of the array (or other array properties) and those changes will be visible outside.
This solution is faster than cloning the card because that involves allocating memory for a new Card instance (which is expensive) and that temporary instance will also have to be freed by the garbage collector (which is also expensive).
You mentioned in a comment that you pass cards down to lower levels of code - if you don't have a back reference to the arrays (and the positions of the cards), you will not be able to easily swap cards - in AS3, all input parameters are copies (either the copy of the value for primitive types or the copy of the reference for complex objects - changes to the input parameters in a function will not be visible outside).
EDIT: renaming the function from clone to copyFrom as pointed out by aaron. Seems like clone is supposed to be used as objA = objB.clone()
At this point, I'm adding a copyFrom() function in the Cards class such that
var temp:Cards = new Cards(...);
var a:Cards = new Cards(...);
...
temp.copyFrom(a);
...
temp will be copying everything from a.
public function swapCard(cardA:Cards, cardB:Cards) {
var temp:Cards = new Cards();
temp.copyFrom(cardA);
cardA.copyFrom(cardB);
cardB.copyFrom(temp);
}
I will wait for a week or so to see if there are any other options
You have some good answers already, but based on the comments back-and-forth with me, here's my suggestion (I use "left" and "right" naming because it helps me visualize, but it doesn't matter):
function swapCard(leftCards:Array, leftCard:Card, rightCards:Array, rightCard:Card):void {
var leftIndex:int = leftCards.indexOf(leftCard);
var rightIndex:int = rightCards.indexOf(rightCard);
leftCards[leftIndex] = rightCard;
rightCards[rightIndex] = leftCard;
}
Now you can swap the cards in the two examples you posted like this:
swapCard(player1.hand, player1.hand[s], player2.hand, player2.hand[t]);
swapCard(player1.hand, player1.hand[s], player1.deck, player1.deck[rand]);
However, note that while this swaps the cards in the arrays, it does not swap direct references to the cards in those arrays. In other words:
var a:Card = player1.hand[0];
var b:Card = player2.hand[0];
swapCard(player1.hand, a, player2.hand, b);
// does not change the references a and b, they still refer to the same card
a == player2.hand[0];
a != player1.hand[0];
b == player1.hand[0];
b != player2.hand[0];
Typically, this sort of thing is handled by dispatching a "changed" event so that any code that cares about the state of a player's hand array will know to re-evaluate the state of the hand.
There's a deep misunderstanding going on here. The question is about object reference but the PO is not trying to swap any Object reference at all.
The problem comes from the fact that the PO does not understand the difference between variable and objects. He's trying to swap variable/object reference which is not dynamically possible of course. He wants with a function to make the variable holding a reference to Object A, swap its object reference with another variable. Since Objects can be passed around but not variables (since they are just holders (not pointers)) the task is not possible without a direct use of the given variable.
To resume:
variables are not Objects!
variables hold a reference to an object.
variables cannot be passed in function or referenced in functions because THEY ARE NOT OBJECTS.
I am kinda new to AS and stumbled upon a "funny looking" feature in the documentation:
You can use the Object class to create associative arrays. At its core, an associative array is an instance of the Object class, and each key-value pair is represented by a property and its value. Another reason to declare an associative array using the Object data type is that you can then use an object literal to populate your associative array (but only at the time you declare it). The following example creates an associative array using an object literal, accesses items using both the dot operator and the array access operator, and then adds a new key-value pair by creating a new property:
Copy var myAssocArray:Object = {fname:"John", lname:"Public"};
trace(myAssocArray.fname); // John
trace(myAssocArray["lname"]); // Public
myAssocArray.initial = "Q";
trace(myAssocArray.initial); // Q
from here.
I understand that there are cases where this can be helpful, like this one but with a backround in mostly typesafe languages like Java and C# I am a little bit confused about which access-operator is common practice and why.
Normally I would go with the dot oporator, as it allows me and the compiler to keep track of all given properties and you are save regarding typos.
The code I am looking at right now uses both, with no recognizable pattern, which is even more confusing.
Any input on this? Is one better than the other? Why? When to use which one?
Normally I would go with the dot oporator, as it allows me and the
compiler to keep track of all given properties and you are save
regarding typos.
You are not safe against typos. When you create an Object, any property that you haven't defined/assigned to will just return undefined.
var awd:Object = {}
awd.aaa++ //NaN
awd ['aaa']++ //NaN
The compiler will not catch any attempt to reference a property that hasn't been defined.
I use the [] method almost exclusively because it does everything I would need the . method to do plus more. The biggest advantage for me is that I can access properties via variables.
var awd:Object = {}
var key:String = 'some_key';
awd [key] = 1;
awd.key = 5; //This will literally assign to the 'key' property, not what I want
I stumbled across this page : Create Linked List in AS3
Since AS3.0 is a scripting language. I wonder how come linked list is possible in AS3.0 ? Isn't pointer ( a way to access memory location ) mandatory to create a linked list. That ultimately makes array of data faster in performance ?
In AS3 you have object references, you don't have pointers exactly, but you can achieve a linked list using the references in a very similar way. The advantage of a Linked List (in general) is in insertion and deletion within the list (you don't have to shift all elements as in using an array). You still get this benefit using object references.
Note: Objects in AS3 are passed by reference, primitives are passed by value.
Effectively all scripting languages do work with pointers.
They only decided to call them differently (most times they call them "references") and to hide the complexity (or even possibilities) of managing the memory allocation and releasing.
Having said that the simplest way to create a linked list in ActionScript (or JavaScript) would be
var node1 = {value: 1};
var node2 = {value: "foo"};
var node3 = {value: "bar"};
//of course this code should be localices within a separate class
//with some nice API
((node1.next = node2).next = node3).next = null;
//and then use like that e.g.
var n = node1;
while (n) {
trace(n.value);
n = n.next;
}
To iterate over the properties of an Object in AS3 you can use for(var i:String in object) like this:
Object:
var object:Object = {
thing: 1,
stuff: "hats",
another: new Sprite()
};
Loop:
for(var i:String in object)
{
trace(i + ": " + object[i]);
}
Result:
stuff: hats
thing: 1
another: [object Sprite]
The order in which the properties are selected however seems to vary and never matches anything that I can think of such as alphabetical property name, the order in which they were created, etc. In fact if I try it a few different times in different places, the order is completely different.
Is it possible to access the properties in a given order? What is happening here?
I'm posting this as an answer just to compliment BoltClock's answer with some extra insight by looking directly at the flash player source code. We can actually see the AVM code that specifically provides this functionality and it's written in C++. We can see inside ArrayObject.cpp the following code:
// Iterator support - for in, for each
Atom ArrayObject::nextName(int index)
{
AvmAssert(index > 0);
int denseLength = (int)getDenseLength();
if (index <= denseLength)
{
AvmCore *core = this->core();
return core->intToAtom(index-1);
}
else
{
return ScriptObject::nextName (index - denseLength);
}
}
As you can see when there is a legitimate property (object) to return, it is looked up from the ScriptObject class, specifically the nextName() method. If we look at those methods within ScriptObject.cpp:
Atom ScriptObject::nextName(int index)
{
AvmAssert(traits()->needsHashtable());
AvmAssert(index > 0);
InlineHashtable *ht = getTable();
if (uint32_t(index)-1 >= ht->getCapacity()/2)
return nullStringAtom;
const Atom* atoms = ht->getAtoms();
Atom m = ht->removeDontEnumMask(atoms[(index-1)<<1]);
if (AvmCore::isNullOrUndefined(m))
return nullStringAtom;
return m;
}
We can see that indeed, as people have pointed out here that the VM is using a hash table. However in these functions there is a specific index supplied, which would suggest, at first glance, that there must then be specific ordering.
If you dig deeper (I won't post all the code here) there are a whole slew of methods from different classes involved in the for in/for each functionality and one of them is the method ScriptObject::nextNameIndex() which basically pulls up the whole hash table and just starts providing indices to valid objects within the table and increments the original index supplied in the argument, so long as the next value points to a valid object. If I'm right in my interpretation, this would be the cause behind your random lookup and I don't believe there would be any way here to force a standardized/ordered map in these operations.
Sources
For those of you who might want to get the source code for the open source portion of the flash player, you can grab it from the following mercurial repositories (you can download a snapshop in zip like github so you don't have to install mercurial):
http://hg.mozilla.org/tamarin-central - This is the "stable" or "release" repository
http://hg.mozilla.org/tamarin-redux - This is the development branch. The most recent changes to the AVM will be found here. This includes the support for Android and such. Adobe is still updating and open sourcing these parts of the flash player, so it's good current and official stuff.
While I'm at it, this might be of interest as well: http://code.google.com/p/redtamarin/. It's a branched off (and rather mature) version of the AVM and can be used to write server-side actionscript. Neat stuff and has a ton of information that gives insight into the workings of the AVM so I thought I'd include it too.
This behavior is documented (emphasis mine):
The for..in loop iterates through the properties of an object, or the elements of an array. For example, you can use a for..in loop to iterate through the properties of a generic object (object properties are not kept in any particular order, so properties may appear in a seemingly random order)
How the properties are stored and retrieved appears to be an implementation detail, which isn't covered in the documentation. As ToddBFisher mentions in a comment, though, a data structure commonly used to implement associative arrays is the hash table. It's even mentioned in this page about associative arrays in AS3, and if you inspect the AVM code as shown by Ascension Systems, you'll find exactly such an implementation. As described, there is no notion of order or sorting in typical hash tables.
I don't believe there is a way to access the properties in a specific order unless you store that order somehow.