Kotlin - Val cannot be reassigned , but can be reassigned by accessing from index? - arrays

I have these two functions where i am trying to modify the elements. One of them compiles and other says 'val cannot be reassigned'. What is the difference between the following functions? Why does one compile and the other does not?
The one that compiles
fun <T> Array<T>.mapInPlace2(transform: (T) -> T) {
for (i in this.indices) {
this[i] = transform(this[i])
}
}
The one that says
Val cannot be reassigned
fun <T> Array<T>.mapInPlace1(transform: (T) -> T) {
for (i in this) {
i = transform(i);
}
}

Similiarly to how function parameters are final in Kotlin, so are the variables used in for loops. Essentially, writing down...
for (i in array) {
...
}
... is the equivalent of doing this in Java:
for (final int i : array) {
...
}
This helps catch some common errors, in this case - if the compiler allowed you - you'd be reassigning this local variable that just contains a reference to the real element, without changing the array. In Java terms, you'd be doing this:
for (int i : array) {
i = transform(i);
}
This new value of i is unused, it doesn't change the array itself, and it will be immediately overwritten by the value of the next element when the loop comes around.

Try this:
for(i:Int in 0 until this.size) {
this[i] = "your value"
}

You're confusing the mutability of references to objects and objects themselves.
In the first example, the structure is mutable, but the reference to it is immutable. You can change the structure of the object itself, but you can't change what structure the references points to.
In the second example, you're trying to change an immutable reference to an object, not the object itself.
If you write
val i = obj
obj can still be mutated if it's a mutable object. i can't be reassigned though, sine the reference can't be changed.

Related

C# - Tuple arrays are mutable, but tuple lists are not. How do I get around this?

I have an array of value pairs I want to modify. I need to add and remove values from this array as well, so I used a list. When I tried to use a list, I encountered an error.
Error CS1612 - Cannot modify the return value of 'List<(int, float)>.this[int]' because it is not a variable
So I decided I would investigate. I tried using an array instead, and it... worked fine? The following code only throws an error on arr1[0].Item1 += 1;.
static void Main()
{
List<(int, float)> arr1 = new List<(int, float)>() { (0, 0) };
(int, float)[] arr2 = new (int, float)[1];
arr1[0].Item1 += 1; // This line
arr2[0].Item1 += 1;
}
Why are tuple arrays mutable, but lists are not? Is this because arrays are simple blocks of data you can modify easily, but lists have a lot of backend behind them that complicates things? Is there a simple way to get around this, or am I going to have to make my own custom class?
Why are tuple arrays mutable, but lists are not?
The list itself is mutable, but not in the way you're doing it. Note that this isn't anything specific to tuples - it's just the case for any mutable struct.
The list indexer getter returns a value (i.e. a copy of the tuple in your case) - so modifying that value wouldn't modify the copy in the list. The compiler is trying to avoid you making a change to a value that's about to be thrown away. Array access doesn't do that - arr2[0] refers to the variable within the array. (An array is effectively a collection of variables.)
If you want to mutate the list, you can have to fetch the tuple, mutate it, then put it back:
var tuple = arr1[0];
tuple.Item1++;
arr1[0] = tuple;
Note that this also explains why you can't use list access expressions as arguments for ref parameters, but you can do the equivalent for arrays:
public void Method(ref int x) => x++;
public void CallMethod()
{
var list = new List<int> { 0 };
var array = new int[] { 0 };
Method(ref list[0]); // Error
Method(ref array[0]); // Valid
}

Swift Struct instance get changed on updating it's value

I have observed that when we do change a property value of struct, then a new struct object is created.
struct StructureTest {
var i: Int
mutating func changeValue(_ val: Int) {
i = val
}
}
var st = StructureTest(i: 10) {
didSet{
print("struct changed")
}
}
print("before: \(st)")
st.changeValue(20)
print("after: \(st)")
Output:
before: StructureTest(i: 10)
struct changed
after: StructureTest(i: 20)
Main thing I have noticed that after changing it's property value. A new struct object is created and assigned to var st.
I do know what are "value types" and what is "copy on assign" feature.
But I am unable to understand that why is it happening here?
Maybe I am missing something here?
Let me know if you do know the reason for this new struct instance creation.
Along side with this I have observer one more thing that:
If I have a array of structs. Like as:
struct StructureTest {
var i: Int
mutating func changeValue(_ val: Int) {
i = val
}
}
var arrStructs = [StructureTest(i: 10), StructureTest(i: 20)] {
didSet {
print("arrStructs changed")
}
}
arrStructs[0].changeValue(30)
Output:
arrStructs changed
I am unable to understand that why is array modifying?
As much I can understand from "copy of write" feature on value types.
It should happen when an array is modified and array capacity requires to create a new array but in this case array modification reason is not clear to me.
Do let me know If you do know the reason behind that or if you can provide me any reference for clarification.
Sorry for my grammatical mistakes. Hope the essence of problem is clear to you.
The structs have value semantics, so when you mutate a struct you have assigned a new value to it; ie you changed the value that the variable holds. Array in swift is a generic struct and it therefore also has value semantics (unlike most languages where array is a reference). Therefore if you mutate any element of the array, you have changed the value of the entire array.

