When iterating an array with range, if the array is updated the updated positions do not make it into the future loop runs. The following prints "1 2" instead of "1 0"
package main
import (
"fmt"
)
func main() {
var A = &[2]int{1, 2}
for i, v := range A {
if i == 0 {
A[1] = 0
}
fmt.Print(v, " ")
}
fmt.Println()
var B = [2]int{1, 2}
for i, v := range B {
if i == 0 {
B[1] = 0
}
fmt.Print(v, " ")
}
}
https://play.golang.org/p/0zZY6vjxwut
It looks like the array is copied before it's iterated.
What part of the spec describes this behavior?
See "For statements with range clause" at https://golang.org/ref/spec#For_range
TLDR; Whatever you range over, a copy is made of it (this is the general "rule", but there is an exception, see below). Arrays are rare in Go, usually slices are used. Slice values (slice headers) contain a pointer to an underlying array, so copying a slice header is fast, efficient, and it does not copy the slice elements, not like arrays. Ranging over a pointer to array is similar to ranging over a slice in this regard.
Spec: For statements:
The range expression x is evaluated once before beginning the loop, with one exception: if at most one iteration variable is present and len(x) is constant, the range expression is not evaluated.
Arrays are values, they do not contain pointers to data located outside of the array's memory (unlike slices). The Go Blog: Go Slices: usage and internals:
Go's arrays are values. An array variable denotes the entire array; it is not a pointer to the first array element (as would be the case in C). This means that when you assign or pass around an array value you will make a copy of its contents. (To avoid the copy you could pass a pointer to the array, but then that's a pointer to an array, not an array.) One way to think about arrays is as a sort of struct but with indexed rather than named fields: a fixed-size composite value.
Evaluating an array is a copy of the entire array, it is a copy of all the elements. Spec: Variables:
A variable's value is retrieved by referring to the variable in an expression; it is the most recent value assigned to the variable.
In your first example the range expression is just a pointer to the array, so only this pointer is copied (but not the pointed array), so when you do A[1] = 0 (which is a shorthand for (*A)[1] = 0), you modify the original array, and the iteration variable gets elements from the pointed array.
In your second example the range expression is the array, so the array (with all its elements) is copied, and inside it B[1] = 0 still modifies the original array (B is a variable, not the result of the evaluation of the range expression), but v is an element of the copy (v is populated from the copied array in each iteration).
Under the hood
So how is this "copy" realized? The compiler generates code for the for range that copies (assigns) the result of the range expression to a temporary variable (if needed, because it might not always be needed: "if at most one iteration variable is present and len(x) is constant, the range expression is not evaluated").
This code can be inspected in the cmd/compile/internal/gc/range.go file.
See related article: Go Range Loop Internals
The spec says
The range expression x is evaluated once before beginning the loop, with one exception: if at most one iteration variable is present and len(x) is constant, the range expression is not evaluated.
Function calls on the left are evaluated once per iteration. For each iteration, iteration values are produced as follows if the respective iteration variables are present
The thing here is that given your loop takes more than one variable, the range expression is evaluated only once at the beginning of the iteration. Thus the value of the B[1] assigned to the v won't change.
In the case with reference, you see the modified value since the expression evaluates the reference to the B[1], which is not modified and prints the value of that referenced variable, which is actually modified.
I've this function and and I got values which I need to use from args
Run: func(cmd *cobra.Command, args []string) {
....
myFunc(args)
}
I need to pass to myFunc all the args from index 1 and not 0.
of course I can loop and create another array from index 1 but
this duplicate almost all the values except index 0 , is there a way to avoid it in GO?
Yes, simply slice the args slice, and pass that:
myFunc(args[1:])
args is a slice, not an array. You can (re-)slice slices, which will be a contiguous subpart of the original slice. For example:
args[1:4]
The above would be another slice, holding only the following elements from args:
args[1], args[2], args[3]
The upper limit is exclusive. A missing upper index defaults to the length, a missing lower index defaults to 0. These are all detailed in Spec: Slice expressions.
Note that slicing a slice does not copy the elements: it will point to the same underlying array which actually holds the elements. A slice is just a small, struct-like header containing a pointer to the underlying array.
Note that if args is empty, the above would result in a run-time panic. To avoid that, first check its length:
if len(args) == 0 {
myFunc(nil) // or an empty slice: []string{}
} else {
myFunc(args[1:])
}
a = make([]int, 7, 15)
creates implicit array of size 15 and slice(a) creates a shallow copy of implicit array and points to first 7 elements in array.
Consider,
var a []int;
creates a zero length slice that does not point to any implicit array.
a = append(a, 9, 86);
creates new implicit array of length 2 and append values 9 and 86. slice(a) points to that new implicit array, where
len(a) is 2 and cap(a) >= 2
My question:
is this the correct understanding?
As I mentioned "Declare slice or make slice?", the zero value of a slice (nil) acts like a zero-length slice.
So you can append to a []int directly.
You would need to make a slice (make([]int, 0) ) only if you wanted to potentially return an empty slice (instead of nil).
If not, no need to allocate memory before starting appending.
See also "Arrays, slices (and strings): The mechanics of 'append': Nil"
a nil slice is functionally equivalent to a zero-length slice, even though it points to nothing. It has length zero and can be appended to, with allocation.
Updated Question; Original below.
I am trying to create an array which represents a grid of cells, which have tuples containing the walls they are surrounded by.
I have come up with this:
rooms(Array) ->
Size = array:size(Array),
if
Size == ?HSIZE * ?VSIZE ->
Array;
true ->
HFactor = Size rem ?VSIZE,
VFactor = Size div ?HSIZE,
Room = {1+HFactor+11*VFactor,
7+HFactor+11*VFactor,
12+HFactor+11*VFactor,
6+HFactor+11*VFactor},
rooms(array:set(Size, Room, Array))
end.
When I run this with rooms(array:new()). I get the following array back:
{array,25,100,undefined,
{{{1,7,12,6},
{2,8,13,7},
{3,9,14,8},
{4,10,15,9},
{5,11,16,10},
{12,18,23,17},
{13,19,24,18},
{14,20,25,19},
{15,21,26,20},
{16,22,27,21}},
{{23,29,34,28},
{24,30,35,29},
{25,31,36,30},
{26,32,37,31},
{27,33,38,32},
{34,40,45,39},
{35,41,46,40},
{36,42,47,41},
{37,43,48,42},
{38,44,49,43}},
{{45,51,56,50},
{46,52,57,51},
{47,53,58,52},
{48,54,59,53},
{49,55,60,54},
undefined,undefined,undefined,undefined,undefined},
10,10,10,10,10,10,10,10}}
Which is quite close to the desired result, but there are two things I can't quite put my finger on (The numbers are correct). Why does it look like it is split up into multiple subarrays? What are those undefineds and 10's doing there? These are mostly due to my lack of erlang knowledge, because array:get produces the expected results, but I couldn't find anything which explains where they come from.
Original Question
rooms(Array) ->
Size = array:size(Array),
if
Size == 5 ->
Array;
Size rem 5 == 0 ->
rooms(array:set(Size, array:new(), Array));
true ->
In_Array = array:get(array:size(Array), Array),
In_Size = array:size(In_Array),
Room = {1+In_Size+11*In_Size,
7+In_Size+11*In_Size,
12+In_Size+11*In_Size,
6+In_Size+11*In_Size},
New_In = array:set(In_Size, Room, In_Array),
rooms(array:set(Size, New_In, Array))
end.
I call it with rooms(array:new()). but the result is
** exception error: bad argument
in function array:size/1 (array.erl, line 317)
in call from framework_kamer:rooms/1 (framework_kamer.erl, line 195)
Which makes sense because In_Array is not an array, but undefined. However, I can't figure out why.
Side question, is there an easier/cleaner/better way to do this?
You are (in the second call of the recursion) trying
In_Array = array:get(array:size(Array), Array),
As array is zero-indexed this will always fail, as the access will be always be off by one. Change this line to
In_Array = array:get(array:size(Array) - 1, Array),
and you are fine.
A few comments on your code:
Conventional variable naming in Erlang would be CamelCase without underscores (i.e. InArray)
Expressing a 2-dimensional array as nested arrays is almost never a good idea. Linearalise it by writing simple wrappers that recalculate a one-dimensional index from x and y as index = y * max_x + x.
Concatenating an element x to an array items is easy in D, it's as if it were an array list:
arr ~= x;
but how do I remove an element at index i from items?
(Caveat: If I remove an element and then add a new element, the array must not be reallocated. So a simple slice won't work.)
Update:
Based on CyberShadow's answer about using assumeSafeAppend, I wrote this code:
static void removeAt(T)(ref T[] arr, size_t index)
{
foreach (i, ref item; arr[index .. $ - 1])
item = arr[i + 1];
arr = arr[0 .. $ - 1];
arr.assumeSafeAppend();
}
However, the problem happens when you have something like:
auto superArr = [0, 1, 2, 3, 4]; //Must not be modified
auto arr = superArr[0 .. $ - 1];
writeln(superArr);
arr.removeAt(0); //Should copy the slice and modify the copy
writeln(superArr); //but obviously doesn't
The base array of slice should not be modified if an element is removed from the slice; instead, the slice needs to be copied.
But I have no way of knowing if an array is a slice of a bigger array... so that doesn't work.
Any suggestions?
Copying my answer on digitalmars.D (thanks for forwarding):
As has been mentioned, std.algorithm.remove can be of help. You may want to look at three of its capabilities in particular: (a) remove multiple offsets in one pass, e.g. remove(a, 0, 4) removes the first and fifth element, (b) you can remove subranges, e.g. remove(a, tuple(1, 3)) removes the second through fourth element, and (c) if you don't care about the order in which elements are left after removal you may want to look into unstable remove, which does considerably less work.
Andrei
(Caveat: If I remove an element and then add a new element, the array must not be reallocated. So a simple slice won't work.)
The assumeSafeAppend function will tell the runtime not to reallocate the array when appending to it (i.e. it is an affirmation from the user that there aren't other slices which might be stomped by an append).
remove from std.algorithm does an in-place remove. If you're using std.container, there's also Array.linearRemove.
Well if order is of no importance you can copy the last element to the location of removal then reduce the array length by one.
If you just want to remove the first or last elements use slices:
array = array [1..$]
array = array [0..$-1]
Or a general way which works for a middle one as well:
array = array [0..unlucky] ~ array [unlucky+1..$]
If the elements aren't basic elements such as structs, floats, ints then arrays are implicitly arrays of pointers and this is an efficient operation.
There's no automated way of doing this, you'll have to shuffle the array items along, reset .length and then catenate.