Any comparison of an array fails with an error Generic Error, all other operators return false or do nothing
Description
Details
- Legacy ID
- 2215735118
- Severity
- None
- Resolution
- Open
- Reproducibility
- Always
- Category
- Feature Request
This seems like an arbitrary limitation. Arrays should be compared and manipulated by reference as follows:
arrayOfArrays = [-100, [2], [3]]; arrayWith1 = [1]; arrayOfArrays set [0, arrayWith1]; // now this array becomes [arrayWith1, [2], [3]] or [[1], [2], [3]] arrayWith1 == [1]; // false, `[1]` creates new array with a new reference arrayWith1 == arrayOfArrays select 0; // true arrayWith1 in arrayOfArrays; // true arrayOfArrays - arrayWith1; // [[2], [3]]
Event Timeline
It was always like this, it is not a bug. You cannot compare arrays or booleans. If you need to compare arrays use str and compare strings
str _array1 == str _array2
I know, Killzone, but if it would be possible to compare arrays by <i>reference</i>, scripts could be cleaner. Im not asking for the actual value checking.
Array reference is nothing but an integer, pointing to the memory or just some internal index of arrays, comparison would take the same ammount of time as comparing two integers.
This restriction is pointless, every other language can do this.
Every other language? Lemmie see. Javascript..
<script>
arr1 = [1,2,3];
arr2 = [1,2,3];
if (arr1 == arr2) {alert ('match')} else {alert ('no match')};
</script>
Result is "no match"
Killzone, did you actually read what I wrote and do you have an understanding of what a reference is?
This is equivalent JS code:
var a = [1,2,3];
var b = a;
if (a == b) {alert ('match')} else {alert ('no match')};
var c = [a]
if (a == c[0]) {alert ('match')} else {alert ('no match')};
This produces two dialogs with the text "match". This functionality is shared by Java, C, C++, C#, Python, x86 assembly, and many, many others. It is called reference equality, and is an artifact of the underlying implementation of the language. Computers implement arrays by having contiguous blocks of memory containing the array values. What is stored in the variable is actually the integer offset into memory.
This is best exemplified by how C handles array accesses. The syntax x[n] in C is equivalent to (x + n)*, or adding n to the pointer x and dereferencing the memory location. The array that the programmer interacts with is x, but x is just the integer memory location where the array is stored.
This ticket is asking for the ability to compare arrays by their reference pointer. In the above javascript example, a, b, and c[1] all have the same integer value, and logically should be equivalent.
@ckfinite Thank you for the explanation. In SQF it kinda works like this already:
a = [1,2,3];
b = a;
b set [3,4];
hint str a; [1,2,3,4]
b resize 2;
hint str a; [1,2]
You just cannot compare arrays. Language differences aside, why and when we might need what OP is asking for? Any practical examples when this could be useful?
I think I will have to generalize this ticket a bit, because what I'm looking for is acceptance of an array as a parameter by some of the operators. Examples of what would be possible are:
arrayOfArrays = [1, [2], [3]]; arrayWith1 = [1]; arrayOfArrays set [0, arrayWith1]; arrayWith1 == [1]; // false, `[1]` creates new array with a new reference arrayWith1 == arrayOfArrays select 0; // true arrayWith1 in arrayOfArrays; // true arrayOfArrays - arrayWith1; // [[2], [3]]
Because array is the only data structure in SQF it would make sense to make operators recognize it as a sort of object, like other object oriented languages do.
I ran into this while making a patrol route planning script. For example:
if (count (_probes select _i select 1) > count (_probes select _max select 1)) then { _max = _i; }; /* */ _maxA = _probes select _max; for [{_i = 0}, {_i < count (_maxA select 1)}, {_i = _i + 1}] do { _probes set [_maxA select 1 select _i, -1]; }; _probes set [_max, -1]; _probes = _probes - [-1];
could be changed to:
if (count (_probes select _i select 1) > count (_max select 1)) then { _max = _probes select _i; }; /* */ for [{_i = 0}, {_i < count (_max select 1)}, {_i = _i + 1}] do { _probes = _probes - [_max select 1 select _i]; }; _probes = _probes - _max;
This change doesn't affect backwards compatibility, unless someone was relying on generating an error (and that's just plain wrong).
I think isEqualTo is exactly that after he has sought.
I would close the issue because i think all questions were answered.
Special thanks to Killzone_Kid
No, isEqualTo compares by value, which has performance penalty. This is not resolved.
https://community.bistudio.com/wiki/Code_Optimisation#Comparing_arrays
edit:
Arrays in SQF are always compared by value. They do not have a checksum or a tag, nor a reference pointer. Therefore, Arrays MUST be compared by value. SQF is not a low-level language.
edit 2: You may wite a simple function to calculate a checksum for your arrays and compare them.
This might be even faster as comparing by value (especially for very large arrays)
mp5gosu, if arrays are not references explain how this code works:
a = [1,2,3]; b = a; b set [3,4]; hint str a; //[1,2,3,4] b resize 2; hint str a; //[1,2]
Variables are referenced, sure. BUT this doesn't imply that the underlying layer allows 1:1 referencing to the heap in memory. This is an engine-secret at low level. There may be other service layers lying in between SQF and ASM denying the usage of direct comparing. So actually no one (but BIS) knows, what's happening inside. It may be implementable or not. If it makes sense? I don't know. :)
a = [];
b = a; a == b; // ERROR
This is top notch madness, and I'm asking for it to be fixed.
It may also be in the operators-implementation. ;)
edit: Upvoting because it makes sense to me, to have a very fast and native way of comparing arrays...
isEqualTo is very fast, I dont understand what is the problem. You can compare your left foot to orange juice if you want and it will not throw an error. Pretty advanced command. But as I said in another ticket about the same problem, the chance of == changing behaviour is very slim.
Things that don't work: array subtraction, in operator. Also, isEqualTo will return a false-positive when comparing arrays with same contents.
Array subtraction is a bit sketchy, especially if applied to ordered lists (e.g. arrays). If there is a special set datatype, then that should support subtraction, but not arrays. The same goes for the in operator, especially as over arrays it's necessarily a O(n) operation, whereas a hashtable or tree backed set implementation may provide O(1) or O(log n) performance.
Is isEqualTo an O(n+m) or O(1) operation over the elements in the arrays? For many applications, constant-time reference equality is required, and it sounds as if isEqualTo is a linear time algorithm.
I also want to add that there must be a reference pointer somewhere - even if they're linked lists or something more exotic, they're still in memory somewhere, and that can be used to do O(1) comparisons. Basically every other language (even most functional ones) have this, because it's very convenient.
in and array subtraction, or I should rather say *element removal from an array*, works with other data types, complex or not.
"Also, isEqualTo will return a false-positive when comparing arrays with same contents."
are you implying that [1,2,3] isEqualTo [3,2,1] can return true? It doesn't. It preforms strict comparison, so unless arrays are identical you will get false.
He's saying that [1,2,3] isEqualTo [1,2,3]. The two arrays have different memory pointers (unless you have a *very* aggressive optimizing compiler), yet isEqualTo returns true.
This indicates that isEqualTo is linear time, as well.
"yet isEqualTo returns true"
Yet? I don't understand this. It supposed to return true because arrays are identical.
Also not sure what OP means by arrays are rejected by "in" operator.
Syntax for in is
ANY in ARRAY
and [1,2,[3]] in 1,2,[3] returns true
Identical is not equivalent to reference equality. Let's assume that isEqualTo implements reference equality, and consider a trivial IL transformation of the earlier code, as would happen in a standard compiler.
[1,2,3] isEqualTo [1,2,3]
becomes
a=[1,2,3]
b=[1,2,3]
rval = isEqualTo a, b
Now, let's assume that the compiler is smart enough to know that a and b are not aliased in isEqualTo, and stores their arrays in program memory. Then, this would become a notional in-memory form (with made-up indices) of
0x60000: 1
0x60004: 2
0x60008: 3
0x6000C: 1
0x60010: 2
0x60014: 3
Then, a = 0x60000 and b = 0x6000C. Then, while every element in the arrays is the same, a != b (assume that a = b, then 0x60000 = 0x6000C iff 0 = 12, which is patently false). Therefore, a isEqualTo b, if isEqualTo implements reference equality, should be false.
However, isEqualTo clearly does not implement reference equality. In the above example, it doesn't compare 0x60000 and 0x6000C, rather, it compares 1=1, 2=2, and 3=3. This means that it's arbitrarily slower (in big omega of n) than the reference equality implementation - to see why, make two identical arrays of a billion elements, and run isEqualTo on them. The notional reference equality implementation is instant - it doesn't depend on the size of the arrays, but isEqualTo could take a very long time indeed.
Note that the actual implementation of the arrays will cause code that looks more like
a=Array.create(1,2,3)
b=Array.create(1,2,3)
to be generated. This means that the pointers are actually looking at heap elements, but the same idea holds.
I now understand what you're saying, ckfinite. So when one would need to know if a variable references the same array as another variable? Could you give a practical example where this is necessary (in terms of Arma scripting of course)?
I'm not actually that familiar with Arma scripting, though the use cases for this are pretty clear generally.
There are two big cases that reference equality is helpful: when comparisons are expensive, and when arrays are large. I don't think you can define your own equality and objects, so the first case doesn't apply to Arma. However, the second case can apply.
Suppose you have an array with a hundred million elements, and you want to see if an array that was just given to you is the same array. If you use isEqualTo, it will have to compare every single one of those hundred million elements, which, if each comparison can happen in a millionth of a second, will take 100 seconds. In contrast, a reference equality implementation would take a millionth of a second.
The way I see it, there is only one case where this could be useful, is when you don't want to modify some other var by mistake because it points to the same array you are modifying. But in this case
a. You should know better you have used a = b somewhere
b. To be on the safe side, use + operator and make copy when you need to copy array a = +b instead of a = b
It is useless for checking if array was modified because you will need to compare arrays by value and this means looking through all elements of an array. In most cases you will be comparing your array with another array which by default wouldn't be the same array.
Killzone_Kid, are you purposefully ignoring my notes? I already gave an example here http://feedback.arma3.com/view.php?id=14917#c55687
This miniature change breaks nothing and allows for a much cleaner and readable code. As mp5gosu outlined in http://feedback.arma3.com/view.php?id=14917#c70848 this is not fixable from user code, it's a change that has to be made to the script interpreter.
"Killzone_Kid, are you purposefully ignoring my notes?"
I think you might find that:
arrayWith1 in arrayOfArrays; true
arrayOfArrays - arrayWith1; [[2], [3]]
works already
You're changing the subject, but that's the least of my worries.
What I've asked with this ticket was a unified and consistent way of array handling by various operators. None of the examples worked eight months ago, and I haven't checked since then, because no note in this ticked suggested otherwise. I will check in the morning.
Given arrays are passed by reference, I don't see why this couldn't be implemented rather easily (it's not like we need the full package of pointer arithmetic or anything...).
And yes, this would be definitely a welcome addition. Or have you ever tried to implement more elaborate data structures on top of arrays (e.g. a binary tree or what not)? Currently using a "sentinel"-node is not possible, and instead we have to use some awkward construct; an empty array is the best option, I guess, but it's tedious and makes for rather ugly code.
@Killzone_Kid: oh please... :3