Skip to content

Commit 351dbc5

Browse files
authored
Merge branch 'exercism:main' into fft
2 parents 9c38e94 + 6d0254e commit 351dbc5

File tree

20 files changed

+655
-146
lines changed

20 files changed

+655
-146
lines changed
+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"authors": [
3+
"colinleach"
4+
],
5+
"contributors": [],
6+
"blurb": "Julia uses nothing for complete absence of a value, missing for gaps in our knowledge of the data, NaN for computation problems and unknown for some types of error state.."
7+
}

concepts/nothingness/about.md

+154
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
# About
2+
3+
Many languages have a way such as `null` or `none` to indicate a non-existent value.
4+
Because Julia is designed to handle large volumes of (often messy) data, it has multiple forms of [nothingness][nothingness].
5+
6+
The overall aim is to flag missing or suspect values as they are encountered, then continue without raising an exception.
7+
8+
## `nothing`
9+
10+
If a value really does not exist, it is represented by [`nothing`][nothing]. This is probably closest to what C (`NULL`) or Python (`None`) might do.
11+
12+
```julia-repl
13+
julia> n = nothing
14+
15+
julia> isnothing(n)
16+
true
17+
18+
julia> typeof(n)
19+
Nothing
20+
```
21+
22+
So `nothing` is a singleton value of type `Nothing`, and we can test for it.
23+
24+
One common use of `nothing` is as a return (non-)value for functions which are used only for their side effects (printing, network configuration, or whatever).
25+
26+
## `missing`
27+
28+
For situations where a value exists in theory but we don't know what it is, [`missing`][missing] is used. For example, when counting vehicles traveling on a road, human observers might need a break or automatic sensors break down, but the traffic continues to flow.
29+
30+
Thus `missing` is a placeholder, warning humans that they need to make a decision about how to handle this gap in the data.
31+
32+
```julia-repl
33+
julia> mv = [1, 2, missing]
34+
3-element Vector{Union{Missing, Int64}}:
35+
1
36+
2
37+
missing
38+
39+
julia> typeof(mv)
40+
Vector{Union{Missing, Int64}} (alias for Array{Union{Missing, Int64}, 1})
41+
42+
julia> ismissing.(mv) # broadcast function, displays as 1 for true, 0 for false
43+
3-element BitVector:
44+
0
45+
0
46+
1
47+
```
48+
49+
Few other languages have this feature built in, but close analogues are `NA` in R or `NULL` in SQL.
50+
51+
The vector type in the example above is set to `Union{Missing, Int64}`, which has some similarity to types such as `Option` or `Maybe` in other languages.
52+
53+
Expressions usually return `missing` by default if `missing` values are present.
54+
If you want these values to be ignored, use the [`skipmissing()`][skipmissing] function to make this explicit:
55+
56+
```julia-repl
57+
julia> mv = [1, 2, missing]
58+
3-element Vector{Union{Missing, Int64}}:
59+
1
60+
2
61+
missing
62+
63+
julia> sum(mv) # missing in, missing out
64+
missing
65+
66+
julia> skipmissing(mv)
67+
skipmissing(Union{Missing, Int64}[1, 2, missing])
68+
69+
julia> collect(skipmissing(mv))
70+
2-element Vector{Int64}:
71+
1
72+
2
73+
74+
julia> sum(skipmissing(mv)) # functions like sum() can work with iterators
75+
3
76+
```
77+
78+
Because `skipmissing` creates an iterator, wrap it in [`collect()`][collect] if you need a vector.
79+
80+
Sometimes it is useful to replace `missing` values with some default.
81+
The [`@coalesce()`][coalesce] macro is useful for this, as it will return the first non-missing value (or `missing` if there is nothing else).
82+
83+
```julia-repl
84+
julia> str = ["I", "exist", missing]
85+
3-element Vector{Union{Missing, String}}:
86+
"I"
87+
"exist"
88+
missing
89+
90+
julia> [@coalesce(s, "-") for s in str]
91+
3-element Vector{String}:
92+
"I"
93+
"exist"
94+
"-"
95+
```
96+
97+
## `NaN`
98+
99+
Short for "Not a Number", [NaN][NaN] flags a computation problem in situations where a number was expected.
100+
101+
```julia-repl
102+
julia> v = [0, 1, -1]
103+
3-element Vector{Int64}:
104+
0
105+
1
106+
-1
107+
108+
julia> v / 0
109+
3-element Vector{Float64}:
110+
NaN
111+
Inf
112+
-Inf
113+
114+
julia> sum(v / 0)
115+
NaN
116+
```
117+
118+
Any sort of calculation on data including a NaN will give a `NaN` result.
119+
120+
There is currently no special function to remove `NaN` values, but the standard [`filter()`][filter] function can do this quite simply.
121+
Only values for which some given condition is `true` will be copied to the result array:
122+
123+
```julia-repl
124+
julia> filter(!isnan, [1, 2, NaN])
125+
2-element Vector{Float64}:
126+
1.0
127+
2.0
128+
```
129+
130+
## Undefined data
131+
132+
This is something you are likely use less frequently.
133+
Exceptions include:
134+
- There are ways to create composite types (such as [`struct`][struct]) with uninitialized fields.
135+
Julia usually tries to use default values, but can only do this if it has enough information to infer the field type.
136+
- In some cases, initializing a vector as `undef` can be a flexible and performant approach.
137+
This has been used, for example, in community solutions to the [Circular Buffer][circbuff] exercise (an advanced technique, but this is graded as a hard exercise).
138+
139+
Getting an [`undef`][undef] result from an attempt to access a value is an error state, typically flagged with an [`UndefRefError`][UndefRefError].
140+
141+
142+
[nothingness]: https://docs.julialang.org/en/v1/manual/faq/#Nothingness-and-missing-values
143+
[nothing]: https://docs.julialang.org/en/v1/base/base/#Core.Nothing
144+
[isnothing]: https://docs.julialang.org/en/v1/base/base/#Base.isnothing
145+
[missing]: https://docs.julialang.org/en/v1/base/base/#Base.missing
146+
[skipmissing]: https://docs.julialang.org/en/v1/base/base/#Base.skipmissing
147+
[collect]: https://docs.julialang.org/en/v1/base/collections/#Base.collect-Tuple{Any}
148+
[coalesce]: https://docs.julialang.org/en/v1/base/base/#Base.@coalesce
149+
[NaN]: https://en.wikipedia.org/wiki/NaN
150+
[filter]: https://docs.julialang.org/en/v1/base/collections/#Base.filter
151+
[struct]: https://docs.julialang.org/en/v1/base/base/#struct
152+
[undef]: https://docs.julialang.org/en/v1/base/arrays/#Core.undef
153+
[UndefRefError]: https://docs.julialang.org/en/v1/base/base/#Core.UndefRefError
154+
[circbuff]: https://exercism.org/tracks/julia/exercises/circular-buffer