Storing a reference to array in swift

I want to pass an array to an object and store a reference to this array. I want to be able to modify this array within this object and make sure that it's modified everywhere else.
Here is what I am trying to accomplish (how the code doesn't work)
class Foo {
var foo : Array<Int>
init(foo: Array<Int>) {
self.foo = foo
}
func modify() {
foo.append(5)
}
}
var a = [1,2,3,4]
let bar = Foo(a)
bar.modify()
print(a) // My goal is that it will print 1,2,3,4,5
My findings so far
A) The array (by default) are passed strange way. It's a reference until you modify an array length. As soon as you modify a length it will be copied and modified. As result, if I append or delete anything from it in the object it won't be seen outside
B) I can use inout on a function parameter. This will allow me to modify it within this function. However, as soon as I will try to assign it to some object member I am again struck by A)
C) I can wrap an array in some Container class. This probably is the cleanest way. However, I serialize/deserialize these objects and I would rather not put it in Container (because I will have to work around some things for serialization and deserialization and sending it to the server).
Are there anything else? Am I missing some Swift construct which allows me to do that?
You'll have to use an NSArray or NSMutableArray for this because Swift Arrays are value types so any assignment will make a copy.
You could make use of Swifts (very un-swifty) UnsafeMutablePointer.
Since (from your post) the behaviour references to arrays can't really seem be trusted, instead keep an UnsafeMutablePointer companion to the class inner array foo as well as any "external" arrays that you want to be binded to foo, in the sense that they are both just pointers to same address in memory.
class Foo {
var foo : [Int]
var pInner: UnsafeMutablePointer<Int>
init(foo: [Int]) {
pInner = UnsafeMutablePointer(foo)
self.foo = Array(UnsafeBufferPointer(start: pInner, count: foo.count))
}
func modify(inout pOuter: UnsafeMutablePointer<Int>) {
foo.append(5) // <-- foo gets new memory adress
pInner = UnsafeMutablePointer(foo)
pOuter = pInner
}
}
var a = [1,2,3,4] // first alloc in memory
var pOuter: UnsafeMutablePointer<Int> = UnsafeMutablePointer(a)
var bar = Foo(foo: a) // 'bar.foo' now at same address as 'a'
print(bar.foo) // [1,2,3,4]
bar.modify(&pOuter) // -> [1,2,3,4,5]
a = Array(UnsafeBufferPointer(start: pOuter, count: bar.foo.count))
/* Same pointer adress, OK! */
print(bar.pInner)
print(pOuter)
/* Naturally same value (same address in memory) */
print(bar.foo)
print(a)
Pointers can be dangerous though (hence the fitting type name), and, again, very un-swifty. Anyway...
/* When you're done: clear pointers. Usually when using
pointers like these you should take care to .destroy
and .dealloc, but here your pointers are just companions
to an Array property (which has a pointer an reference
counter itself), and the latter will take care of the
objects in memory when it goes out of scope. */
bar.pInner = nil
pOuter = nil
Now, what happens when either a or foo goes out of scope, will it break the variable that are not out of scope, or does Swift contain some clever reference counting that realises a memory address is still in use? I haven't investigated this, but feel free to indulge yourself in that.
From the Swift Programming Language,
Structures are always copied when they are passed around in your code, and do not use reference counting.
If you examine the contents of the array variable, you will see that indeed the append works:
class Foo {
var foo : Array
init(_ foo: Array) {
self.foo = foo
}
func modify() {
foo.append(5)
}
func printFoo() {
print("self.foo: \(foo)")
}
}
let a = [1,2,3,4]
let bar = Foo(a)
bar.modify()
bar.printFoo()
print("a: \(a)")
produces
self.foo: [1, 2, 3, 4, 5]
a: [1, 2, 3, 4]
You have taken a copy of a, not a reference to a.
a is declared a constant hence cannot be modified. If you are planning to modify the contents of a, declare it as a variable. i.e.,
var a = [1,2,3,4]
I haven't tested this but, as you are using a class to wrap the array, I see no reason why the following would not work.
class Foo {
var foo : Array<Int>
init(foo: inout Array<Int>) {
self.foo = foo
}
func modify() {
foo.append(5)
}
}
let a = [1,2,3,4]
let bar = Foo(&a)
bar.modify()
print("a: \(a)") // a: [1,2,3,4,5]

