Does Swift have quadratic string concatenation when using var? - swift

In the Swift Language Reference, under String Mutability it says:
You indicate whether a particular String can be modified (or mutated) by assigning it to a variable (in which case it can be modified), or to a constant (in which case it cannot be modified)
It's unclear to me if the "it" that is mutable is the variable or the value.
For example, if I write:
var s = ""
for i in 0...100 {
s += "a"
}
Is it akin to creating an NSMutableString and calling appendString 100 times (i.e. linear cost)?
Or is it akin to creating a series of ever-larger NSString instances and combining them with stringByAppendingString (i.e. quadratic cost)?
Or perhaps it creates some kind of rope structure behind the scenes, so it's immutable and linear in aggregate?

Appending to a collection like this (while String is not itself a collection, you're essentially appending to its characters view with that code) is linear, not quadratic. A string in Swift has an internal buffer whose size is doubled whenever it fills up, which means you will see fewer and fewer reallocations as you repeatedly append. The documentation describes appending in this way as an "amortized" O(1) operation: most of the time appending is O(1), but occasionally it will need to reallocate the string's storage.
Arrays, sets, and dictionaries have the same behavior, although you can also reserve a specific capacity for an array (using reserveCapacity(_:)) if you know you'll be appending many times.
All these collections use "copy-on-write" to guarantee value semantics. Here, x and y share a buffer:
let x = "a"
let y = x
If you mutate x, it gets a new, unique copy of the buffer:
x += "b"
// x == "ab"
// y == "a"
After that, x has its own buffer, so subsequent mutations won't require a copy.
x += "c" // no copy unless buffer is full

Related

How to shuffle such that two same elements are not together?

I have a string containing several elements, some identical and some unique. I want my code to check every 2 following elements in my string and if they're equal, it should call a function ShuffleString, where the input variable (randomize) is the string itself, that will re-shuffle the string in a new position. Then, the script should re-check every 2 following elements in the string again until no two identical elements appear next to each other.
I have done the following:
My function file ShuffleString works fine. The input variable randomize, as stated earlier, contains the same elements as MyString but in a different order, as this was needed on an unrelated matter earlier in the script.
function [MyString] = ShuffleString(randomize)
MyString = [];
while length(randomize) > 0
S = randi(length(randomize), 1);
MyString = [MyString, randomize(S)];
randomize(S) = [];
end
The script doesn't work as intended. Right now it looks like this:
MyString = ["Cat" "Dog" "Mouse" "Mouse" "Dog" "Hamster" "Zebra" "Obama"...
"Dog" "Fish" "Salmon" "Turkey"];
randomize = MyString;
while(1)
for Z = 1:length(MyString)
if Z < length(MyString)
Q = Z+1;
end
if isequal(MyString{Z},MyString{Q})
[MyString]=ShuffleString(randomize)
continue;
end
end
end
It just seems to reshuffle the string an infinite amount of times. What's wrong with this and how can I make it work?
You are using an infinite while loop that has no way to break and hence it keeps iterating.
Here is a simpler way:
Use the third output argument of the unique function to get the elements in numeric form for easier processing. Apply diff on it to check if consecutive elements are same. If there is any occurrence of same consecutive elements, the output of diff will give at least one zero which when applied with negated all will return true to continue the loop and vice versa. At the end, use the shuffled indices/numeric representation of the strings obtained after the loop to index the first output argument of unique (which was calculated earlier). So the script will be:
MyString = ["Cat" "Dog" "Mouse" "Mouse" "Dog" "Hamster" "Zebra" "Obama"...
"Dog" "Fish" "Salmon" "Turkey"]; %Given string array
[a,~,c] = unique(MyString);%finding unique elements and their indices
while ~all(diff(c)) %looping until there are no same strings together
c = ShuffleString(c); %shuffling the unique indices
end
MyString = a(c); %using the shuffled indices to get the required string array
For the function ShuffleString, a better way would be to use randperm. Your version of function works but it keeps changing the size of the arrays MyString and randomize and hence adversely affects the performance and memory usage. Here is a simpler way:
function MyString = ShuffleString(MyString)
MyString = MyString(randperm(numel(MyString)));
end

Assigning last array element to a variable in Swift

I have this very simple line of code
var dblArray : [Double] = [0.01]
var x = dblArray.last
println(x * x)
The '.last' module returns the last element of the array, which is 0.01. However, based on the playground assistant view, it shows that the actual assignment to var x is (Some 0.01). And doing a println will lead to "Optional 0.01"
What I'm hoping to accomplish is merely capturing the value of the last element and placing it in x.
What am I doing wrong here?
I'm pretty certain .last would have to be an optional, if only to handle the edge case of an empty array, where .last would make no sense as a "solid" value.
In any case, if you're sure the array won't be empty, just unwrap the value. If you're not sure then you'll need to check intelligently such as with:
var x = 0
if let junk = dblArray.last {
x = junk
}
I think that's the correct syntax, I don't have my Mac with me at the moment, but it should hopefully be close enough to show the concept.

Mutable reference to immutable data

I often time hear the term "Mutable reference to immutable data". In my case this was for Scala.
If you have a mutable reference, wouldn't this imply that the immutable data is mutable? I am having hard time understanding the theory and practical aspect of it. Example would be great.
It means you can mutate the reference (change what it refers to) but not mutate the data (change what's behind the reference). The difference matters as soon as there are multiple references to the data, which happens all the time in a language like Scala (assignment, parameter passing, adding to collections, etc.). For example:
var x = List(1);
var y = x;
x = List(2);
// y.head == 1
// x.head == 2
Note that this distinction applies even to Java:
String x = "foo";
String y = x;
x = "bar";
// y.equals("foo")
// x.equals("bar")
Note that in both examples, we mutated the references x and y, but we didn't (and in fact couldn't) mutate the objects they refer to.

What is the time complexity of this hash function?

I have a hashing function and I want to know if it is constant. Since the length of the array word is constant, does that mean the function is constant in Big O notation?
public int hash(String s) {
if (s.length() > 7)
return -1;
for (int i = 0; i < word.length; ++i) {
if (word[i].compareTo(s) == 0)
return i;
}
return -1;
}
Since the length of the array word is constant, does that mean the function is constant in Big O notation?
Big O is used to describe how the run time or memory consumption of a process grows as its input grows. If your array is of constant length, then it will not grow and have an effect. Therefore, you can in this context consider hash() to run in O(1), assuming that the string comparisons are done in relatively constant time.
One way to think about it would be to say that since the length of the array is not variable, it should always be possible to "unroll" that loop so as to have a fixed number of O(1) comparisons one after the other, which all-in-all will still be O(1). Again, this presumes that the time taken to compare the strings is also constant (which in reality may not be the case if you have very large strings of varying lengths). Of course, if you know that the contents of the array will also be constant in addition to its length, then you can say for certain that the function will be O(1).
The time required to compare two strings of lengths m and n is O(min{m, n} + 1). Let's suppose that k is the length of the word array and that m is the length of the longest word in word and n is the length of the input string. In this case, the function does O(k) string comparisons, each of which take time O(min{m, n} + 1). Therefore, the runtime is O(k min{m, n} + m).
Now, since m is known to be a constant, we can simplify this and say that the runtime will be O(min{m, n} + 1). If all of the strings in word are fixed constants, then m is a constant and the runtime is O(min{1, n} + 1) = O(1) and your hash function runs in constant time. Otherwise, if they're unboundedly long, the only thing you can claim is that the runtime is O(min{m, n} + 1).
Hope this helps!
This function is O(1) if word is constant.
s.length() runs in constant time regardless of the length of s.
The time it takes to run word[i].compareTo(s) is bounded by the length of word[i]. As long as word doesn't change, this means there is an upper bound for the time it takes to run the entire for loop.
So there's an upper bound on the time this function takes to run, and the function is O(1).
If word can change, I believe this function would be O(n) where n is the size of word. However, if the elements of word have increasing lengths, word[i].compareTo(s) will be bounded by larger and larger numbers, so the length of s might begin to matter. Perhaps the complexity is actually O(n^2). I don't know, and now I'm curious myself.
your function has complexity O(N2), as it has 2 inputs:
s - your string (length N1)
word - array (length N2)
so, you complexity will be O(N1*N2), which can be simplified to O(N2)
if length N2 is really const, then function will have complexity O(N1) in worst case.
if length N1 also consts - then we have O(1) complexity

objective-c variable length array global scope

is it possible to declare a variable length array with global scope in objective-c?
I'm making a game with a world class, which initializes the world map as a three dimensional integer array. while it's only a two dimensional side scroller, the third dimension of the list states which kinda of block goes at the coordinate given by the first two dimensions
after the initialization function, a method nextFrame: is scheduled (I'm using cocos2d and the CCDirector schedule method). I was wondering how to pass the int[][][] map array from the initialization function to the nextFrame function
I tried using global (static keyword) declaration, but got an error saying that global arrays cannot be variable length
the actual line of code I'm referring to is:
int map[xmax][ymax][3];
where xmax and ymax are the farthest x and y coordinates in the list of coordinates that defines the stage.
I'd like to somehow pass them to nextFrame:, which is scheduled in
[self schedule:#selector(nextFrame:)];
I realize I can use NSMutableArray, but NSMutableArray is kinda a headache for 3-dimensional lists of integers (I have to use wrapper numbers for everything...). is there any way to do this with integer arrays?
You can't have a statically allocated global array of dynamic dimensions in C (of which Objective C is a clean superset). But you can use a global array of any length or size (up to available memory) at runtime by using a global pointer, malloc, and array indexing arithmetic.
static int *map = NULL;
...
map = malloc (dim1 * dim2 * dim3 * sizeof(int)); // in some initialization method
if (map == NULL) { /* handle error */ } // before first array access
...
myElement = map[ index3 + dim2 * ( index2 + dim1 * index1 ) ]; // some macro might be suitable here
Or you could make Objective C getter and setter methods that checks the array and array bounds on every access, since a method can return plain C data types.
Another option, if you know the max dimensions you want to have available and are willing to use (waste) that amount of memory, is to just statically allocate the max array, and throw an exception if the program tries to set up something larger than your allowed max.
I tried using global (static keyword)
declaration, but got an error saying
that global arrays cannot be variable
length
But global array pointers can point to arrays of variable length.