concepts/nothingness/introduction.md

+126
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
# Introduction
2+
3+
Many languages have a way such as `null` or `none` to indicate a non-existent value.
4+
Because Julia is designed to handle large volumes of (often messy) data, it has multiple forms of nothingness.
5+
6+
The overall aim is to flag missing or suspect values as they are encountered, then continue without raising an exception.
7+
8+
## `nothing`
9+
10+
If a value really does not exist, it is represented by `nothing`. This is probably closest to what C (`NULL`) or Python (`None`) might do.
11+
12+
```julia-repl
13+
julia> n = nothing
14+
15+
julia> isnothing(n)
16+
true
17+
18+
julia> typeof(n)
19+
Nothing
20+
```
21+
22+
So `nothing` is a singleton value of type `Nothing`, and we can test for it.
23+
24+
One common use of `nothing` is as a return (non-)value for functions which are used only for their side effects (printing, network configuration, or whatever).
25+
26+
## `missing`
27+
28+
For situations where a value exists in theory but we don't know what it is, `missing` is used. For example, when counting vehicles traveling on a road, human observers might need a break or automatic sensors break down, but the traffic continues to flow.
29+
30+
Thus `missing` is a placeholder, warning humans that they need to make a decision about how to handle this gap in the data.
31+
32+
```julia-repl
33+
julia> mv = [1, 2, missing]
34+
3-element Vector{Union{Missing, Int64}}:
35+
1
36+
2
37+
missing
38+
39+
julia> typeof(mv)
40+
Vector{Union{Missing, Int64}} (alias for Array{Union{Missing, Int64}, 1})
41+
42+
julia> ismissing.(mv) # broadcast function, displays as 1 for true, 0 for false
43+
3-element BitVector:
44+
0
45+
0
46+
1
47+
```
48+
49+
Few other languages have this feature built in, but close analogues are `NA` in R or `NULL` in SQL.
50+
51+
Expressions usually return `missing` by default if `missing` values are present.
52+
If you want these values to be ignored, use the `skipmissing()` function to make this explicit:
53+
54+
```julia-repl
55+
julia> mv = [1, 2, missing]
56+
3-element Vector{Union{Missing, Int64}}:
57+
1
58+
2
59+
missing
60+
61+
julia> sum(mv) # missing in, missing out
62+
missing
63+
64+
julia> skipmissing(mv)
65+
skipmissing(Union{Missing, Int64}[1, 2, missing])
66+
67+
julia> collect(skipmissing(mv))
68+
2-element Vector{Int64}:
69+
1
70+
2
71+
72+
julia> sum(skipmissing(mv)) # functions like sum() can work with iterators
73+
3
74+
```
75+
76+
Because `skipmissing` creates an iterator, wrap it in `collect()` if you need a vector.
77+
78+
Sometimes it is useful to replace `missing` values with some default.
79+
The `@coalesce()` macro is useful for this, as it will return the first non-missing value (or `missing` if there is nothing else).
80+
81+
```julia-repl
82+
julia> str = ["I", "exist", missing]
83+
3-element Vector{Union{Missing, String}}:
84+
"I"
85+
"exist"
86+
missing
87+
88+
julia> [@coalesce(s, "-") for s in str]
89+
3-element Vector{String}:
90+
"I"
91+
"exist"
92+
"-"
93+
```
94+
95+
## `NaN`
96+
97+
Short for "Not a Number", NaN flags a computation problem in situations where a number was expected.
98+
99+
```julia-repl
100+
julia> v = [0, 1, -1]
101+
3-element Vector{Int64}:
102+
0
103+
1
104+
-1
105+
106+
julia> v / 0
107+
3-element Vector{Float64}:
108+
NaN
109+
Inf
110+
-Inf
111+
112+
julia> sum(v / 0)
113+
NaN
114+
```
115+
116+
Any sort of calculation on data including a NaN will give a `NaN` result.
117+
118+
There is currently no special function to remove `NaN` values, but the standard `filter()` function can do this quite simply.
119+
Only values for which some given condition is `true` will be copied to the result array:
120+
121+
```julia-repl
122+
julia> filter(!isnan, [1, 2, NaN])
123+
2-element Vector{Float64}:
124+
1.0
125+
2.0
126+
```

