You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardexpand all lines: lib/elixir/pages/getting-started/lists-and-tuples.md
+27-5
Original file line number
Diff line number
Diff line change
@@ -130,7 +130,7 @@ iex> list ++ [4]
130
130
[1, 2, 3, 4]
131
131
```
132
132
133
-
Tuples, on the other hand, are stored contiguously in memory. This means getting the tuple size or accessing an element by index is fast. However, updating or adding elements to tuples is expensive because it requires creating a new tuple in memory:
133
+
Tuples, on the other hand, are stored contiguously in memory. This means getting the tuple size or accessing an element by index is fast. On the other hand, updating or adding elements to tuples is expensive because it requires creating a new tuple in memory:
134
134
135
135
```elixir
136
136
iex> tuple = {:a, :b, :c, :d}
@@ -139,9 +139,29 @@ iex> put_elem(tuple, 2, :e)
139
139
{:a, :b, :e, :d}
140
140
```
141
141
142
-
Note that this applies only to the tuple itself, not its contents. For instance, when you update a tuple, all entries are shared between the old and the new tuple, except for the entry that has been replaced. In other words, tuples and lists in Elixir are capable of sharing their contents. This reduces the amount of memory allocation the language needs to perform and is only possible thanks to the immutable semantics of the language.
142
+
Note, however, the elements themselves are not copied. When you update a tuple, all entries are shared between the old and the new tuple, except for the entry that has been replaced. This rule applies to most data structures in Elixir. This reduces the amount of memory allocation the language needs to perform and is only possible thanks to the immutable semantics of the language.
143
143
144
-
Those performance characteristics dictate the usage of those data structures. One very common use case for tuples is to use them to return extra information from a function. For example, `File.read/1` is a function that can be used to read file contents. It returns a tuple:
144
+
Those performance characteristics dictate the usage of those data structures. In a nutshell, lists are used when the number of elements returned may vary. Tuples have a fixed size. Let's see two examples from the `String` module:
145
+
146
+
```elixir
147
+
iex>String.split("hello world")
148
+
["hello", "world"]
149
+
iex>String.split("hello beautiful world")
150
+
["hello", "beautiful", "world"]
151
+
```
152
+
153
+
The `String.split/2` function breaks a string into a list of strings on every whitespace character. Since the amount of elements returned depends on the input, we use a list.
154
+
155
+
On the other hand, `String.split_at/2` splits a string in two parts at a given position. Since it always returns two entries, regardless of the input size, it returns tuples:
156
+
157
+
```elixir
158
+
iex>String.split_at("hello world", 3)
159
+
{"hel", "lo world"}
160
+
iex>String.split_at("hello world", 3)
161
+
{"hello w", "orld"}
162
+
```
163
+
164
+
It is also very common to use tuples and atoms to create "tagged tuples", which is a handy return value when an operation may succeed or fail. For example, `File.read/1` reads the contents of a file at a given path, which may or may not exist. It returns tagged tuples:
If the path given to `File.read/1` exists, it returns a tuple with the atom `:ok` as the first element and the file contents as the second. Otherwise, it returns a tuple with `:error` and the error description. `File.read/1` returns a tuple, not a list, because it always contains two values. As we will soon learn, Elixir allows us to _pattern match_ on such shapes. Returning a list would not bring any advantages.
173
+
If the path given to `File.read/1` exists, it returns a tuple with the atom `:ok` as the first element and the file contents as the second. Otherwise, it returns a tuple with `:error` and the error description. As we will soon learn, Elixir allows us to *pattern match* on tagged tuples and effortlessly handle both success and failure cases.
154
174
155
-
Most of the time, Elixir is going to guide you to do the right thing. For example, there is an `elem/2` function to access a tuple item but there is no built-in equivalent for lists:
175
+
Given Elixir consistently follows those rules, the choice between lists and tuples get clearer as you learn and use the language. Elixir often guides you to do the right thing. For example, there is an `elem/2` function to access a tuple item:
156
176
157
177
```elixir
158
178
iex> tuple = {:ok, "hello"}
@@ -161,6 +181,8 @@ iex> elem(tuple, 1)
161
181
"hello"
162
182
```
163
183
184
+
However, given you often don't know the number of elements in a list, there is no built-in equivalent for accessing arbitrary entries in a lists, apart from its head.
185
+
164
186
## Size or length?
165
187
166
188
When counting the elements in a data structure, Elixir also abides by a simple rule: the function is named `size` if the operation is in constant time (the value is pre-calculated) or `length` if the operation is linear (calculating the length gets slower as the input grows). As a mnemonic, both "length" and "linear" start with "l".
0 commit comments