Getting the indexes of NSButtons in an array and appending them to an array of integers in swift (Cocoa)

I'm super new to programming so this may be a pretty basic question.
I have an array of NSButtons (checkboxes) in my ViewController that I am calling "buttonArray." I want to go through the array, find out which buttons are checked and add the indexes of those checked buttons to another array of integers (located in a struct), which I am calling "intArray." This is the struct:
struct GetInterval {
var intArray = [Int]()
mutating func putIntIntoArray (intToAdd:Int) {
self.intArray.append(intToAdd)
}
}
I have tried doing this two ways (shown below), both of which have given me compiler errors.
First, I tried to use a function to import the index as an Int and add it to the "intArray" array...
for interval in buttonArray {
if interval.state == NSOnState {
var intervalIndex = find(buttonArray, interval)
GetInterval.putIntIntoArray(Int(intervalIndex!))
}
}
...which gave me the error: "Cannot invoke 'putIntoArray' with argument list of type ((int))"
When that didn't work, I tried appending it directly from the "if" statement in the ViewController...
for interval in buttonArray {
if interval.state == NSOnState {
var intervalIndex = find(buttonArray, interval)
GetInterval.intArray.append(intervalIndex!)
}
}
...which gives me the error: "GetInterval.Type does not have a member named 'intArray'"
How can I fix this?
When you say GetInterval.(something), this refers to a static member (a.k.a., something owned by the GetInterval type). However, your intArray is a member owned by each instance of the GetInterval type. So you need to create an instance first. For example:
var getInt = GetInterval() // create a new instance
getInt.putIntIntoArray(...) // modify this instance, by calling a mutating function
getInt.intArray.append(...) // modify this instance, by directly accessing a property

List.forEach unable to modify element?

I'm attempting to create a list of lists to imitate the functionality of a 2D array in Dart, and I was having trouble for a while figuring out how to make it work.
I originally used List.forEach in order to create the lists and fill them, but after each loop, it's as if I never created the lists. Here's what that code looked like:
currentMap = new List<List<int>>(height);
currentMap.forEach((e) {
e = new List<int>(width);
e.forEach((f) {
f = 1;
});
});
Printing currentMap resulted in a list of null. Here's what I have now:
currentMap = new List<List<int>>(height);
for(int i = 0; i < height; i++) {
currentMap[i] = new List<int>(width);
for(int j = 0; j < width; j++) {
currentMap[i][j] = 1;
}
}
This works exactly as I expected it to.
I understand that this is probably a very basic issue, and I am assuming that forEach does not allow you to modify the element, but I wanted some confirmation on that, and the Dart API docs do not specify except with the phrase "apples the function f to each element..." which may just not be clear to me.
Also, I am aware of things like the List.filled() constructor -- this is just how I am starting to build towards other functionality.
EDIT: Okay, I think I understand now. Arguments are "pass-by-sharing" which means that a copy of a reference to the object is made. This means that you can modify a member of a mutable object that is pointed to by the argument (as follows):
void function(MyObject o) {
o.x = 5;
}
but trying to change what o points to will not change the argument after exiting the function, such as this:
void function(MyObject o) {
MyObject p = new MyObject();
o = p;
}
Dart does not have variable references, all elements are passed as a reference to an object, not to a variable. (In other words, Dart is purely "call-by-sharing" like both Java and JavaScript).
That means that the e parameter to the forEach callback is just a normal local variable, and assigning to it has no effect outside the callback. The same goes for iterators: they return the value in the iterable, but it has no reference back to the iterable after that.
The double loop is what you want when you do "read-modify-write". You read a value from the list, modify it and store it back into the list using the list's index-set operator.
I know you know about List.filled, but just for other readers, I'll give the one-liner to create the two-dimensional array initialized to 1 values:
currentMap = new List.generate(height, (_) => new List.filled(width, 1));
This is because e is only a reference to the List in currentMap not to a currentMap item.
You update a copy of the reference to a List, but this reference has no connection to currentMap.
After the loop, e just gets garbage collected.
This is because of how an Enumerator works and I believe because the Enumerator is immutable You can't modify the collection items when it's being processed in a foreach... removing members and such.
See also:
http://msdn.microsoft.com/en-us/library/ttw7t8t6.aspx
What is the best way to modify a list in a 'foreach' loop?

Resources