concepts/nothingness/links.json

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
[
2+
{
3+
"url": "https://docs.julialang.org/en/v1/manual/faq/#Nothingness-and-missing-values",
4+
"description": "General introduction in the Julia manual."
5+
},
6+
{
7+
"url": "https://docs.julialang.org/en/v1/base/base/#Core.Nothing",
8+
"description": "Manual section on `nothing`."
9+
},
10+
{
11+
"url": "https://docs.julialang.org/en/v1/base/base/#Base.missing",
12+
"description": "Manual section on `missing`."
13+
},
14+
{
15+
"url": "https://en.wikipedia.org/wiki/NaN",
16+
"description": "NaN Wikipedia page."
17+
}
18+
]

config.json

+17-8
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,18 @@
229229
],
230230
"status": "beta"
231231
},
232+
{
233+
"slug": "name-badges",
234+
"name": "Name Badges",
235+
"uuid": "4b3e6780-b566-4315-a7b0-45500883f050",
236+
"concepts": [
237+
"nothingness"
238+
],
239+
"prerequisites": [
240+
"functions"
241+
],
242+
"status": "beta"
243+
},
232244
{
233245
"slug": "old-annalyns-infiltration",
234246
"name": "Old Annalyn's Infiltration",
@@ -285,14 +297,6 @@
285297
"prerequisites": [],
286298
"status": "deprecated"
287299
},
288-
{
289-
"slug": "old-name-badge",
290-
"name": "Old Name Badge",
291-
"uuid": "4b3e6780-b566-4315-a7b0-45500883f050",
292-
"concepts": [],
293-
"prerequisites": [],
294-
"status": "deprecated"
295-
},
296300
{
297301
"slug": "old-elyses-analytic-enchantments",
298302
"name": "Old Elyse's Analytic Enchantments",
@@ -1477,6 +1481,11 @@
14771481
"slug": "pairs-and-dicts",
14781482
"name": "Pairs And Dicts"
14791483
},
1484+
{
1485+
"uuid": "82847597-92f5-4ff6-94a6-b7a2313f1da8",
1486+
"slug": "nothingness",
1487+
"name": "Nothingness"
1488+
},
14801489
{
14811490
"uuid": "c91bb493-98f7-4e5c-8e34-320cb9e290e2",
14821491
"slug": "randomness",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Hints
2+
3+
The first three tasks all call the same `print_name_badge()` function.
4+
Implementation of this can be built up stepwise, adding logic for tasks 2 and 3 as you proceed.
5+
6+
## 1. Print a badge for an employee
7+
8+
- This is the default case, just assemble the 3 arguments into a suitable string.
9+
- [String interpolatation][interpolation] is probably the easiest approach.
10+
11+
## 2. Print a badge for a new employee
12+
13+
- Now the iID is `missing,` so the target string format is different.
14+
- [`ismissing()][ismissing] is useful here.
15+
- Julia has a [ternary operator][ternary] with concise syntax.
16+
17+
## 3. Print a badge for the owner
18+
19+
- Now the department is `nothing`.
20+
- There is an [`isnothing()`][isnothing] function.
21+
22+
## 4. Calculate the total salary of emplyees with no ID
23+
24+
- Are [any][any] IDs missing? If not, the return value is zero.
25+
- To pull out entries with missing ID, it may help to start by [zipping][zip] the `ids` and `salaries` vectors.
26+
- There are several ways to iterate over the resulting vector of tuples, without writing an explicit loop.
27+
- Pythonically, with a vector [comprehension][comprehension].
28+
- Functionally, with [`map()`][map] and an [anonymous function][anonymous].
29+
- Vectorially, with [broadcasting][broadcasting] of an [anonymous function][anonymous].
30+
- At the end, there will be a [`sum()`][sum] over a modified salaries vector.
31+
- The salaries of employees with IDs can either be omitted or set to zero.
32+
33+
[interpolation]: https://exercism.org/tracks/julia/concepts/strings
34+
[isnothing]: https://docs.julialang.org/en/v1/base/base/#Base.isnothing
35+
[ismissing]: https://docs.julialang.org/en/v1/base/base/#Base.ismissing
36+
[ternary]: https://docs.julialang.org/en/v1/base/base/#?:
37+
[any]: https://docs.julialang.org/en/v1/base/collections/#Base.any-Tuple{AbstractArray,%20Any}
38+
[comprehension]: https://docs.julialang.org/en/v1/manual/arrays/#man-comprehensions
39+
[anonymous]: https://docs.julialang.org/en/v1/manual/functions/#man-anonymous-functions
40+
[broadcasting]: https://exercism.org/tracks/julia/concepts/vector-operations
41+
[sum]: https://docs.julialang.org/en/v1/base/collections/#Base.sum
42+
[map]: https://docs.julialang.org/en/v1/base/collections/#Base.map
43+
[zip]: https://docs.julialang.org/en/v1/base/iterators/#Base.Iterators.zip

0 commit comments

Comments
 (0)