Storing elements in a sequence (Part 2)
Mutation
Here's what mutation looks like. When we assign a new value to one of the positions, the address associated with that index in changed. That is, the sequence itself is being changed.
For example, as illustrated in the figure, the pseudocode pets[4] ← "ant" changes the address stored within the sequence. This results in the sequence being changed.
Dangers of mutation
As you can imagine, mutation is quite handy. As you can also imagine, mutation is somewhat dangerous. This isn't the only time we'll see this combination of traits.
Definition
Aliasing occurs when two different names are used for the same data.
One of the dangers of mutation is aliasing, that is, mutable data having two different names. This is to be avoided when possible.
Caution
Avoid aliasing where possible.
Example with immutable data
Going back to our earlier example, we will see that it is not dangerous to assign two names to immutable data. Consider the sequence of pseudocode given below executed in the order listed.
-
x ← 10:
We first assign the value 10 to the variable x.
-
x ← 20:
As before, we reassign x to 20.
-
y ← x:
Then we let y use the same address.
-
x ← 30:
What happens when x changes? The address associated with x changes to point to a new value, and y stays the same. The key point here is that changes to x do not make change to y. Not so with aliasing.
Aliasing
As shown in the figure, we have a sequence with the name pets. The sequence consists of the values "cat", "ape", "dog", "fox", "bat" with indices 0, 1, 2, 3, 4, respectively.
As shown in the figure, now we assign the same address to the variable names: names ← pets.
Now let's change the value stored in position 4 of names: names[4] ← "ant". What happened to pets? It was changed too!
Aliasing is confusing, so we try to avoid it. But sometimes it is unavoidable, as we'll see.
Using sequences as inputs to functions
What happens when we call a function on mutable data, like a sequence? There are two ways that functions can handle input parameters: call by value and call by reference. In call by value, it is a copy of the input data that is changed inside the function call. In other words, the function uses a copy of the input data. For example, in the following pseudocode, no changes to input variable, total , occur:
total ← 20 many ← paint(total)
Here, when we call the function paint on the parameter total, the value assigned to total does not change.
In contrast, when a function that uses call by reference, the function call uses the reference to the parameter, that is, it uses the address to access the original data. In other words, the function uses the input value itself. This results in changing the input as a side effect. This can be very useful as long as you are aware what is happening! Make sure to inform yourself.
Caution
Make sure you know how each function behaves.
Built-in functions
Ways to create sequences:- Specify the entire sequence of elements.
- Create an empty sequence and then add elements.
- Copy an existing sequence and modify it.
What types of built-in functions are there? We need ways to create sequences, such as by specifying all the elements, or, since mutation is possible, by creating an empty sequence and then changing it by adding elements. Yet another way to create a sequence is to copy a sequence and then possibly make changes to the copy.
Language-dependent:- Elements can be of different types.
- Elements can only be added in certain positions.
- Elements can be deleted.
Depending on the programming language, a sequence can consists of different types of elements, such as both strings and numbers. Sometimes elements can only be added at certain positions such as, for example, only at the beginning or at the end. Again, depending on the language, elements can be deleted to shorten a sequence. Whatever functions a language provides, make sure you know whether the input will be altered or not.
Caution
Make sure you know if the function creates a new sequence or alters the input.