english improved
This commit is contained in:
193
AGENTS.md
193
AGENTS.md
@@ -1,23 +1,51 @@
|
|||||||
# AGENTS.md - Guidelines for Agentic Coding in JuliaKurs23
|
# AGENTS.md - Guidelines for JuliaKurs23
|
||||||
|
|
||||||
This is a **Quarto-based Julia programming course book** in English. The book teaches scientific computing with Julia using interactive Jupyter notebooks rendered with Quarto.
|
This is a **Quarto-based Julia programming course book**. The book teaches scientific computing with Julia using interactive Jupyter notebooks rendered with Quarto.
|
||||||
|
|
||||||
**Translation Note:** The book was originally written in German and has been translated to professional, concise English suitable for mathematicians. All code, LaTeX equations, and formatting have been preserved during translation. Only text descriptions and code comments were translated to English.
|
**Translation Note:** The book was originally written in German and translated to English. The English needs improvement—focus on making the prose professional, concise, and suitable for mathematicians.
|
||||||
|
|
||||||
## Project Overview
|
## Project Overview
|
||||||
|
|
||||||
- **Type**: Educational book website/PDF generated from Quarto (.qmd) files
|
- **Type**: Educational book (HTML website + PDF) generated from Quarto (.qmd) files
|
||||||
- **Language**: English (all content)
|
- **Language**: English (all content)
|
||||||
- **Build System**: Quarto with Julia Jupyter kernel
|
- **Build System**: Quarto with Julia Jupyter kernel
|
||||||
- **Julia Version**: 1.10
|
- **Julia Version**: 1.10
|
||||||
- **Output**: HTML website and PDF
|
|
||||||
- **License**: CC BY-NC-SA 4.0
|
- **License**: CC BY-NC-SA 4.0
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Build Commands
|
## Current Task: English Translation Improvement
|
||||||
|
|
||||||
### Build the Book
|
The English text in this book was translated from German and needs refinement for quality.
|
||||||
|
|
||||||
|
### Workflow (one change at a time)
|
||||||
|
|
||||||
|
For each chapter file in `chapters/`:
|
||||||
|
|
||||||
|
1. I read the file and identify English improvements
|
||||||
|
2. I present ONE improvement at a time showing:
|
||||||
|
- The current text
|
||||||
|
- The proposed improvement
|
||||||
|
- Explanation of why the change is needed
|
||||||
|
3. You reply **yes** or **no**
|
||||||
|
4. If yes, I apply the change; if no, I skip it
|
||||||
|
5. Repeat until all improvements for that file are done
|
||||||
|
6. Move to the next chapter file
|
||||||
|
|
||||||
|
### What to improve:
|
||||||
|
- English prose, grammar, word choice
|
||||||
|
- Clarity and flow for mathematical audience
|
||||||
|
- Professional tone suitable for mathematicians/scientists
|
||||||
|
|
||||||
|
### What NOT to change:
|
||||||
|
- Julia code
|
||||||
|
- LaTeX equations
|
||||||
|
- Markdown formatting
|
||||||
|
- Quarto cell options (`#| eval:`, etc.)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Build Commands
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Build HTML website
|
# Build HTML website
|
||||||
@@ -31,17 +59,10 @@ quarto render --to julia-color-pdf
|
|||||||
|
|
||||||
# Preview locally
|
# Preview locally
|
||||||
quarto preview
|
quarto preview
|
||||||
```
|
|
||||||
|
|
||||||
### Development Commands
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Render a specific chapter
|
# Render a specific chapter
|
||||||
quarto render chapters/first_contact.qmd
|
quarto render chapters/first_contact.qmd
|
||||||
|
|
||||||
# Build with specific output directory
|
|
||||||
quarto render --output-dir _book
|
|
||||||
|
|
||||||
# Run in daemon mode (faster rebuilds)
|
# Run in daemon mode (faster rebuilds)
|
||||||
quarto render --execute-daemon 3600
|
quarto render --execute-daemon 3600
|
||||||
```
|
```
|
||||||
@@ -49,135 +70,55 @@ quarto render --execute-daemon 3600
|
|||||||
### Deployment
|
### Deployment
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Deploy to web server (rsync _book/)
|
|
||||||
./deploy.sh
|
./deploy.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
### Testing Individual Code Blocks
|
|
||||||
|
|
||||||
Quarto executes Julia code in Jupyter notebooks. To test individual code blocks:
|
|
||||||
|
|
||||||
1. Open the `.qmd` file in VS Code with Julia extension
|
|
||||||
2. Use Julia REPL to execute code blocks interactively
|
|
||||||
3. Or use IJulia/Jupyter to run specific cells
|
|
||||||
|
|
||||||
```julia
|
|
||||||
# In Julia REPL, test code from a cell:
|
|
||||||
include("path/to/notebook.jl")
|
|
||||||
```
|
|
||||||
|
|
||||||
### Julia Package Management
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Install dependencies (from Project.toml)
|
|
||||||
julia -e 'using Pkg; Pkg.instantiate()'
|
|
||||||
|
|
||||||
# Add a new package
|
|
||||||
julia -e 'using Pkg; Pkg.add("PackageName")'
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Code Style Guidelines
|
## Code Style Guidelines
|
||||||
|
|
||||||
### General Structure (.qmd files)
|
### Quarto Structure (.qmd files)
|
||||||
|
|
||||||
- All `.qmd` files start with YAML front matter specifying `engine: julia`
|
- All `.qmd` files start with YAML front matter: `engine: julia`
|
||||||
- Code blocks use triple backticks with `julia` language identifier
|
- Code blocks use triple backticks with `julia` identifier
|
||||||
- Use Quarto cell options in comments like `#| option: value`
|
- Cell options in comments: `#| option: value`
|
||||||
- Common options: `eval:`, `echo:`, `output:`, `error:`, `hide_line:`
|
|
||||||
|
|
||||||
Example:
|
|
||||||
```markdown
|
|
||||||
---
|
|
||||||
engine: julia
|
|
||||||
---
|
|
||||||
|
|
||||||
```{julia}
|
|
||||||
#| eval: true
|
|
||||||
#| echo: true
|
|
||||||
#| output: true
|
|
||||||
using Statistics
|
|
||||||
mean([1, 2, 3])
|
|
||||||
```
|
|
||||||
```
|
|
||||||
|
|
||||||
### Julia Code Conventions
|
### Julia Code Conventions
|
||||||
|
|
||||||
#### Imports
|
**Imports:**
|
||||||
|
|
||||||
```julia
|
```julia
|
||||||
# Prefer using for packages with many exports
|
using Statistics, LinearAlgebra, Plots # many exports
|
||||||
using Statistics, LinearAlgebra, Plots
|
import Base: convert, show # specific exports
|
||||||
|
|
||||||
# Use import when you need specific exports or module name
|
|
||||||
import Base: convert, show
|
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Naming Conventions
|
**Naming:**
|
||||||
|
- Variables/functions: lowercase with underscores (`my_variable`)
|
||||||
|
- Types: CamelCase (`MyType`, `AbstractMatrix`)
|
||||||
|
- Constants: ALL_UPPERCASE (`MAX_ITER`)
|
||||||
|
- Modules: PascalCase (`Statistics`)
|
||||||
|
|
||||||
- **Variables/functions**: lowercase with underscores (`my_variable`, `calculate_mean`)
|
**Formatting:**
|
||||||
- **Types**: CamelCase (`MyType`, `AbstractMatrix`)
|
- 4 spaces indentation
|
||||||
- **Constants**: ALL_UPPERCASE (`MAX_ITER`, `PI`)
|
- ~92 character line limit
|
||||||
- **Modules**: PascalCase (`Statistics`, `LinearAlgebra`)
|
- Spaces around operators: `a + b`
|
||||||
|
- No spaces before commas: `f(a, b)`
|
||||||
|
|
||||||
#### Formatting
|
**Types:**
|
||||||
|
- Explicit types for performance/clarity
|
||||||
- Use 4 spaces for indentation (Julia default)
|
- Parameterize: `Vector{Float64}`, `Dict{Symbol, Int}`
|
||||||
- Limit lines to ~92 characters when practical
|
- Concrete types for struct fields
|
||||||
- Use spaces around operators: `a + b`, not `a+b`
|
|
||||||
- No spaces before commas: `f(a, b)`, not `f(a , b)`
|
|
||||||
|
|
||||||
#### Types
|
|
||||||
|
|
||||||
- Use explicit types when needed for performance or clarity
|
|
||||||
- Parameterize types when useful: `Vector{Float64}`, `Dict{Symbol, Int}`
|
|
||||||
- Prefer concrete types for fields in structs
|
|
||||||
|
|
||||||
#### Error Handling
|
|
||||||
|
|
||||||
|
**Error Handling:**
|
||||||
```julia
|
```julia
|
||||||
# Use try-catch for expected errors
|
|
||||||
try
|
try
|
||||||
parse(Int, user_input)
|
parse(Int, user_input)
|
||||||
catch e
|
catch e
|
||||||
println("Invalid input: $e")
|
println("Invalid input: $e")
|
||||||
end
|
end
|
||||||
|
|
||||||
# Use assertions for internal checks
|
|
||||||
@assert x >= 0 "x must be non-negative"
|
@assert x >= 0 "x must be non-negative"
|
||||||
```
|
```
|
||||||
|
|
||||||
### Quarto-Specific Guidelines
|
|
||||||
|
|
||||||
#### Cell Options
|
|
||||||
|
|
||||||
Use these in code block comments:
|
|
||||||
- `#| eval: true/false` - whether to execute
|
|
||||||
- `#| echo: true/false` - show source code
|
|
||||||
- `#| output: true/false` - show output
|
|
||||||
- `#| error: true/false` - show errors
|
|
||||||
- `#| hide_line: true` - hide this line from output
|
|
||||||
- `#| fig-cap: "Caption"` - figure caption
|
|
||||||
|
|
||||||
#### Embedding Notebooks
|
|
||||||
|
|
||||||
```markdown
|
|
||||||
{{< embed notebooks/nb-types.ipynb#nb1 >}}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Conditional Content
|
|
||||||
|
|
||||||
```markdown
|
|
||||||
::: {.content-visible when-format="html"}
|
|
||||||
... HTML only content
|
|
||||||
:::
|
|
||||||
|
|
||||||
::: {.content-visible when-format="pdf"}
|
|
||||||
... PDF only content
|
|
||||||
:::
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## File Organization
|
## File Organization
|
||||||
@@ -197,20 +138,10 @@ Use these in code block comments:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## VS Code Settings
|
|
||||||
|
|
||||||
The project uses VS Code with:
|
|
||||||
- Julia environment: `/home/hellmund/Julia/23`
|
|
||||||
- LTEx for spell checking (disabled for code blocks)
|
|
||||||
- Auto-exclude: `.ipynb`, `.md`, `.quarto_ipynb` files
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Important Notes
|
## Important Notes
|
||||||
|
|
||||||
1. **All content is in English** - Comments, documentation, variable names in examples should be English (professional, concise, suitable for mathematicians)
|
1. **All content is in English** — professional, concise, suitable for mathematicians
|
||||||
2. **Code comments were translated** - German text and code comments were translated to English; all Julia code, LaTeX equations, and Quarto formatting were preserved
|
2. **Code, LaTeX, formatting preserved** — only improve English prose
|
||||||
3. **Code execution is cached** - Use `#| eval: true` to execute cells
|
3. **Code execution cached** — use `#| eval: true` to execute cells
|
||||||
4. **ansi color codes** - The `julia-color` extension handles ANSI escape sequences in output
|
4. **Freeze mode** — set to `auto` in _quarto.yml; use `freeze: false` to re-run all code
|
||||||
5. **Freeze mode** - Set to `auto` in _quarto.yml; use `freeze: false` to re-run all code
|
5. **Keep intermediates** — `keep-ipynb`, `keep-tex`, `keep-md` are all true
|
||||||
6. **Keep intermediate files** - `keep-ipynb`, `keep-tex`, `keep-md` are all true
|
|
||||||
|
|||||||
@@ -22,9 +22,9 @@ version = "0.4.5"
|
|||||||
|
|
||||||
[[deps.Adapt]]
|
[[deps.Adapt]]
|
||||||
deps = ["LinearAlgebra", "Requires"]
|
deps = ["LinearAlgebra", "Requires"]
|
||||||
git-tree-sha1 = "7e35fca2bdfba44d797c53dfe63a51fabf39bfc0"
|
git-tree-sha1 = "35ea197a51ce46fcd01c4a44befce0578a1aaeca"
|
||||||
uuid = "79e6a3ab-5dfb-504d-930d-738a2a938a0e"
|
uuid = "79e6a3ab-5dfb-504d-930d-738a2a938a0e"
|
||||||
version = "4.4.0"
|
version = "4.5.0"
|
||||||
weakdeps = ["SparseArrays", "StaticArrays"]
|
weakdeps = ["SparseArrays", "StaticArrays"]
|
||||||
|
|
||||||
[deps.Adapt.extensions]
|
[deps.Adapt.extensions]
|
||||||
@@ -165,9 +165,9 @@ version = "1.1.1"
|
|||||||
|
|
||||||
[[deps.CairoMakie]]
|
[[deps.CairoMakie]]
|
||||||
deps = ["CRC32c", "Cairo", "Cairo_jll", "Colors", "FileIO", "FreeType", "GeometryBasics", "LinearAlgebra", "Makie", "PrecompileTools"]
|
deps = ["CRC32c", "Cairo", "Cairo_jll", "Colors", "FileIO", "FreeType", "GeometryBasics", "LinearAlgebra", "Makie", "PrecompileTools"]
|
||||||
git-tree-sha1 = "5017d6849aff775febd36049f7d926a5fb6677ec"
|
git-tree-sha1 = "fa072933899aae6dc61dde934febed8254e66c6a"
|
||||||
uuid = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0"
|
uuid = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0"
|
||||||
version = "0.15.8"
|
version = "0.15.9"
|
||||||
|
|
||||||
[[deps.Cairo_jll]]
|
[[deps.Cairo_jll]]
|
||||||
deps = ["Artifacts", "Bzip2_jll", "CompilerSupportLibraries_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "JLLWrappers", "LZO_jll", "Libdl", "Pixman_jll", "Xorg_libXext_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"]
|
deps = ["Artifacts", "Bzip2_jll", "CompilerSupportLibraries_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "JLLWrappers", "LZO_jll", "Libdl", "Pixman_jll", "Xorg_libXext_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"]
|
||||||
@@ -286,9 +286,9 @@ version = "1.3.0+1"
|
|||||||
|
|
||||||
[[deps.ComputePipeline]]
|
[[deps.ComputePipeline]]
|
||||||
deps = ["Observables", "Preferences"]
|
deps = ["Observables", "Preferences"]
|
||||||
git-tree-sha1 = "76dab592fa553e378f9dd8adea16fe2591aa3daa"
|
git-tree-sha1 = "3b4be73db165146d8a88e47924f464e55ab053cd"
|
||||||
uuid = "95dc2771-c249-4cd0-9c9f-1f3b4330693c"
|
uuid = "95dc2771-c249-4cd0-9c9f-1f3b4330693c"
|
||||||
version = "0.1.6"
|
version = "0.1.7"
|
||||||
|
|
||||||
[[deps.ConcurrentUtilities]]
|
[[deps.ConcurrentUtilities]]
|
||||||
deps = ["Serialization", "Sockets"]
|
deps = ["Serialization", "Sockets"]
|
||||||
@@ -423,9 +423,9 @@ uuid = "5ae413db-bbd1-5e63-b57d-d24a61df00f5"
|
|||||||
version = "2.2.4+0"
|
version = "2.2.4+0"
|
||||||
|
|
||||||
[[deps.EnumX]]
|
[[deps.EnumX]]
|
||||||
git-tree-sha1 = "7bebc8aad6ee6217c78c5ddcf7ed289d65d0263e"
|
git-tree-sha1 = "c49898e8438c828577f04b92fc9368c388ac783c"
|
||||||
uuid = "4e289a0a-7415-4d19-859d-a7e5c4648b56"
|
uuid = "4e289a0a-7415-4d19-859d-a7e5c4648b56"
|
||||||
version = "1.0.6"
|
version = "1.0.7"
|
||||||
|
|
||||||
[[deps.EpollShim_jll]]
|
[[deps.EpollShim_jll]]
|
||||||
deps = ["Artifacts", "JLLWrappers", "Libdl"]
|
deps = ["Artifacts", "JLLWrappers", "Libdl"]
|
||||||
@@ -603,9 +603,9 @@ version = "3.4.1+0"
|
|||||||
|
|
||||||
[[deps.GR]]
|
[[deps.GR]]
|
||||||
deps = ["Artifacts", "Base64", "DelimitedFiles", "Downloads", "GR_jll", "HTTP", "JSON", "Libdl", "LinearAlgebra", "Preferences", "Printf", "Qt6Wayland_jll", "Random", "Serialization", "Sockets", "TOML", "Tar", "Test", "p7zip_jll"]
|
deps = ["Artifacts", "Base64", "DelimitedFiles", "Downloads", "GR_jll", "HTTP", "JSON", "Libdl", "LinearAlgebra", "Preferences", "Printf", "Qt6Wayland_jll", "Random", "Serialization", "Sockets", "TOML", "Tar", "Test", "p7zip_jll"]
|
||||||
git-tree-sha1 = "ee0585b62671ce88e48d3409733230b401c9775c"
|
git-tree-sha1 = "44716a1a667cb867ee0e9ec8edc31c3e4aa5afdc"
|
||||||
uuid = "28b8d3ca-fb5f-59d9-8090-bfdbd6d07a71"
|
uuid = "28b8d3ca-fb5f-59d9-8090-bfdbd6d07a71"
|
||||||
version = "0.73.22"
|
version = "0.73.24"
|
||||||
|
|
||||||
[deps.GR.extensions]
|
[deps.GR.extensions]
|
||||||
IJuliaExt = "IJulia"
|
IJuliaExt = "IJulia"
|
||||||
@@ -615,9 +615,9 @@ version = "0.73.22"
|
|||||||
|
|
||||||
[[deps.GR_jll]]
|
[[deps.GR_jll]]
|
||||||
deps = ["Artifacts", "Bzip2_jll", "Cairo_jll", "FFMPEG_jll", "Fontconfig_jll", "FreeType2_jll", "GLFW_jll", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Libtiff_jll", "Pixman_jll", "Qt6Base_jll", "Zlib_jll", "libpng_jll"]
|
deps = ["Artifacts", "Bzip2_jll", "Cairo_jll", "FFMPEG_jll", "Fontconfig_jll", "FreeType2_jll", "GLFW_jll", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Libtiff_jll", "Pixman_jll", "Qt6Base_jll", "Zlib_jll", "libpng_jll"]
|
||||||
git-tree-sha1 = "7dd7173f7129a1b6f84e0f03e0890cd1189b0659"
|
git-tree-sha1 = "be8a1b8065959e24fdc1b51402f39f3b6f0f6653"
|
||||||
uuid = "d2c73de3-f751-5644-a686-071e5b155ba9"
|
uuid = "d2c73de3-f751-5644-a686-071e5b155ba9"
|
||||||
version = "0.73.22+0"
|
version = "0.73.24+0"
|
||||||
|
|
||||||
[[deps.GeometryBasics]]
|
[[deps.GeometryBasics]]
|
||||||
deps = ["EarCut_jll", "Extents", "IterTools", "LinearAlgebra", "PrecompileTools", "Random", "StaticArrays"]
|
deps = ["EarCut_jll", "Extents", "IterTools", "LinearAlgebra", "PrecompileTools", "Random", "StaticArrays"]
|
||||||
@@ -669,9 +669,9 @@ version = "1.3.15+0"
|
|||||||
|
|
||||||
[[deps.Graphs]]
|
[[deps.Graphs]]
|
||||||
deps = ["ArnoldiMethod", "DataStructures", "Inflate", "LinearAlgebra", "Random", "SimpleTraits", "SparseArrays", "Statistics"]
|
deps = ["ArnoldiMethod", "DataStructures", "Inflate", "LinearAlgebra", "Random", "SimpleTraits", "SparseArrays", "Statistics"]
|
||||||
git-tree-sha1 = "031d63d09bd3e6e319df66bb466f5c3e8d147bee"
|
git-tree-sha1 = "7eb45fe833a5b7c51cf6d89c5a841d5967e44be3"
|
||||||
uuid = "86223c79-3864-5bf0-83f7-82e725a168b6"
|
uuid = "86223c79-3864-5bf0-83f7-82e725a168b6"
|
||||||
version = "1.13.4"
|
version = "1.14.0"
|
||||||
weakdeps = ["Distributed", "SharedArrays"]
|
weakdeps = ["Distributed", "SharedArrays"]
|
||||||
|
|
||||||
[deps.Graphs.extensions]
|
[deps.Graphs.extensions]
|
||||||
@@ -1101,9 +1101,9 @@ version = "0.5.16"
|
|||||||
|
|
||||||
[[deps.Makie]]
|
[[deps.Makie]]
|
||||||
deps = ["Animations", "Base64", "CRC32c", "ColorBrewer", "ColorSchemes", "ColorTypes", "Colors", "ComputePipeline", "Contour", "Dates", "DelaunayTriangulation", "Distributions", "DocStringExtensions", "Downloads", "FFMPEG_jll", "FileIO", "FilePaths", "FixedPointNumbers", "Format", "FreeType", "FreeTypeAbstraction", "GeometryBasics", "GridLayoutBase", "ImageBase", "ImageIO", "InteractiveUtils", "Interpolations", "IntervalSets", "InverseFunctions", "Isoband", "KernelDensity", "LaTeXStrings", "LinearAlgebra", "MacroTools", "Markdown", "MathTeXEngine", "Observables", "OffsetArrays", "PNGFiles", "Packing", "Pkg", "PlotUtils", "PolygonOps", "PrecompileTools", "Printf", "REPL", "Random", "RelocatableFolders", "Scratch", "ShaderAbstractions", "Showoff", "SignedDistanceFields", "SparseArrays", "Statistics", "StatsBase", "StatsFuns", "StructArrays", "TriplotBase", "UnicodeFun", "Unitful"]
|
deps = ["Animations", "Base64", "CRC32c", "ColorBrewer", "ColorSchemes", "ColorTypes", "Colors", "ComputePipeline", "Contour", "Dates", "DelaunayTriangulation", "Distributions", "DocStringExtensions", "Downloads", "FFMPEG_jll", "FileIO", "FilePaths", "FixedPointNumbers", "Format", "FreeType", "FreeTypeAbstraction", "GeometryBasics", "GridLayoutBase", "ImageBase", "ImageIO", "InteractiveUtils", "Interpolations", "IntervalSets", "InverseFunctions", "Isoband", "KernelDensity", "LaTeXStrings", "LinearAlgebra", "MacroTools", "Markdown", "MathTeXEngine", "Observables", "OffsetArrays", "PNGFiles", "Packing", "Pkg", "PlotUtils", "PolygonOps", "PrecompileTools", "Printf", "REPL", "Random", "RelocatableFolders", "Scratch", "ShaderAbstractions", "Showoff", "SignedDistanceFields", "SparseArrays", "Statistics", "StatsBase", "StatsFuns", "StructArrays", "TriplotBase", "UnicodeFun", "Unitful"]
|
||||||
git-tree-sha1 = "d1b974f376c24dad02c873e951c5cd4e351cd7c2"
|
git-tree-sha1 = "68af66ec16af8b152309310251ecb4fbfe39869f"
|
||||||
uuid = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a"
|
uuid = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a"
|
||||||
version = "0.24.8"
|
version = "0.24.9"
|
||||||
|
|
||||||
[deps.Makie.extensions]
|
[deps.Makie.extensions]
|
||||||
MakieDynamicQuantitiesExt = "DynamicQuantities"
|
MakieDynamicQuantitiesExt = "DynamicQuantities"
|
||||||
@@ -1129,9 +1129,9 @@ version = "0.6.7"
|
|||||||
|
|
||||||
[[deps.MbedTLS]]
|
[[deps.MbedTLS]]
|
||||||
deps = ["Dates", "MbedTLS_jll", "MozillaCACerts_jll", "NetworkOptions", "Random", "Sockets"]
|
deps = ["Dates", "MbedTLS_jll", "MozillaCACerts_jll", "NetworkOptions", "Random", "Sockets"]
|
||||||
git-tree-sha1 = "c067a280ddc25f196b5e7df3877c6b226d390aaf"
|
git-tree-sha1 = "8785729fa736197687541f7053f6d8ab7fc44f92"
|
||||||
uuid = "739be429-bea8-5141-9913-cc70e7f3736d"
|
uuid = "739be429-bea8-5141-9913-cc70e7f3736d"
|
||||||
version = "1.1.9"
|
version = "1.1.10"
|
||||||
|
|
||||||
[[deps.MbedTLS_jll]]
|
[[deps.MbedTLS_jll]]
|
||||||
deps = ["Artifacts", "JLLWrappers", "Libdl"]
|
deps = ["Artifacts", "JLLWrappers", "Libdl"]
|
||||||
@@ -1496,9 +1496,9 @@ version = "1.3.3"
|
|||||||
|
|
||||||
[[deps.Preferences]]
|
[[deps.Preferences]]
|
||||||
deps = ["TOML"]
|
deps = ["TOML"]
|
||||||
git-tree-sha1 = "522f093a29b31a93e34eaea17ba055d850edea28"
|
git-tree-sha1 = "8b770b60760d4451834fe79dd483e318eee709c4"
|
||||||
uuid = "21216c6a-2e73-6563-6e65-726566657250"
|
uuid = "21216c6a-2e73-6563-6e65-726566657250"
|
||||||
version = "1.5.1"
|
version = "1.5.2"
|
||||||
|
|
||||||
[[deps.PrettyTables]]
|
[[deps.PrettyTables]]
|
||||||
deps = ["Crayons", "LaTeXStrings", "Markdown", "PrecompileTools", "Printf", "REPL", "Reexport", "StringManipulation", "Tables"]
|
deps = ["Crayons", "LaTeXStrings", "Markdown", "PrecompileTools", "Printf", "REPL", "Reexport", "StringManipulation", "Tables"]
|
||||||
@@ -1535,9 +1535,9 @@ uuid = "92933f4c-e287-5a05-a399-4b506db050ca"
|
|||||||
version = "1.11.0"
|
version = "1.11.0"
|
||||||
|
|
||||||
[[deps.PtrArrays]]
|
[[deps.PtrArrays]]
|
||||||
git-tree-sha1 = "1d36ef11a9aaf1e8b74dacc6a731dd1de8fd493d"
|
git-tree-sha1 = "4fbbafbc6251b883f4d2705356f3641f3652a7fe"
|
||||||
uuid = "43287f4e-b6f4-7ad1-bb20-aadabca52c3d"
|
uuid = "43287f4e-b6f4-7ad1-bb20-aadabca52c3d"
|
||||||
version = "1.3.0"
|
version = "1.4.0"
|
||||||
|
|
||||||
[[deps.QOI]]
|
[[deps.QOI]]
|
||||||
deps = ["ColorTypes", "FileIO", "FixedPointNumbers"]
|
deps = ["ColorTypes", "FileIO", "FixedPointNumbers"]
|
||||||
@@ -1547,27 +1547,33 @@ version = "1.0.2"
|
|||||||
|
|
||||||
[[deps.Qt6Base_jll]]
|
[[deps.Qt6Base_jll]]
|
||||||
deps = ["Artifacts", "CompilerSupportLibraries_jll", "Fontconfig_jll", "Glib_jll", "JLLWrappers", "Libdl", "Libglvnd_jll", "OpenSSL_jll", "Vulkan_Loader_jll", "Xorg_libSM_jll", "Xorg_libXext_jll", "Xorg_libXrender_jll", "Xorg_libxcb_jll", "Xorg_xcb_util_cursor_jll", "Xorg_xcb_util_image_jll", "Xorg_xcb_util_keysyms_jll", "Xorg_xcb_util_renderutil_jll", "Xorg_xcb_util_wm_jll", "Zlib_jll", "libinput_jll", "xkbcommon_jll"]
|
deps = ["Artifacts", "CompilerSupportLibraries_jll", "Fontconfig_jll", "Glib_jll", "JLLWrappers", "Libdl", "Libglvnd_jll", "OpenSSL_jll", "Vulkan_Loader_jll", "Xorg_libSM_jll", "Xorg_libXext_jll", "Xorg_libXrender_jll", "Xorg_libxcb_jll", "Xorg_xcb_util_cursor_jll", "Xorg_xcb_util_image_jll", "Xorg_xcb_util_keysyms_jll", "Xorg_xcb_util_renderutil_jll", "Xorg_xcb_util_wm_jll", "Zlib_jll", "libinput_jll", "xkbcommon_jll"]
|
||||||
git-tree-sha1 = "34f7e5d2861083ec7596af8b8c092531facf2192"
|
git-tree-sha1 = "d7a4bff94f42208ce3cf6bc8e4e7d1d663e7ee8b"
|
||||||
uuid = "c0090381-4147-56d7-9ebc-da0b1113ec56"
|
uuid = "c0090381-4147-56d7-9ebc-da0b1113ec56"
|
||||||
version = "6.8.2+2"
|
version = "6.10.2+1"
|
||||||
|
|
||||||
[[deps.Qt6Declarative_jll]]
|
[[deps.Qt6Declarative_jll]]
|
||||||
deps = ["Artifacts", "JLLWrappers", "Libdl", "Qt6Base_jll", "Qt6ShaderTools_jll"]
|
deps = ["Artifacts", "JLLWrappers", "Libdl", "Qt6Base_jll", "Qt6ShaderTools_jll", "Qt6Svg_jll"]
|
||||||
git-tree-sha1 = "da7adf145cce0d44e892626e647f9dcbe9cb3e10"
|
git-tree-sha1 = "d5b7dd0e226774cbd87e2790e34def09245c7eab"
|
||||||
uuid = "629bc702-f1f5-5709-abd5-49b8460ea067"
|
uuid = "629bc702-f1f5-5709-abd5-49b8460ea067"
|
||||||
version = "6.8.2+1"
|
version = "6.10.2+1"
|
||||||
|
|
||||||
[[deps.Qt6ShaderTools_jll]]
|
[[deps.Qt6ShaderTools_jll]]
|
||||||
deps = ["Artifacts", "JLLWrappers", "Libdl", "Qt6Base_jll"]
|
deps = ["Artifacts", "JLLWrappers", "Libdl", "Qt6Base_jll"]
|
||||||
git-tree-sha1 = "9eca9fc3fe515d619ce004c83c31ffd3f85c7ccf"
|
git-tree-sha1 = "4d85eedf69d875982c46643f6b4f66919d7e157b"
|
||||||
uuid = "ce943373-25bb-56aa-8eca-768745ed7b5a"
|
uuid = "ce943373-25bb-56aa-8eca-768745ed7b5a"
|
||||||
version = "6.8.2+1"
|
version = "6.10.2+1"
|
||||||
|
|
||||||
|
[[deps.Qt6Svg_jll]]
|
||||||
|
deps = ["Artifacts", "JLLWrappers", "Libdl", "Qt6Base_jll"]
|
||||||
|
git-tree-sha1 = "81587ff5ff25a4e1115ce191e36285ede0334c9d"
|
||||||
|
uuid = "6de9746b-f93d-5813-b365-ba18ad4a9cf3"
|
||||||
|
version = "6.10.2+0"
|
||||||
|
|
||||||
[[deps.Qt6Wayland_jll]]
|
[[deps.Qt6Wayland_jll]]
|
||||||
deps = ["Artifacts", "JLLWrappers", "Libdl", "Qt6Base_jll", "Qt6Declarative_jll"]
|
deps = ["Artifacts", "JLLWrappers", "Libdl", "Qt6Base_jll", "Qt6Declarative_jll"]
|
||||||
git-tree-sha1 = "8f528b0851b5b7025032818eb5abbeb8a736f853"
|
git-tree-sha1 = "672c938b4b4e3e0169a07a5f227029d4905456f2"
|
||||||
uuid = "e99dba38-086e-5de3-a5b1-6e4c66e897c3"
|
uuid = "e99dba38-086e-5de3-a5b1-6e4c66e897c3"
|
||||||
version = "6.8.2+2"
|
version = "6.10.2+1"
|
||||||
|
|
||||||
[[deps.QuadGK]]
|
[[deps.QuadGK]]
|
||||||
deps = ["DataStructures", "LinearAlgebra"]
|
deps = ["DataStructures", "LinearAlgebra"]
|
||||||
@@ -1824,9 +1830,9 @@ version = "0.15.8"
|
|||||||
|
|
||||||
[[deps.StringManipulation]]
|
[[deps.StringManipulation]]
|
||||||
deps = ["PrecompileTools"]
|
deps = ["PrecompileTools"]
|
||||||
git-tree-sha1 = "a3c1536470bf8c5e02096ad4853606d7c8f62721"
|
git-tree-sha1 = "d05693d339e37d6ab134c5ab53c29fce5ee5d7d5"
|
||||||
uuid = "892a3eda-7b42-436c-8928-eab12a02cf0e"
|
uuid = "892a3eda-7b42-436c-8928-eab12a02cf0e"
|
||||||
version = "0.4.2"
|
version = "0.4.4"
|
||||||
|
|
||||||
[[deps.StructArrays]]
|
[[deps.StructArrays]]
|
||||||
deps = ["ConstructionBase", "DataAPI", "Tables"]
|
deps = ["ConstructionBase", "DataAPI", "Tables"]
|
||||||
@@ -1909,9 +1915,9 @@ version = "1.11.0"
|
|||||||
|
|
||||||
[[deps.TiffImages]]
|
[[deps.TiffImages]]
|
||||||
deps = ["ColorTypes", "DataStructures", "DocStringExtensions", "FileIO", "FixedPointNumbers", "IndirectArrays", "Inflate", "Mmap", "OffsetArrays", "PkgVersion", "PrecompileTools", "ProgressMeter", "SIMD", "UUIDs"]
|
deps = ["ColorTypes", "DataStructures", "DocStringExtensions", "FileIO", "FixedPointNumbers", "IndirectArrays", "Inflate", "Mmap", "OffsetArrays", "PkgVersion", "PrecompileTools", "ProgressMeter", "SIMD", "UUIDs"]
|
||||||
git-tree-sha1 = "98b9352a24cb6a2066f9ababcc6802de9aed8ad8"
|
git-tree-sha1 = "08c10bc34f4e7743f530793d0985bf3c254e193d"
|
||||||
uuid = "731e570b-9d59-4bfa-96dc-6df516fadf69"
|
uuid = "731e570b-9d59-4bfa-96dc-6df516fadf69"
|
||||||
version = "0.11.6"
|
version = "0.11.8"
|
||||||
|
|
||||||
[[deps.TikzGraphs]]
|
[[deps.TikzGraphs]]
|
||||||
deps = ["Graphs", "LaTeXStrings", "TikzPictures"]
|
deps = ["Graphs", "LaTeXStrings", "TikzPictures"]
|
||||||
|
|||||||
@@ -108,6 +108,7 @@ format:
|
|||||||
julia-color-typst:
|
julia-color-typst:
|
||||||
papersize: a4
|
papersize: a4
|
||||||
toc: true
|
toc: true
|
||||||
|
toc-depth: 2
|
||||||
number-depth: 2
|
number-depth: 2
|
||||||
fig-cap-location: bottom
|
fig-cap-location: bottom
|
||||||
fig-pos: H
|
fig-pos: H
|
||||||
|
|||||||
@@ -17,11 +17,11 @@ Base.stdout = QuartoNotebookWorker.with_context(stdout)
|
|||||||
|
|
||||||
There were - depending on manufacturer, country, programming language, operating system, etc. - a large variety of encodings.
|
There were - depending on manufacturer, country, programming language, operating system, etc. - a large variety of encodings.
|
||||||
|
|
||||||
Still relevant today:
|
Still relevant today are:
|
||||||
|
|
||||||
|
|
||||||
### ASCII
|
### ASCII
|
||||||
The _American Standard Code for Information Interchange_ was published as a standard in the USA in 1963.
|
The American Standard Code for Information Interchange (ASCII) was published as a standard in the USA in 1963.
|
||||||
|
|
||||||
- It defines $2^7=128$ characters, namely:
|
- It defines $2^7=128$ characters, namely:
|
||||||
- 33 control characters, such as `newline`, `escape`, `end of transmission/file`, `delete`
|
- 33 control characters, such as `newline`, `escape`, `end of transmission/file`, `delete`
|
||||||
@@ -40,7 +40,7 @@ The _American Standard Code for Information Interchange_ was published as a stan
|
|||||||
### ISO 8859 Character Sets
|
### ISO 8859 Character Sets
|
||||||
|
|
||||||
- ASCII uses only 7 bits.
|
- ASCII uses only 7 bits.
|
||||||
- In a byte, one can fit another 128 characters by setting the 8th bit.
|
- In a byte, you can fit another 128 characters by setting the 8th bit.
|
||||||
- In 1987/88, various 1-byte encodings were standardized in ISO 8859, all ASCII-compatible, including:
|
- In 1987/88, various 1-byte encodings were standardized in ISO 8859, all ASCII-compatible, including:
|
||||||
|
|
||||||
:::{.narrow}
|
:::{.narrow}
|
||||||
@@ -61,11 +61,11 @@ The _American Standard Code for Information Interchange_ was published as a stan
|
|||||||
|
|
||||||
## Unicode
|
## Unicode
|
||||||
|
|
||||||
The goal of the Unicode Consortium is a uniform encoding for all scripts of the world.
|
The goal of the Unicode Consortium is a uniform encoding for all scripts worldwide.
|
||||||
|
|
||||||
- Unicode version 1 was published in 1991
|
- Unicode version 1 was published in 1991
|
||||||
- Unicode version 15.1 was published in 2023 with 149,813 characters, including:
|
- Unicode version 17 was published in 2025 with 159,801 characters, including:
|
||||||
- 161 scripts
|
- 172 scripts
|
||||||
- mathematical and technical symbols
|
- mathematical and technical symbols
|
||||||
- Emojis and other symbols, control and formatting characters
|
- Emojis and other symbols, control and formatting characters
|
||||||
- Over 90,000 characters are assigned to the CJK scripts (Chinese/Japanese/Korean)
|
- Over 90,000 characters are assigned to the CJK scripts (Chinese/Japanese/Korean)
|
||||||
@@ -73,21 +73,19 @@ The goal of the Unicode Consortium is a uniform encoding for all scripts of the
|
|||||||
|
|
||||||
### Technical Details
|
### Technical Details
|
||||||
|
|
||||||
- Each character is assigned a `codepoint`. This is simply a sequential number.
|
- Each character is assigned a `codepoint`, which is simply a sequential number written hexadecimally
|
||||||
- This number is written hexadecimally
|
- either with 4 digit as `U+XXXX` (zeroth plane)
|
||||||
- either 4-digit as `U+XXXX` (0th plane)
|
- or with 6 digit as `U+XXXXXX` (further planes)
|
||||||
- or 6-digit as `U+XXXXXX` (further planes)
|
- Each plane ranges from `U+XY0000` to `U+XYFFFF`, thus containing $2^{16}=65\;534$ characters.
|
||||||
- Each plane ranges from `U+XY0000` to `U+XYFFFF`, thus can contain $2^{16}=65\;534$ characters.
|
- 17 planes `XY=00` to `XY=10` are provided, giving a value range from `U+0000` to `U+10FFFF`.
|
||||||
- 17 planes `XY=00` to `XY=10` are provided so far, thus the value range from `U+0000` to `U+10FFFF`.
|
|
||||||
- Thus, a maximum of 21 bits per character are needed.
|
- Thus, a maximum of 21 bits per character are needed.
|
||||||
- The total number of possible codepoints is slightly less than 0x10FFFF, as certain areas are not used for technical reasons. It is about 1.1 million, so there is still much room.
|
- The total number of possible codepoints is slightly less than 0x10FFFF, as certain areas are not used for technical reasons. It is about 1.1 million, so there is still much room.
|
||||||
- So far, codepoints from the planes have been assigned only from
|
- So far, codepoints have been assigned only from these planes:
|
||||||
- Plane 0 = BMP _Basic Multilingual Plane_ `U+0000 - U+FFFF`,
|
- Plane 0 = BMP (Basic Multilingual Plane) `U+0000 - U+FFFF`,
|
||||||
- Plane 1 = SMP _Supplementary Multilingual Plane_ `U+010000 - U+01FFFF`,
|
- Plane 1 = SMP (Supplementary Multilingual Plane) `U+010000 - U+01FFFF`,
|
||||||
- Plane 2 = SIP _Supplementary Ideographic Plane_ `U+020000 - U+02FFFF`,
|
- Plane 2 = SIP (Supplementary Ideographic Plane) `U+020000 - U+02FFFF`,
|
||||||
- Plane 3 = TIP _Tertiary Ideographic Plane_ `U+030000 - U+03FFFF` and
|
- Plane 3 = TIP (Tertiary Ideographic Plane) `U+030000 - U+03FFFF`, and
|
||||||
- Plane 14 = SSP _Supplementary Special-purpose Plane_ `U+0E0000 - U+0EFFFF`
|
- Plane 14 = SSP (Supplementary Special-purpose Plane) `U+0E0000 - U+0EFFFF`.
|
||||||
have been assigned.
|
|
||||||
- `U+0000` to `U+007F` is identical to ASCII
|
- `U+0000` to `U+007F` is identical to ASCII
|
||||||
- `U+0000` to `U+00FF` is identical to ISO 8859-1 (Latin-1)
|
- `U+0000` to `U+00FF` is identical to ISO 8859-1 (Latin-1)
|
||||||
|
|
||||||
@@ -101,7 +99,7 @@ In the standard, each character is described by
|
|||||||
- script direction
|
- script direction
|
||||||
- category: uppercase letter, lowercase letter, modifier letter, digit, punctuation, symbol, separator,....
|
- category: uppercase letter, lowercase letter, modifier letter, digit, punctuation, symbol, separator,....
|
||||||
|
|
||||||
In the Unicode standard, this looks like this (simplified, only codepoint and name):
|
In the Unicode standard, this looks like (simplified, only codepoint and name):
|
||||||
```
|
```
|
||||||
...
|
...
|
||||||
U+0041 LATIN CAPITAL LETTER A
|
U+0041 LATIN CAPITAL LETTER A
|
||||||
@@ -144,8 +142,9 @@ Alternatively, you can use the PDF version of this page. There, all fonts are em
|
|||||||
A small helper function:
|
A small helper function:
|
||||||
```{julia}
|
```{julia}
|
||||||
function printuc(c, n)
|
function printuc(c, n)
|
||||||
for i in 0:n-1
|
for i in 1:n
|
||||||
print(c + i)
|
print(c + i -1)
|
||||||
|
if i%70 == 0 print("\n") end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
@@ -205,8 +204,8 @@ printuc('\U16a0', 40)
|
|||||||
__Phaistos Disc__
|
__Phaistos Disc__
|
||||||
|
|
||||||
- This script is not deciphered.
|
- This script is not deciphered.
|
||||||
- It is unclear what language is represented.
|
- It is unclear what language it represents.
|
||||||
- There is only one single document in this script: the Phaistos Disc from the Bronze Age
|
- There is only one single document in this script: the Phaistos Disc from the Bronze Age.
|
||||||
|
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
@@ -231,7 +230,7 @@ printuc('\U101D0', 46 )
|
|||||||
|
|
||||||
_Unicode transformation formats_ define how a sequence of codepoints is represented as a sequence of bytes.
|
_Unicode transformation formats_ define how a sequence of codepoints is represented as a sequence of bytes.
|
||||||
|
|
||||||
Since the codepoints are of different lengths, they cannot simply be written down one after the other. Where does one end and the next begin?
|
Since codepoints are of different lengths, they cannot simply be written down one after the other. Where does one end and the next begin?
|
||||||
|
|
||||||
- __UTF-32__: The simplest but also most memory-intensive is to make them all the same length. Each codepoint is encoded in 4 bytes = 32 bits.
|
- __UTF-32__: The simplest but also most memory-intensive is to make them all the same length. Each codepoint is encoded in 4 bytes = 32 bits.
|
||||||
- In __UTF-16__, a codepoint is represented either with 2 bytes or with 4 bytes.
|
- In __UTF-16__, a codepoint is represented either with 2 bytes or with 4 bytes.
|
||||||
@@ -243,14 +242,14 @@ Since the codepoints are of different lengths, they cannot simply be written dow
|
|||||||
|
|
||||||
- For each codepoint, 1, 2, 3, or 4 full bytes are used.
|
- For each codepoint, 1, 2, 3, or 4 full bytes are used.
|
||||||
|
|
||||||
- With variable-length encoding, one must be able to recognize which byte sequences belong together:
|
- With variable-length encoding, you must be able to recognize which byte sequences belong together:
|
||||||
- A byte of the form 0xxxxxxx represents an ASCII codepoint of length 1.
|
- A byte of the form 0xxxxxxx represents an ASCII codepoint of length 1.
|
||||||
- A byte of the form 110xxxxx starts a 2-byte code.
|
- A byte of the form 110xxxxx starts a 2-byte code.
|
||||||
- A byte of the form 1110xxxx starts a 3-byte code.
|
- A byte of the form 1110xxxx starts a 3-byte code.
|
||||||
- A byte of the form 11110xxx starts a 4-byte code.
|
- A byte of the form 11110xxx starts a 4-byte code.
|
||||||
- All further bytes of a 2-, 3-, or 4-byte code have the form 10xxxxxx.
|
- All further bytes of a 2-, 3-, or 4-byte code have the form 10xxxxxx.
|
||||||
|
|
||||||
- Thus, the space available for the codepoint (number of x):
|
- Thus, the space available for the codepoint (number of x) is:
|
||||||
- One-byte code: 7 bits
|
- One-byte code: 7 bits
|
||||||
- Two-byte code: 5 + 6 = 11 bits
|
- Two-byte code: 5 + 6 = 11 bits
|
||||||
- Three-byte code: 4 + 6 + 6 = 16 bits
|
- Three-byte code: 4 + 6 + 6 = 16 bits
|
||||||
@@ -258,24 +257,22 @@ Since the codepoints are of different lengths, they cannot simply be written dow
|
|||||||
|
|
||||||
- Thus, every ASCII text is automatically also a correctly encoded UTF-8 text.
|
- Thus, every ASCII text is automatically also a correctly encoded UTF-8 text.
|
||||||
|
|
||||||
- If the 17 planes (= 21 bits = 1.1 million possible characters) defined for Unicode so far are ever expanded, UTF-8 will be expanded to 5- and 6-byte codes.
|
- If the 17 planes (equivalent to 21 bits, resulting in approximately 1.1 million possible characters) currently defined in Unicode are ever depleted, UTF-8 can be extended to include 5- and 6-byte code sequences.
|
||||||
|
|
||||||
|
## Characters and Strings in Julia
|
||||||
|
|
||||||
## Characters and Character Strings in Julia
|
### Characters
|
||||||
|
|
||||||
### Characters: `Char`
|
|
||||||
|
|
||||||
The `Char` type encodes a single Unicode character.
|
The `Char` type encodes a single Unicode character.
|
||||||
|
|
||||||
- Julia uses single quotes for this: `'a'`.
|
- Julia uses single quotes for characters: `'a'`.
|
||||||
- A `Char` occupies 4 bytes of memory and
|
- A `Char` occupies 4 bytes of memory and
|
||||||
- represents a Unicode codepoint.
|
- represents a Unicode codepoint.
|
||||||
- `Char`s can be converted to/from `UInt`s and
|
- `Char`s can be converted to/from `UInt`s and
|
||||||
- the integer value is equal to the Unicode codepoint.
|
- the integer value is equal to the Unicode codepoint.
|
||||||
|
|
||||||
|
|
||||||
`Char`s can be converted to/from `UInt`s.
|
`Char`s can be converted to/from `UInt`s:
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
UInt('a')
|
UInt('a')
|
||||||
```
|
```
|
||||||
@@ -285,10 +282,10 @@ UInt('a')
|
|||||||
b = Char(0x2656)
|
b = Char(0x2656)
|
||||||
```
|
```
|
||||||
|
|
||||||
### Character Strings: `String`
|
### Strings
|
||||||
|
|
||||||
- For strings, Julia uses double quotes: `"a"`.
|
- In Julia, strings are denoted with double quotes: `"a"`.
|
||||||
- They are UTF-8 encoded, i.e., one character can be between 1 and 4 bytes long.
|
- These strings are encoded in UTF-8, where a single character may consist of 1 to 4 bytes.
|
||||||
|
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
@@ -305,7 +302,6 @@ __For a non-ASCII string, the number of bytes and the number of characters diffe
|
|||||||
asciistr = "Hello World!"
|
asciistr = "Hello World!"
|
||||||
@show length(asciistr) ncodeunits(asciistr);
|
@show length(asciistr) ncodeunits(asciistr);
|
||||||
```
|
```
|
||||||
(The space, of course, also counts.)
|
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
str = "😄 Hellö 🎶"
|
str = "😄 Hellö 🎶"
|
||||||
@@ -323,7 +319,7 @@ end
|
|||||||
|
|
||||||
### Concatenation of Strings
|
### Concatenation of Strings
|
||||||
|
|
||||||
"Strings with concatenation form a non-commutative monoid."
|
Strings with concatenation form a non-commutative monoid.
|
||||||
|
|
||||||
Therefore, Julia writes concatenation multiplicatively.
|
Therefore, Julia writes concatenation multiplicatively.
|
||||||
```{julia}
|
```{julia}
|
||||||
@@ -338,9 +334,7 @@ str^3, str^0
|
|||||||
|
|
||||||
### String Interpolation
|
### String Interpolation
|
||||||
|
|
||||||
The dollar sign has a special function in strings, which we have often used in
|
The dollar sign serves a special purpose in strings, frequently utilized within `print()` statements. It enables the interpolation of variables or expressions.
|
||||||
`print()` statements. One can interpolate a variable or expression with it:
|
|
||||||
|
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
a = 33.4
|
a = 33.4
|
||||||
@@ -351,8 +345,8 @@ s = "The result for $b is equal to $a and the doubled square root of it is $(2sq
|
|||||||
|
|
||||||
### Backslash Escape Sequences
|
### Backslash Escape Sequences
|
||||||
|
|
||||||
The _backslash_ `\` also has a special function in string constants.
|
The backslash `\` also has a special function in string constants.
|
||||||
Julia uses the backslash codings known from C and other languages for special characters and for dollar signs and backslashes themselves:
|
Julia uses the backslash codings known from C and other languages for special characters, dollar signs, and backslashes themselves:
|
||||||
|
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
@@ -364,9 +358,7 @@ print(s)
|
|||||||
|
|
||||||
### Triple Quotes
|
### Triple Quotes
|
||||||
|
|
||||||
Strings can also be delimited with triple quotes.
|
Strings may also be enclosed in triple quotes, preserving line breaks and embedded quotes:
|
||||||
In this form, line breaks and quotes are preserved:
|
|
||||||
|
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
s = """
|
s = """
|
||||||
@@ -380,8 +372,7 @@ print(s)
|
|||||||
|
|
||||||
### Raw Strings
|
### Raw Strings
|
||||||
|
|
||||||
In a `raw string`, all backslash codings except `\"` are disabled:
|
In a `raw string`, all backslash escape sequences except for `\"` are disabled:
|
||||||
|
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
s = raw"A $ and a \ and two \\ and a 'bla'..."
|
s = raw"A $ and a \ and two \\ and a 'bla'..."
|
||||||
@@ -400,7 +391,7 @@ print(s)
|
|||||||
|
|
||||||
### Application to Strings
|
### Application to Strings
|
||||||
|
|
||||||
These tests can e.g. be used with `all()`, `any()`, or `count()` on strings:
|
These tests can be used on strings with `all()`, `any()`, or `count()`:
|
||||||
|
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
@@ -449,11 +440,10 @@ replace("π is irrational.", "is" => "is allegedly")
|
|||||||
|
|
||||||
## Indexing of Strings
|
## Indexing of Strings
|
||||||
|
|
||||||
Strings are immutable but indexable. There are a few special features here.
|
Strings are immutable but indexable, with a few special features:
|
||||||
|
|
||||||
- The index numbers the bytes of the string.
|
- The index numbers the bytes of the string.
|
||||||
- For a non-ASCII string, not all indices are valid, because
|
- For a non-ASCII string, not all indices are valid because a valid index always addresses a Unicode character.
|
||||||
- a valid index always addresses a Unicode character.
|
|
||||||
|
|
||||||
Our example string:
|
Our example string:
|
||||||
```{julia}
|
```{julia}
|
||||||
@@ -476,7 +466,7 @@ Only the 5th byte is a new character:
|
|||||||
str[5]
|
str[5]
|
||||||
```
|
```
|
||||||
|
|
||||||
Even when addressing substrings, start and end must always be valid indices, i.e., the end index must also index the first byte of a character, and that character is the last of the substring.
|
Even when addressing substrings, start and end must always be valid indices; i.e., the end index must also index the first byte of a character, and that character is the last of the substring.
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
str[1:7]
|
str[1:7]
|
||||||
@@ -504,8 +494,8 @@ The function `nextind()` returns the next valid index.
|
|||||||
|
|
||||||
Why does Julia use a byte index instead of a character index? The main reason is the efficiency of indexing.
|
Why does Julia use a byte index instead of a character index? The main reason is the efficiency of indexing.
|
||||||
|
|
||||||
- In a long string, e.g., a book text, the position `s[123455]` can be found quickly with a byte index.
|
- In a long string (e.g., book text), the position `s[123455]` can be found quickly with a byte index.
|
||||||
- A character index would have to traverse the entire string in UTF-8 encoding to find the n-th character, since the characters can be 1, 2, 3, or 4 bytes long.
|
- A character index would have to traverse the entire string in UTF-8 encoding to find the n-th character, since characters can be 1, 2, 3, or 4 bytes long.
|
||||||
|
|
||||||
|
|
||||||
Some functions return indices or ranges as results. They always return valid indices:
|
Some functions return indices or ranges as results. They always return valid indices:
|
||||||
@@ -530,7 +520,7 @@ str2 = "αβγδϵ"^3
|
|||||||
n = findfirst('γ', str2)
|
n = findfirst('γ', str2)
|
||||||
```
|
```
|
||||||
|
|
||||||
So one can continue searching from the next valid index after `n=5`:
|
So you can continue searching from the next valid index after `n=5`:
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
findnext('γ', str2, nextind(str2, n))
|
findnext('γ', str2, nextind(str2, n))
|
||||||
|
|||||||
@@ -15,9 +15,9 @@ using InteractiveUtils
|
|||||||
using LinearAlgebra
|
using LinearAlgebra
|
||||||
```
|
```
|
||||||
|
|
||||||
The `LinearAlgebra` package provides among other things:
|
The `LinearAlgebra` package provides, among other things:
|
||||||
|
|
||||||
- additional subtypes of `AbstractMatrix`: usable like other matrices, e.g.,
|
- additional subtypes of `AbstractMatrix` which are usable like other matrices, among them:
|
||||||
|
|
||||||
- `Tridiagonal`
|
- `Tridiagonal`
|
||||||
- `SymTridiagonal`
|
- `SymTridiagonal`
|
||||||
@@ -44,7 +44,7 @@ The `LinearAlgebra` package provides among other things:
|
|||||||
|
|
||||||
- Access to BLAS/LAPACK functions
|
- Access to BLAS/LAPACK functions
|
||||||
|
|
||||||
## Matrix Types
|
## Special Matrix Types
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -68,20 +68,20 @@ Read-only index access is possible,
|
|||||||
A[1,4]
|
A[1,4]
|
||||||
```
|
```
|
||||||
|
|
||||||
write operations not necessarily:
|
Write operations are not necessarily possible:
|
||||||
```{julia}
|
```{julia}
|
||||||
#| error: true
|
#| error: true
|
||||||
A[1,3] = 17
|
A[1,3] = 17
|
||||||
```
|
```
|
||||||
|
|
||||||
Conversion to a 'normal' matrix is possible, for example, with `collect()`:
|
Conversion to a 'normal' matrix is possible using `collect()`:
|
||||||
```{julia}
|
```{julia}
|
||||||
A2 = collect(A)
|
A2 = collect(A)
|
||||||
```
|
```
|
||||||
|
|
||||||
### The Identity Matrix `I`
|
### The Identity Matrix `I`
|
||||||
|
|
||||||
`I` denotes an identity matrix (square, diagonal elements = 1, all others = 0) of the respective required size
|
`I` denotes an identity matrix (square, diagonal elements = 1, all others = 0) of the required size
|
||||||
|
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
@@ -91,7 +91,7 @@ A + 4I
|
|||||||
|
|
||||||
## Norms
|
## Norms
|
||||||
|
|
||||||
To be able to study questions such as conditioning or convergence of an algorithm, we need a metric. For linear spaces, it is appropriate to define the metric via a norm:
|
To study questions such as conditioning or convergence of an algorithm, we need a metric. For linear spaces, it is appropriate to define the metric via a norm:
|
||||||
$$
|
$$
|
||||||
d(x,y) := \lVert x-y\rVert
|
d(x,y) := \lVert x-y\rVert
|
||||||
$$
|
$$
|
||||||
@@ -109,12 +109,12 @@ which generalize the Euclidean norm $p=2$.
|
|||||||
|
|
||||||
## The Max-Norm $p=\infty$
|
## The Max-Norm $p=\infty$
|
||||||
|
|
||||||
Let $x_{\text{max}}$ be the _largest in absolute value_ component of $\mathbf{x}\in ℝ^n$. Then always
|
Let $x_{\text{max}}$ be the component of $\mathbf{x}\in ℝ^n$ with the largest absolute value. Then always
|
||||||
$$ \lvert x_{\text{max}}\rvert \le \lVert\mathbf{x}\rVert_p \le n^\frac{1}{p} \lvert x_{\text{max}}\rvert
|
$$ \lvert x_{\text{max}}\rvert \le \lVert\mathbf{x}\rVert_p \le n^\frac{1}{p} \lvert x_{\text{max}}\rvert
|
||||||
$$
|
$$
|
||||||
(Consider a vector whose components are all equal to $x_{\text{max}}$ respectively a vector whose components are all equal to zero except $x_{\text{max}}$.)
|
(Consider a vector whose components are all equal to $x_{\text{max}}$ respectively a vector whose components are all equal to zero except $x_{\text{max}}$.)
|
||||||
|
|
||||||
Thus follows
|
It follows that
|
||||||
$$
|
$$
|
||||||
\lim_{p \rightarrow \infty} \lVert\mathbf{x}\rVert_p = \lvert x_{\text{max}}\rvert =: \lVert\mathbf{x}\rVert_\infty.
|
\lim_{p \rightarrow \infty} \lVert\mathbf{x}\rVert_p = \lvert x_{\text{max}}\rvert =: \lVert\mathbf{x}\rVert_\infty.
|
||||||
$$
|
$$
|
||||||
@@ -132,7 +132,7 @@ w = [-1, 2, 33.2]
|
|||||||
|
|
||||||
- If the 2nd argument `p` is missing, `p=2` is set.
|
- If the 2nd argument `p` is missing, `p=2` is set.
|
||||||
- The 2nd argument can also be `Inf` (i.e., $+\infty$).
|
- The 2nd argument can also be `Inf` (i.e., $+\infty$).
|
||||||
- The 1st argument can be any container full of numbers. The sum $\sum \lvert x_i\rvert^p$ extends over *all* elements of the container.
|
- The 1st argument can be any numerical container. The sum $\sum \lvert x_i\rvert^p$ extends over *all* elements of the container.
|
||||||
- Thus, for a matrix `norm(A)` is equal to the _Frobenius norm_ of the matrix `A`.
|
- Thus, for a matrix `norm(A)` is equal to the _Frobenius norm_ of the matrix `A`.
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
@@ -163,9 +163,9 @@ for p ∈ (0.8, 1, 1.5, 2, 3.001, 1000)
|
|||||||
end
|
end
|
||||||
fig1
|
fig1
|
||||||
```
|
```
|
||||||
As one can see, $p\ge 1$ must hold so that the unit ball is convex and $\lVert.\rVert_p$ is a norm.
|
We see that, $p\ge 1$ must hold so that the unit ball is convex and $\lVert.\rVert_p$ is a norm.
|
||||||
|
|
||||||
However, the Julia function `norm(v, p)` returns a result for arbitrary parameters `p`.
|
However, the Julia function `norm(v, p)` also works for parameters `p<1`.
|
||||||
|
|
||||||
|
|
||||||
### Induced Norms (Operator Norms)
|
### Induced Norms (Operator Norms)
|
||||||
@@ -185,7 +185,7 @@ $$
|
|||||||
$$
|
$$
|
||||||
:::
|
:::
|
||||||
|
|
||||||
Induced norms can only be calculated with difficulty for general $p$. Exceptions are the cases
|
Induced norms are difficult to calculate for general $p$. Exceptions are the cases
|
||||||
|
|
||||||
- $p=1$: column sum norm
|
- $p=1$: column sum norm
|
||||||
- $p=2$: spectral norm and
|
- $p=2$: spectral norm and
|
||||||
@@ -260,7 +260,7 @@ $$
|
|||||||
|
|
||||||
## Matrix Factorizations
|
## Matrix Factorizations
|
||||||
|
|
||||||
Basic tasks of numerical linear algebra:
|
The basic tasks of numerical linear algebra:
|
||||||
|
|
||||||
- Solve a system of linear equations $A\mathbf{x} = \mathbf{b}$.
|
- Solve a system of linear equations $A\mathbf{x} = \mathbf{b}$.
|
||||||
- If no solution exists, find the best approximation, i.e., the vector $\mathbf{x}$ that minimizes $\lVert A\mathbf{x} - \mathbf{b}\rVert$.
|
- If no solution exists, find the best approximation, i.e., the vector $\mathbf{x}$ that minimizes $\lVert A\mathbf{x} - \mathbf{b}\rVert$.
|
||||||
@@ -270,7 +270,6 @@ These tasks can be solved using matrix factorizations. Some fundamental matrix f
|
|||||||
|
|
||||||
- **LU decomposition** $A=L\cdot U$
|
- **LU decomposition** $A=L\cdot U$
|
||||||
- factorizes a matrix as a product of a _lower_ and an _upper_ triangular matrix
|
- factorizes a matrix as a product of a _lower_ and an _upper_ triangular matrix
|
||||||
- in German also called LR decomposition (but the Julia function is called `lu()`)
|
|
||||||
- always works (possibly after row exchanges - pivoting)
|
- always works (possibly after row exchanges - pivoting)
|
||||||
- **Cholesky decomposition** $A=L\cdot L^*$
|
- **Cholesky decomposition** $A=L\cdot L^*$
|
||||||
- the upper triangular matrix is the conjugate of the lower,
|
- the upper triangular matrix is the conjugate of the lower,
|
||||||
@@ -287,8 +286,7 @@ These tasks can be solved using matrix factorizations. Some fundamental matrix f
|
|||||||
|
|
||||||
### LU Factorization
|
### LU Factorization
|
||||||
|
|
||||||
LU factorization is Gaussian elimination. The result of Gaussian elimination is the upper triangular matrix $U$. The lower triangular matrix $L$ contains ones on the diagonal and the non-diagonal entries $l_{ij}$ are equal to minus the coefficients by which row $Z_j$ is multiplied and added to row $Z_i$ in the Gaussian algorithm.
|
LU factorization is Gaussian elimination. The result of Gaussian elimination is the upper triangular matrix $U$. The lower triangular matrix $L$ contains ones on the diagonal and the non-diagonal entries $l_{ij}$ are equal to minus the coefficients by which row $Z_j$ is multiplied and added to row $Z_i$ in Gaussian elimination:
|
||||||
An example:
|
|
||||||
|
|
||||||
:::{.content-visible unless-format="typst"}
|
:::{.content-visible unless-format="typst"}
|
||||||
$$
|
$$
|
||||||
@@ -394,9 +392,9 @@ $
|
|||||||
:::
|
:::
|
||||||
|
|
||||||
|
|
||||||
- Often in practice: $A\mathbf{x}=\mathbf{b}$ must be solved for one $A$ and many right-hand sides $\mathbf{b}$.
|
- In practice, it is often necessary to solve $A\mathbf{x}=\mathbf{b}$ for a fixed matrix $A$ and multiple right-hand sides $\mathbf{b}$.
|
||||||
- The factorization, whose effort grows cubically $\sim n^3$ with the matrix size $n$, only needs to be done once.
|
- The factorization of $A$, which has a cubic complexity $\sim n^3$ relative to the size $n$ of the matrix, needs to be performed only once.
|
||||||
- The subsequent effort of forward/backward substitution for each $\mathbf{b}$ is only quadratically $\sim n^2$.
|
- For each subsequent right-hand side $\mathbf{b}$, the forward and backward substitution steps have quadratic complexity $\sim n^2$.
|
||||||
|
|
||||||
The `LinearAlgebra` package of Julia contains the function `lu(A, options)` for calculating an LU decomposition:
|
The `LinearAlgebra` package of Julia contains the function `lu(A, options)` for calculating an LU decomposition:
|
||||||
```{julia}
|
```{julia}
|
||||||
@@ -447,9 +445,9 @@ $
|
|||||||
```
|
```
|
||||||
:::
|
:::
|
||||||
|
|
||||||
The goal is to make the entry $a_{i+1,j}$ disappear by adding an appropriate multiple of row $Z_i$ to row $Z_{i+1}$. This only works if the _pivot element_ ${\color{red}a_{ij}}$ is not zero. If ${\color{red}a_{ij}}=0$, we must exchange rows to fix this.
|
The goal is to make the entry $a_{i+1,j}$ disappear by adding an appropriate multiple of row $Z_i$ to row $Z_{i+1}$. This only works if the *pivot element* ${\color{red}a_{ij}}$ is not zero. If ${\color{red}a_{ij}}=0$, we must exchange rows to fix this.
|
||||||
|
|
||||||
Furthermore, the conditioning of the algorithm is best if we arrange the matrix at each step so that the pivot element is the largest in absolute value in the corresponding column of the remaining submatrix. In (row) pivoting, rows are exchanged at each step to ensure that
|
Furthermore, the conditioning of the algorithm is best if we arrange the matrix at each step so that the pivot element is the largest in absolute value in the corresponding column of the remaining submatrix. In (row) pivoting, rows are exchanged to ensure that
|
||||||
|
|
||||||
:::{.content-visible unless-format="typst"}
|
:::{.content-visible unless-format="typst"}
|
||||||
$$
|
$$
|
||||||
@@ -485,7 +483,8 @@ L, U, p = lu(A);
|
|||||||
p
|
p
|
||||||
```
|
```
|
||||||
|
|
||||||
The permutation vector indicates how the rows of the matrix have been permuted. It holds: $$ L\cdot U = PA$$. The syntax of indirect indexing allows applying the row permutation with the notation `A[p,:]`:
|
The permutation vector indicates how the rows of the matrix have been permuted. It holds: $$ L\cdot U = P A$$
|
||||||
|
Julia's syntax of indirect indexing allows applying the row permutation with the notation `A[p,:]`:
|
||||||
```{julia}
|
```{julia}
|
||||||
display(A)
|
display(A)
|
||||||
display(A[p,:])
|
display(A[p,:])
|
||||||
@@ -503,18 +502,17 @@ Verification:
|
|||||||
A * x - b
|
A * x - b
|
||||||
```
|
```
|
||||||
|
|
||||||
In Julia, the `\` operator hides a quite universal "matrix solver", and it can also be applied directly:
|
In Julia, the `\` operator hides a quite universal "matrix solver" which perfoms implicitely an appropriate matrix factorization:
|
||||||
```{julia}
|
```{julia}
|
||||||
A \ b
|
A \ b
|
||||||
```
|
```
|
||||||
An appropriate factorization is performed implicitly, but its result is not saved.
|
|
||||||
|
|
||||||
|
|
||||||
### QR Decomposition
|
### QR Decomposition
|
||||||
|
|
||||||
|
|
||||||
The function `qr()` returns a special QR object that contains the components $Q$ and $R$. The orthogonal (or unitary) matrix $Q$ is
|
The function `qr()` returns a special QR object that contains the components $Q$ and $R$. The orthogonal (or unitary) matrix $Q$ is
|
||||||
[in an optimized form](https://docs.julialang.org/en/v1/stdlib/LinearAlgebra/#man-linalg-abstractq) stored. Conversion to a "normal" matrix is, as always, possible with `collect()` if needed.
|
[in an optimized form](https://docs.julialang.org/en/v1/stdlib/LinearAlgebra/#man-linalg-abstractq). Conversion to a "normal" matrix is, as always, possible with `collect()` if needed.
|
||||||
```{julia}
|
```{julia}
|
||||||
F = qr(A)
|
F = qr(A)
|
||||||
@show typeof(F) typeof(F.Q)
|
@show typeof(F) typeof(F.Q)
|
||||||
@@ -525,10 +523,7 @@ display(F.R)
|
|||||||
### Appropriate Factorization
|
### Appropriate Factorization
|
||||||
|
|
||||||
|
|
||||||
The function `factorize()` returns a factorization form adapted to the matrix type, see [documentation](https://docs.julialang.org/en/v1/stdlib/LinearAlgebra/#LinearAlgebra.factorize) for details.
|
The function `factorize()` returns a factorization appropriate for the given matrix type; see [documentation](https://docs.julialang.org/en/v1/stdlib/LinearAlgebra/#LinearAlgebra.factorize) for details. This factorization can subsequently be utilized for multiple right-hand sides $\mathbf{b_1}, \mathbf{b_2},\ldots$.
|
||||||
If solutions for several right-hand sides $\mathbf{b_1}, \mathbf{b_2},...$ are needed, the factorization should only be performed once:
|
|
||||||
|
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
Af = factorize(A)
|
Af = factorize(A)
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -40,28 +40,27 @@ Base.active_module() = myactive_module()
|
|||||||
|
|
||||||
## Console
|
## Console
|
||||||
|
|
||||||
The operating system normally provides 3 channels (_streams_) for a program:
|
The operating system typically provides three channels (_streams_) for a program:
|
||||||
|
|
||||||
- Standard input channel `stdin`
|
- Standard input (`stdin`)
|
||||||
- Standard output channel `stdout` and
|
- Standard output (`stdout`)
|
||||||
- Standard error output channel `stderr`.
|
- Standard error (`stderr`)
|
||||||
|
|
||||||
When the program is started in a terminal (or console or shell), the program can read keyboard input via `stdin` and output appears in the terminal via `stdout` and `stdout`.
|
When executed in a terminal, console, or shell, the program reads keyboard input through `stdin` and outputs to the terminal via `stdout` and `stderr`.
|
||||||
|
|
||||||
|
|
||||||
- Writing to `stdout`: `print()`,`println()`,`printstyled()`
|
- Writing to `stdout`: `print()`, `println()`, `printstyled()`
|
||||||
- Writing to `stderr`: `print(strerr,...)`, `println(stderr,...)`, `printstyled(stderr,...)`
|
- Writing to `stderr`: `print(stderr,...)`, `println(stderr,...)`, `printstyled(stderr,...)`
|
||||||
- Reading from `stdin`: `readline()`
|
- Reading from `stdin`: `readline()`
|
||||||
|
|
||||||
|
|
||||||
### Input
|
### Input
|
||||||
|
|
||||||
The language _Python_ provides a function `input()`:
|
The _Python_ language provides an `input()` function:
|
||||||
```{.python}
|
```{.python}
|
||||||
ans = input("Please enter a positive number!")
|
ans = input("Please enter a positive number!")
|
||||||
```
|
```
|
||||||
The function prints the prompt, waits for input, and returns the
|
It prints the prompt, waits for input, and returns a `string`.
|
||||||
input as a `string`.
|
|
||||||
|
|
||||||
|
|
||||||
In Julia, you can implement this function as follows:
|
In Julia, you can implement this function as follows:
|
||||||
@@ -76,12 +75,12 @@ end
|
|||||||
|
|
||||||
**Comments**
|
**Comments**
|
||||||
|
|
||||||
- Write instructions are buffered by modern operating systems. With `flush(stdout)`, the buffer is emptied and the write operation is forced to complete immediately.
|
- Write operations are buffered by modern operating systems. `flush(stdout)` empties the buffer and forces the write operation to complete immediately.
|
||||||
- `readline()` returns a string ending with a newline `\n`. The function `chomp()` removes a possible line break from the end of a string.
|
- `readline()` returns a string ending with a newline (`\n`). The function `chomp()` removes a trailing line break from the string.
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
#| eval: false
|
#| eval: false
|
||||||
a = input("Please enter 2 numbers!")
|
a = input("Please enter two numbers!")
|
||||||
```
|
```
|
||||||
```{julia}
|
```{julia}
|
||||||
#| echo: false
|
#| echo: false
|
||||||
@@ -91,7 +90,7 @@ a = "34 56"
|
|||||||
|
|
||||||
### Processing the Input
|
### Processing the Input
|
||||||
|
|
||||||
> `split(str)` splits a string into "words" and returns an _(array of strings)_:
|
> `split(str)` splits a string into "words", returning a string array:
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
av = split(a)
|
av = split(a)
|
||||||
@@ -105,46 +104,33 @@ av = split(a)
|
|||||||
v = parse.(Int, av)
|
v = parse.(Int, av)
|
||||||
```
|
```
|
||||||
|
|
||||||
`parse()` generates an error if the string cannot be parsed as a value of type `T`. You can catch the error with
|
`parse()` throws an error if the string cannot be parsed as type `T`. You can catch the error with
|
||||||
`try/catch` or use the function `tryparse(T, str)`, which returns `nothing` in such a case - on which you can then
|
`try/catch`, or use `tryparse(T, str)`, which returns `nothing` in such cases. Test the result with `isnothing()`.
|
||||||
e.g. test with `isnothing()`.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### Reading Individual Keystrokes
|
|
||||||
|
|
||||||
- `readline()` and similar functions wait for the input to be completed by pressing the `Enter` key.
|
|
||||||
- Techniques for reading individual _keystrokes_ can be found here:
|
|
||||||
|
|
||||||
- [https://stackoverflow.com/questions/56888266/how-to-read-keyboard-inputs-at-every-keystroke-in-julia](https://stackoverflow.com/questions/56888266/how-to-read-keyboard-inputs-at-every-keystroke-in-julia)
|
|
||||||
- [https://stackoverflow.com/questions/60954235/how-can-i-test-whether-stdin-has-input-available-in-julia](https://stackoverflow.com/questions/60954235/how-can-i-test-whether-stdin-has-input-available-in-julia)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Formatted Output with the `Printf` Macro
|
## Formatted Output with the `Printf` Macro
|
||||||
|
|
||||||
Often you want to output numbers or strings with a strict format specification - total length, decimal places, right/left-aligned, etc.
|
You often need to output numbers or strings with strict formatting: total length, decimal places, alignment, etc.
|
||||||
|
|
||||||
To this end, the `Printf` package defines the macros `@sprintf` and `@printf`, which work very similarly to the corresponding C functions.
|
For this purpose, the `Printf` package defines the macros `@sprintf` and `@printf`, which work similarly to the corresponding C functions.
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
using Printf
|
using Printf
|
||||||
|
|
||||||
x = 123.7876355638734
|
x = 123.7876355638734
|
||||||
|
|
||||||
@printf("Output right-aligned with max. 10 character width and 3 decimal places: x= %10.3f", x)
|
@printf("Output right-aligned with max. 10 character width and 3 decimal places: x = %10.3f", x)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
The first argument is a string containing placeholders (here: `%10.3`) for variables to be output; followed by these variables as further arguments.
|
The first argument is a string containing placeholders (here: `%10.3f`) for the variables, followed by the variables themselves.
|
||||||
|
|
||||||
Placeholders have the form
|
Placeholders have the form:
|
||||||
```
|
```
|
||||||
%[flags][width][.precision]type
|
%[flags][width][.precision]type
|
||||||
```
|
```
|
||||||
where the entries in square brackets are all optional.
|
where entries in square brackets are optional.
|
||||||
|
|
||||||
**Type specifications in placeholders**
|
**Type specifications in placeholders**
|
||||||
|
|
||||||
@@ -152,11 +138,11 @@ where the entries in square brackets are all optional.
|
|||||||
|:--|:------------|
|
|:--|:------------|
|
||||||
|`%s`| `string`|
|
|`%s`| `string`|
|
||||||
|`%i`| `integer`|
|
|`%i`| `integer`|
|
||||||
|`%o`| `integer octal (base=8)`|
|
|`%o`| `integer, octal (base 8)`|
|
||||||
|`%x, %X`| `integer hexadecimal (base=16) with digits 0-9abcdef or 0-9ABCDEF, resp.`|
|
|`%x, %X`| `integer, hexadecimal (base 16), digits 0-9a-f or 0-9A-F`|
|
||||||
|`%f`| `floating point number`|
|
|`%f`| `floating point`|
|
||||||
|`%e`| `floating point number, scientific representation`|
|
|`%e`| `floating point, scientific notation`|
|
||||||
|`%g`| `floating point, uses %f or %e depending on value`|
|
|`%g`| `floating point, %f or %e as appropriate`|
|
||||||
|
|
||||||
: {.striped .hover}
|
: {.striped .hover}
|
||||||
|
|
||||||
@@ -165,9 +151,9 @@ where the entries in square brackets are all optional.
|
|||||||
|
|
||||||
| | |
|
| | |
|
||||||
|:----|:-----|
|
|:----|:-----|
|
||||||
|Plus sign| right-aligned (default)|
|
|Plus sign| right-aligned (default) |
|
||||||
|Minus sign| left-aligned|
|
|Minus sign| left-aligned |
|
||||||
|Zero| with leading zeros|
|
|Zero| adds leading zeros |
|
||||||
|
|
||||||
: {.striped .hover}
|
: {.striped .hover}
|
||||||
|
|
||||||
@@ -175,22 +161,22 @@ where the entries in square brackets are all optional.
|
|||||||
**Width**
|
**Width**
|
||||||
|
|
||||||
```
|
```
|
||||||
Number of minimum characters used (more will be taken if necessary)
|
Minimum number of characters used (more will be taken if necessary)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
### Examples:
|
### Examples
|
||||||
|
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
using Printf # Don't forget to load the package!
|
using Printf # Load the package first
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
@printf("|%s|", "Hello") # string with placeholder for string
|
@printf("|%s|", "Hello") # string with placeholder for string
|
||||||
```
|
```
|
||||||
The vertical bars are not part of the placeholder. They are intended to indicate the boundaries of the output field.
|
The vertical bars are not part of the placeholder; they indicate the output field boundaries.
|
||||||
|
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
@@ -205,7 +191,7 @@ The vertical bars are not part of the placeholder. They are intended to indicate
|
|||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
@printf("|%3s|", "Hello") # Length specification can be exceeded
|
@printf("|%3s|", "Hello") # Length specification can be exceeded
|
||||||
# Better a 'badly formatted' table than incorrect values!
|
# Better a badly formatted table than wrong values!
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
@@ -214,36 +200,34 @@ j = 123
|
|||||||
k = 90019001
|
k = 90019001
|
||||||
l = 3342678
|
l = 3342678
|
||||||
|
|
||||||
@printf("j= %012i, k= %-12i, l = %12i", j, k, l) # 0-flag for leading zeros
|
@printf("j = %012i, k = %-12i, l = %12i", j, k, l) # 0-flag for leading zeros
|
||||||
```
|
```
|
||||||
|
|
||||||
`@printf` and `@sprintf` can be called like functions or as macros:
|
`@printf` and `@sprintf` can be called like functions:
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
@printf("%i %i", 22, j)
|
@printf("%i %i", 22, j)
|
||||||
```
|
```
|
||||||
|
|
||||||
-- or as macros, i.e., without function parentheses and without comma:
|
or as macros, i.e., without parentheses or commas:
|
||||||
|
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
@printf "%i %i" 22 j
|
@printf "%i %i" 22 j
|
||||||
```
|
```
|
||||||
|
|
||||||
`@printf` can take a stream as its first argument.
|
`@printf` can take a stream as its first argument; otherwise, the argument list consists of:
|
||||||
|
|
||||||
Otherwise, the argument list consists of
|
|
||||||
|
|
||||||
- format string with placeholders
|
- format string with placeholders
|
||||||
- variables in the order of the placeholders, matching in number and type to the placeholders
|
- variables matching the placeholders in number and type
|
||||||
|
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
@printf(stderr, "First result: %i %s\nSecond result %i",
|
@printf(stderr, "First result: %i %s\nSecond result %i",
|
||||||
j, "(estimated)" ,k)
|
j, "(estimated)", k)
|
||||||
```
|
```
|
||||||
|
|
||||||
The macro `@sprintf` does not print anything but returns the filled formatted string:
|
The macro `@sprintf` does not print; it returns the formatted string:
|
||||||
|
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
@@ -255,12 +239,12 @@ str = @sprintf("x = %10.6f", π );
|
|||||||
str
|
str
|
||||||
```
|
```
|
||||||
|
|
||||||
### Formatting Floating Point Numbers:
|
### Formatting Floating-Point Numbers
|
||||||
|
|
||||||
Meaning of the _precision_ value:
|
The _precision_ value specifies:
|
||||||
|
|
||||||
- `%f` and `%e` format: maximum number of decimal places
|
- `%f` and `%e` formats: maximum decimal places
|
||||||
- `%g` format: maximum number of digits output (integer + decimal places)
|
- `%g` format: maximum total digits (integer part + decimal places)
|
||||||
|
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
@@ -276,34 +260,33 @@ x = 123456.7890123456
|
|||||||
|
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
@printf("%20.7g %20.4g", x, x) # total 7 and 4 digits respectively
|
@printf("%20.7g %20.4g", x, x) # 7 and 4 digits total, respectively
|
||||||
```
|
```
|
||||||
|
|
||||||
## File Operations
|
## File Operations
|
||||||
|
|
||||||
Files are
|
Files are handled by:
|
||||||
|
|
||||||
- opened $\Longrightarrow$ a new _stream_-object is created (in addition to `stdin, stdout, stderr`)
|
- Opening $\Longrightarrow$ creation of a new _stream_ object (in addition to `stdin`, `stdout`, `stderr`)
|
||||||
- then this _stream_ can be read from and written to
|
- Reading from and writing to this _stream_
|
||||||
- closed $\Longrightarrow$ _stream_-object is detached from file
|
- Closing $\Longrightarrow$ detachment of the _stream_ object from the file
|
||||||
|
|
||||||
```{.julia}
|
```
|
||||||
stream = open(path, mode)
|
stream = open(path, mode)
|
||||||
```
|
```
|
||||||
|
|
||||||
- path: filename/path
|
- path: filename or path
|
||||||
- mode:
|
- mode:
|
||||||
|
```
|
||||||
```
|
"r" read, opens at file beginning
|
||||||
"r" read, opens at file beginning
|
"w" write, opens at file beginning (file is created or overwritten)
|
||||||
"w" write, opens at file beginning (file is created or overwritten)
|
"a" append, opens to continue writing at file end
|
||||||
"a" append, opens to continue writing at file end
|
```
|
||||||
```
|
|
||||||
|
|
||||||
Let's write a file:
|
Let's write a file:
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
file = open("datei.txt", "w")
|
file = open("myfile.txt", "w")
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
@@ -324,18 +307,18 @@ close(file)
|
|||||||
Let's look at the file:
|
Let's look at the file:
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
;cat datei.txt
|
;cat myfile.txt
|
||||||
```
|
```
|
||||||
|
|
||||||
...and now we open it again for reading:
|
...and now we open it again for reading:
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
stream = open("datei.txt", "r")
|
stream = open("myfile.txt", "r")
|
||||||
```
|
```
|
||||||
|
|
||||||
`readlines(stream)` returns all lines of a text file as a vector of strings.
|
`readlines(stream)` returns all lines of a text file as a string vector.
|
||||||
|
|
||||||
`eachline(stream)` returns an iterator over the lines of the file.
|
`eachline(stream)` returns an iterator over the file lines.
|
||||||
|
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
@@ -349,30 +332,30 @@ close(stream)
|
|||||||
|
|
||||||
## Packages for File Formats
|
## Packages for File Formats
|
||||||
|
|
||||||
For input and output in various file formats, there are Julia packages, e.g.,
|
Julia packages for various file formats include:
|
||||||
|
|
||||||
- [PrettyTables.jl](https://ronisbr.github.io/PrettyTables.jl/stable/) Output of formatted tables
|
- [PrettyTables.jl](https://ronisbr.github.io/PrettyTables.jl/stable/) Output formatted tables
|
||||||
- [DelimitedFiles.jl](https://docs.julialang.org/en/v1/stdlib/DelimitedFiles/) Input and output of matrices, etc.
|
- [DelimitedFiles.jl](https://docs.julialang.org/en/v1/stdlib/DelimitedFiles/) Read and write matrices
|
||||||
- [CSV.jl](https://csv.juliadata.org/stable/) Input and output of "comma-separated values" files, etc.
|
- [CSV.jl](https://csv.juliadata.org/stable/) Read and write CSV files
|
||||||
- [XLSX.jl](https://felipenoris.github.io/XLSX.jl/stable/tutorial/) Input and output of Excel files
|
- [XLSX.jl](https://felipenoris.github.io/XLSX.jl/stable/tutorial/) Read and write Excel files
|
||||||
|
|
||||||
and many more...
|
and many more...
|
||||||
|
|
||||||
### DelimitedFiles.jl
|
### DelimitedFiles.jl
|
||||||
|
|
||||||
This package enables convenient saving/reading of matrices. It provides the functions `writedlm()` and `readdlm()`.
|
This package offers convenient functions for saving and reading matrices using `writedlm()` and `readdlm()`.
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
using DelimitedFiles
|
using DelimitedFiles
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
We generate a 200×3 matrix of random numbers
|
Generate a 200×3 matrix of random numbers:
|
||||||
```{julia}
|
```{julia}
|
||||||
A = rand(200,3)
|
A = rand(200,3)
|
||||||
```
|
```
|
||||||
|
|
||||||
and save it
|
and save it:
|
||||||
```{julia}
|
```{julia}
|
||||||
f = open("data2.txt", "w")
|
f = open("data2.txt", "w")
|
||||||
writedlm(f, A)
|
writedlm(f, A)
|
||||||
@@ -387,33 +370,30 @@ The written file starts like this:
|
|||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
Reading it back is even simpler:
|
Reading it back is simple:
|
||||||
```{julia}
|
```{julia}
|
||||||
B = readdlm("data2.txt")
|
B = readdlm("data2.txt")
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
One more point: In Julia, the `do` notation is often used for file handling, see @sec-do.
|
|
||||||
This uses the fact that `open()` also has methods where the 1st argument is a `function(iostream)`.
|
In Julia, the `do` notation is frequently utilized for file handling (see @sec-do). The `open()` function includes methods where the first argument is a `function(iostream)`. This function is applied to the stream, which is automatically closed afterward. The `do` notation allows to define this function anonymously:
|
||||||
This function is then applied to the _stream_ and the stream is automatically closed at the end. The `do` notation allows you to
|
|
||||||
define this function anonymously after the `do`:
|
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
open("data2.txt", "w") do io
|
|
||||||
writedlm(io, A)
|
writedlm(io, A)
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
### CSV and DataFrames
|
### CSV and DataFrames
|
||||||
|
|
||||||
- The CSV format is often used to provide tables in a form that can be read not only by MS Excel.
|
- The CSV format provides tables readable by MS Excel and other applications.
|
||||||
- An example is the weather and climate database _Meteostat_.
|
- Example: the weather and climate database _Meteostat_.
|
||||||
- The [DataFrames.jl](https://dataframes.juliadata.org/stable/) package provides functions for convenient handling of tabular data.
|
- The [DataFrames.jl](https://dataframes.juliadata.org/stable/) package handles tabular data conveniently.
|
||||||
|
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
using CSV, DataFrames, Downloads
|
using CSV, DataFrames, Downloads
|
||||||
# Weather data from Westerland, see https://dev.meteostat.net/bulk/hourly.html
|
# Weather data from Westerland (see https://dev.meteostat.net/bulk/hourly.html)
|
||||||
|
|
||||||
url = "https://bulk.meteostat.net/v2/hourly/10018.csv.gz"
|
url = "https://bulk.meteostat.net/v2/hourly/10018.csv.gz"
|
||||||
http_response = Downloads.download(url)
|
http_response = Downloads.download(url)
|
||||||
@@ -452,8 +432,7 @@ describe(df)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
For convenient plotting and handling of date and time formats in the weather table,
|
For convenient plotting and date/time handling, we load two packages:
|
||||||
we load two helper packages:
|
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
using StatsPlots, Dates
|
using StatsPlots, Dates
|
||||||
@@ -480,7 +459,7 @@ df[!, :datetime] = DateTime.(df.Column1) .+ Hour.(df.Column2);
|
|||||||
@df df plot(:datetime, :Column3)
|
@df df plot(:datetime, :Column3)
|
||||||
```
|
```
|
||||||
|
|
||||||
And now to the plot:
|
The resulting plot:
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
@df df plot(:datetime, [:Column9, :Column6, :Column3],
|
@df df plot(:datetime, [:Column9, :Column6, :Column3],
|
||||||
|
|||||||
@@ -12,25 +12,25 @@ using InteractiveUtils
|
|||||||
|
|
||||||
# Plots and Data Visualization in Julia: _Plots.jl_
|
# Plots and Data Visualization in Julia: _Plots.jl_
|
||||||
|
|
||||||
There are numerous graphics packages for Julia. Two frequently used ones are [Makie.jl](https://docs.makie.org/stable/) and
|
Julia has numerous graphics packages. Two frequently used ones are [Makie.jl](https://docs.makie.org/stable/) and
|
||||||
[Plots.jl](https://docs.juliaplots.org/latest/). Before presenting these in more detail, some other packages are listed.
|
[Plots.jl](https://docs.juliaplots.org/latest/). Before presenting `Plots.jl` in detail, we list some others.
|
||||||
|
|
||||||
## Brief Overview: Some Graphics Packages
|
## Brief Overview: Some Graphics Packages
|
||||||
|
|
||||||
| Package/Documentation | Tutorial | Examples | Remarks |
|
| Package/Documentation | Tutorial | Examples | Remarks |
|
||||||
|:----|:--|:--|:--------|
|
|:----|:--|:--|:--------|
|
||||||
|[Plots.jl](https://docs.juliaplots.org/latest/) | [Tutorial](https://docs.juliaplots.org/latest/tutorial/) | [Gallery](https://goropikari.github.io/PlotsGallery.jl/) | designed as a unified interface to various _backends_ (graphics libraries) |
|
|[Plots.jl](https://docs.juliaplots.org/latest/) | [Tutorial](https://docs.juliaplots.org/latest/tutorial/) | [Gallery](https://docs.juliaplots.org/latest/gallery/gr/) | designed as a unified interface to various _backends_ (graphics libraries) |
|
||||||
| [Makie.jl](https://docs.makie.org/stable/) | [Basic tutorial](https://docs.makie.org/v0.21/tutorials/basic-tutorial) | [Beautiful Makie](https://beautiful.makie.org/) | "data visualization ecosystem for Julia", backends: Cairo (vector graphics), OpenGL, WebGL |
|
| [Makie.jl](https://docs.makie.org/stable/) | [Basic tutorial](https://docs.makie.org/v0.21/tutorials/basic-tutorial) | [Beautiful Makie](https://beautiful.makie.org/) | "data visualization ecosystem for Julia", backends: Cairo (vector graphics), OpenGL, WebGL |
|
||||||
|[PlotlyJS.jl](http://juliaplots.org/PlotlyJS.jl/stable/) | [Getting started](https://plotly.com/julia/getting-started/)| [Examples](https://plotly.com/julia/plotly-fundamentals/)| Interface to the [Plotly](https://plotly.com/graphing-libraries/) JavaScript graphics library |
|
|[PlotlyJS.jl](http://juliaplots.org/PlotlyJS.jl/stable/) | [Getting started](https://plotly.com/julia/getting-started/)| [Examples](https://plotly.com/julia/plotly-fundamentals/)| Interface to the [Plotly](https://plotly.com/graphing-libraries/) JavaScript graphics library |
|
||||||
| [Gadfly.jl](https://gadflyjl.org/stable/)| [Tutorial](https://gadflyjl.org/stable/tutorial/) | [Gallery](https://github.com/GiovineItalia/Gadfly.jl?tab=readme-ov-file#gallery)| "a plotting and data visualization system written in Julia, influenced by R's [ggplot2](https://ggplot2.tidyverse.org/)" |
|
| [Gadfly.jl](https://gadflyjl.org/stable/)| [Tutorial](https://gadflyjl.org/stable/tutorial/) | [Gallery](https://github.com/GiovineItalia/Gadfly.jl?tab=readme-ov-file#gallery)| "a plotting and data visualization system written in Julia, influenced by R's [ggplot2](https://ggplot2.tidyverse.org/)" |
|
||||||
| [Bokeh.jl](https://cjdoris.github.io/Bokeh.jl/stable/) | | [Gallery](https://cjdoris.github.io/Bokeh.jl/stable/gallery/)| Julia frontend for [Bokeh](https://bokeh.org/) |
|
| [Bokeh.jl](https://cjdoris.github.io/Bokeh.jl/stable/) | | [Gallery](https://cjdoris.github.io/Bokeh.jl/stable/gallery/)| Julia frontend for [Bokeh](https://bokeh.org/) |
|
||||||
|[VegaLite.jl](https://www.queryverse.org/VegaLite.jl/stable/) | [Tutorial](https://www.queryverse.org/VegaLite.jl/stable/gettingstarted/tutorial/)| [Examples](https://www.queryverse.org/VegaLite.jl/stable/examples/examples_barcharts/)| Julia frontend for [Vega-Lite](https://vega.github.io/vega-lite/)|
|
|[VegaLite.jl](https://www.queryverse.org/VegaLite.jl/stable/) | [Tutorial](https://www.queryverse.org/VegaLite.jl/stable/gettingstarted/tutorial/)| [Examples](https://www.queryverse.org/VegaLite.jl/stable/examples/examples_barcharts/)| Julia frontend for [Vega-Lite](https://vega.github.io/vega-lite/)|
|
||||||
| [Luxor.jl](http://juliagraphics.github.io/Luxor.jl/stable/) |[Tutorial](https://juliagraphics.github.io/Luxor.jl/stable/tutorial/helloworld/)|[Examples](https://juliagraphics.github.io/Luxor.jl/stable/example/moreexamples/)| General vector graphics/illustrations |
|
| [Luxor.jl](https://juliagraphics.github.io/LuxorManual/stable/) |[Tutorial](https://juliagraphics.github.io/LuxorManual/stable/tutorial/helloworld/)|[Examples](https://juliagraphics.github.io/LuxorManual/stable/example/moreexamples/)| General vector graphics/illustrations |
|
||||||
| [Javis.jl](https://juliaanimators.github.io/Javis.jl/stable/) |[Tutorials](https://juliaanimators.github.io/Javis.jl/stable/tutorials/)| [Examples](https://juliaanimators.github.io/Javis.jl/stable/examples/)| *Animated* vector graphics
|
| [Javis.jl](https://juliaanimators.github.io/Javis.jl/stable/) |[Tutorials](https://juliaanimators.github.io/Javis.jl/stable/tutorials/)| [Examples](https://juliaanimators.github.io/Javis.jl/stable/examples/)| *Animated* vector graphics
|
||||||
| [TidierPlots.jl](https://github.com/TidierOrg/TidierPlots.jl)| [Reference](https://tidierorg.github.io/TidierPlots.jl/latest/) || "is a 100% Julia implementation of the R package ggplot2 powered by Makie.jl"|
|
| [TidierPlots.jl](https://github.com/TidierOrg/TidierPlots.jl)| [Reference](https://tidierorg.github.io/TidierPlots.jl/latest/) || "is a 100% Julia implementation of the R package ggplot2 powered by Makie.jl"|
|
||||||
|[PythonPlot.jl](https://github.com/JuliaPy/PythonPlot.jl)| |[Examples (in Python)](https://matplotlib.org/stable/gallery/index.html)| Interface to Matplotlib (Python), 1:1 transfer of the Python API, therefore see [Matplotlib documentation](https://matplotlib.org/stable/api/pyplot_summary.html)
|
|[PythonPlot.jl](https://github.com/JuliaPy/PythonPlot.jl)| |[Examples (in Python)](https://matplotlib.org/stable/gallery/index.html)| Interface to Matplotlib (Python), 1:1 transfer of the Python API, therefore see [Matplotlib documentation](https://matplotlib.org/stable/api/pyplot_summary.html)
|
||||||
|
|
||||||
{: .striped .hover}
|
: {.striped .hover}
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
| [PyPlot.jl](https://github.com/JuliaPy/PyPlot.jl) | | [Examples](https://gist.github.com/gizmaa/7214002)| Interface to Matplotlib (Python), 1:1 transfer of the Python API, therefore see [Matplotlib documentation](https://matplotlib.org/stable/) |
|
| [PyPlot.jl](https://github.com/JuliaPy/PyPlot.jl) | | [Examples](https://gist.github.com/gizmaa/7214002)| Interface to Matplotlib (Python), 1:1 transfer of the Python API, therefore see [Matplotlib documentation](https://matplotlib.org/stable/) |
|
||||||
@@ -44,7 +44,7 @@ The `plot()` function expects, in the simplest case:
|
|||||||
|
|
||||||
- as the first argument a vector of $x$-values of length $n$ and
|
- as the first argument a vector of $x$-values of length $n$ and
|
||||||
- as the second argument a vector of the same length with the corresponding $y$-values.
|
- as the second argument a vector of the same length with the corresponding $y$-values.
|
||||||
- The second argument can also be an $n\times m$-matrix. Then each column vector is regarded as a separate graph (called `series` in the documentation) and $m$ curves are plotted:
|
- The second argument can also be an $n\times m$ matrix. Each column is treated as a separate graph (called a `series` in the documentation), plotting $m$ curves:
|
||||||
|
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
@@ -57,8 +57,8 @@ cx = @. cos(2x^(1/2))
|
|||||||
plot(x, [sx cx])
|
plot(x, [sx cx])
|
||||||
```
|
```
|
||||||
|
|
||||||
- The functions of the _Plots.jl_ package such as `plot(), scatter(), contour(), heatmap(), histogram(), bar(),...` etc. all start a new plot.
|
- Functions like `plot()`, `scatter()`, `contour()`, `heatmap()`, `histogram()`, `bar()`, etc. from _Plots.jl_ all start a new plot.
|
||||||
- The versions `plot!(), scatter!(), contour!(), heatmap!(), histogram!(), bar!(),...` extend an existing plot:
|
- The versions `plot!()`, `scatter!()`, `contour!()`, `heatmap!()`, `histogram!()`, `bar!()`, etc. extend an existing plot:
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
plot(x, sx) # plot only sin(x)
|
plot(x, sx) # plot only sin(x)
|
||||||
@@ -74,7 +74,7 @@ plot1a = deepcopy(plot1) # plot objects are quite deep structures
|
|||||||
scatter!(plot1, x, sx) # add scatter plot, i.e. unconnected data points
|
scatter!(plot1, x, sx) # add scatter plot, i.e. unconnected data points
|
||||||
```
|
```
|
||||||
|
|
||||||
The copied version `plot1a` has not been modified by the `scatter!` statement and can be used independently:
|
The copied version `plot1a` remains unchanged by the `scatter!` call and can be used independently:
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
plot!(plot1a, x, 2 .* sx)
|
plot!(plot1a, x, 2 .* sx)
|
||||||
@@ -82,7 +82,7 @@ plot!(plot1a, x, 2 .* sx)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
Plot objects can be saved as graphics files (PDF, SVG, PNG,...):
|
Plot objects can be saved as graphics files (PDF, SVG, PNG, etc.):
|
||||||
|
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
@@ -111,7 +111,7 @@ plot(f, 0:0.01:3)
|
|||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
The parametric form $x = x(t),\ y = y(t)$ can be drawn by passing two functions and a vector of $t$-values to `plot()`.
|
The parametric form $x = x(t),\ y = y(t)$ is plotted by passing two functions and a vector of $t$-values to `plot()`.
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
# https://en.wikipedia.org/wiki/Butterfly_curve_(transcendental)
|
# https://en.wikipedia.org/wiki/Butterfly_curve_(transcendental)
|
||||||
@@ -125,10 +125,9 @@ plot(xt, yt, 0:0.01:12π)
|
|||||||
|
|
||||||
### Plot Themes
|
### Plot Themes
|
||||||
|
|
||||||
> "PlotThemes is a package to spice up the plots made with Plots.jl."\
|
> "PlotThemes is a package to spice up plots made with Plots.jl."\
|
||||||
Here is the illustrated [list of themes](https://docs.juliaplots.org/stable/generated/plotthemes/)
|
See the illustrated [list of themes](https://docs.juliaplots.org/stable/generated/plotthemes/)
|
||||||
|
|
||||||
or:
|
|
||||||
```{julia}
|
```{julia}
|
||||||
using PlotThemes
|
using PlotThemes
|
||||||
|
|
||||||
@@ -151,8 +150,8 @@ plot(x, [sx cx 1 ./ (1 .+ x)])
|
|||||||
|
|
||||||
### Plot Attributes
|
### Plot Attributes
|
||||||
|
|
||||||
The functions of the `Plots.jl` package have a large number of options.
|
The `Plots.jl` functions have numerous options.
|
||||||
`Plots.jl` divides the attributes into 4 groups:
|
Attributes are divided into 4 groups:
|
||||||
|
|
||||||
::::{.cell}
|
::::{.cell}
|
||||||
```{julia}
|
```{julia}
|
||||||
@@ -182,7 +181,7 @@ plotattr(:Series) # attributes for a series, e.g., a line in the plot
|
|||||||
```
|
```
|
||||||
::::
|
::::
|
||||||
|
|
||||||
One can also ask what the individual attributes mean and which values are allowed:
|
You can also query what individual attributes mean and which values are allowed:
|
||||||
```{julia}
|
```{julia}
|
||||||
plotattr("linestyle")
|
plotattr("linestyle")
|
||||||
```
|
```
|
||||||
@@ -200,7 +199,7 @@ plot(x, y, seriestype = :sticks, linewidth = 4, seriescolor = "#00b300",
|
|||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
Many specifications can also be abbreviated significantly, see e.g. the `Aliases:` in the above output of the command `plotattr("linestyle")`.
|
Many specifications can be abbreviated significantly; see, e.g., the `Aliases:` in the output of `plotattr("linestyle")`.
|
||||||
|
|
||||||
The following `plot()` command is equivalent to the previous one:
|
The following `plot()` command is equivalent to the previous one:
|
||||||
|
|
||||||
@@ -213,13 +212,13 @@ plot(x, y, t = :sticks, w = 4, c = "#00b300", m = (:circle, 8, :green ))
|
|||||||
### Additional Extras
|
### Additional Extras
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
using Plots # repetition does not hurt
|
using Plots # no harm in repeating
|
||||||
using Plots.PlotMeasures # for measurements in mm, cm,...
|
using Plots.PlotMeasures # for measurements in mm, cm,...
|
||||||
using LaTeXStrings # for LaTeX constructs in plot labels
|
using LaTeXStrings # for LaTeX constructs in plot labels
|
||||||
using PlotThemes # predefined themes
|
using PlotThemes # predefined themes
|
||||||
```
|
```
|
||||||
|
|
||||||
The `LaTeXStrings.jl` package provides a string constructor `L"..."`. These strings can contain LaTeX constructs, especially formulas. If the string does not contain explicit dollar signs, it is automatically interpreted in LaTeX math mode.
|
The `LaTeXStrings.jl` package provides the `L"..."` string constructor. These strings can contain LaTeX constructs, especially formulas. Without explicit dollar signs, they are automatically interpreted in LaTeX math mode.
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
xs = range(0, 2π, length = 100)
|
xs = range(0, 2π, length = 100)
|
||||||
@@ -232,9 +231,9 @@ plot10 = plot(xs, data,
|
|||||||
fontfamily="Computer Modern",
|
fontfamily="Computer Modern",
|
||||||
|
|
||||||
# LaTeX string L"..."
|
# LaTeX string L"..."
|
||||||
title = L"Winkelfunktionen $\sin(\alpha), \cos(\alpha), 2\sin(\alpha), \sin(\alpha^2)$",
|
title = L"Trigonometric functions $\sin(\alpha), \cos(\alpha), 2\sin(\alpha), \sin(\alpha^2)$",
|
||||||
xlabel = L"Winkel $\alpha$",
|
xlabel = L"angle $\alpha$",
|
||||||
ylabel = "Funktionswert",
|
ylabel = "value",
|
||||||
|
|
||||||
# 1x4-matrices with colors, markers,... for the 4 'series'
|
# 1x4-matrices with colors, markers,... for the 4 'series'
|
||||||
color=[:black :green RGB(0.3, 0.8, 0.2) :blue ],
|
color=[:black :green RGB(0.3, 0.8, 0.2) :blue ],
|
||||||
@@ -255,14 +254,14 @@ plot10 = plot(xs, data,
|
|||||||
top_margin = 5mm, # here Plots.PlotMeasures is needed
|
top_margin = 5mm, # here Plots.PlotMeasures is needed
|
||||||
)
|
)
|
||||||
|
|
||||||
# additional text: annotate!(x-pos, y-pos, text("...", font, fontsize))
|
# additional text: annotate!(x-pos, y-pos, text("...", font, fontsize))
|
||||||
|
|
||||||
annotate!(plot10, 4.1, 1.8, text("nicht schön, aber viel","Computer Modern", 10) )
|
annotate!(plot10, 4.1, 1.8, text("Some remark","Computer Modern", 10) )
|
||||||
```
|
```
|
||||||
|
|
||||||
### Other Plot Functions
|
### Other Plot Functions
|
||||||
|
|
||||||
So far, we have plotted mainly lines. There are many other types such as _scatter plot, contour, heatmap, histogram, stick,..._
|
So far, we have mainly plotted lines. Many other types exist, such as _scatter plots, contours, heatmaps, histograms, sticks_, etc.
|
||||||
|
|
||||||
This can be controlled with the `seriestype` attribute:
|
This can be controlled with the `seriestype` attribute:
|
||||||
|
|
||||||
@@ -283,7 +282,7 @@ scatter(x, sin.(x))
|
|||||||
|
|
||||||
### Subplots and Layout {#sec-subplot}
|
### Subplots and Layout {#sec-subplot}
|
||||||
|
|
||||||
Multiple plots can be combined into one figure. The arrangement is determined by the `layout` parameter. `layout=(m,n)` means that the plots are arranged in an $m\times n$ scheme:
|
Multiple plots can be combined into one figure. The arrangement is determined by the `layout` parameter: `layout=(m,n)` arranges plots in an $m\times n$ grid:
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
x = range(0, 2π; length = 100)
|
x = range(0, 2π; length = 100)
|
||||||
@@ -317,11 +316,11 @@ plot(plots..., layout=mylayout, legend=false, title=["sin" "cos" "tan" "sinc"])
|
|||||||
|
|
||||||
### Backends
|
### Backends
|
||||||
|
|
||||||
`Plots.jl` is designed as a unified interface to various _backends_ (graphics engines). One can switch to another backend and use the same plot commands and attributes.
|
`Plots.jl` provides a unified interface to various _backends_ (graphics engines). You can switch backends and use the same plot commands and attributes.
|
||||||
|
|
||||||
However, not all _backends_ support all plot types and attributes. An overview is available [here](https://docs.juliaplots.org/stable/generated/supported/).
|
However, not all _backends_ support all plot types and attributes. An overview is available [here](https://docs.juliaplots.org/stable/generated/supported/).
|
||||||
|
|
||||||
So far, the default backend has been used. It is called [GR](https://gr-framework.org/about.html) and is a graphics engine developed at the Jülich Research Center and written primarily in C.
|
So far, we have used the default backend: [GR](https://gr-framework.org/about.html), a graphics engine developed at the Jülich Research Center, primarily in C.
|
||||||
|
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
@@ -330,22 +329,22 @@ backend() # display the selected backend, GR is the default
|
|||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
Another example
|
Another example:
|
||||||
```{julia}
|
```{julia}
|
||||||
x = 1:30
|
x = 1:30
|
||||||
y = rand(30)
|
y = rand(30)
|
||||||
plot(x, y, linecolor =:green, bg_inside =:lightblue1, line =:solid, label = "Wasserstand")
|
plot(x, y, linecolor =:green, bg_inside =:lightblue1, line =:solid, label = "Water level")
|
||||||
```
|
```
|
||||||
|
|
||||||
and here the same plot with the `PlotlyJS` backend.
|
The same plot with the `PlotlyJS` backend:
|
||||||
|
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
plotlyjs() # change plots backend
|
plotlyjs() # change plots backend
|
||||||
plot(x, y, linecolor =:green, bg_inside =:lightblue1, line =:solid, label = "Wasserstand")
|
plot(x, y, linecolor =:green, bg_inside =:lightblue1, line =:solid, label = "Water level")
|
||||||
```
|
```
|
||||||
|
|
||||||
This backend enables a certain interactivity using JavaScript. When moving the mouse into the image, one can zoom and pan with the mouse, and 3D plots can also be rotated.
|
This backend enables interactivity via JavaScript. Hovering over the image allows zooming and panning; 3D plots can also be rotated.
|
||||||
|
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
@@ -356,7 +355,7 @@ gr() # return to GR as backend
|
|||||||
|
|
||||||
### 3D Plots
|
### 3D Plots
|
||||||
|
|
||||||
The functions `surface()` and `contour()` allow plotting of a function $f(x,y)$. The required arguments are:
|
The `surface()` and `contour()` functions plot a function $f(x,y)$. Required arguments are:
|
||||||
|
|
||||||
- a set (vector) $X$ of $x$-values,
|
- a set (vector) $X$ of $x$-values,
|
||||||
- a set (vector) $Y$ of $y$-values and
|
- a set (vector) $Y$ of $y$-values and
|
||||||
@@ -372,8 +371,8 @@ surface( -3:0.02:3, -3:0.02:3, f)
|
|||||||
contour( -3:0.02:3, -3:0.02:3, f, fill=true, colormap=:summer, levels=20, contour_labels=false)
|
contour( -3:0.02:3, -3:0.02:3, f, fill=true, colormap=:summer, levels=20, contour_labels=false)
|
||||||
```
|
```
|
||||||
|
|
||||||
Curves (or simply point sets) in three dimensions can be plotted by calling `plot()` with 3 vectors
|
Curves (or point sets) in three dimensions are plotted by calling `plot()` with three vectors
|
||||||
containing the $x$, $y$ and $z$-coordinates of the data points, respectively.
|
containing the $x$, $y$, and $z$ coordinates of the data points.
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
plotlyjs()
|
plotlyjs()
|
||||||
@@ -387,25 +386,23 @@ plot(x, y, z, zcolor=reverse(z), markersize=3, markershape= :circle,
|
|||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
> We use the `plotlyjs` backend here, so the plot is interactive and can be rotated and zoomed with the mouse.
|
> The `plotlyjs` backend makes the plot interactive: it can be rotated and zoomed with the mouse.
|
||||||
|
|
||||||
### Plots.jl and _recipes_
|
### Plots.jl and _recipes_
|
||||||
|
|
||||||
Other packages can extend the capabilities of `Plots.jl` by defining so-called _recipes_ for special plots and data structures, see [https://docs.juliaplots.org/latest/ecosystem/](https://docs.juliaplots.org/latest/ecosystem/), e.g.:
|
Other packages can extend `Plots.jl` by defining so-called _recipes_ for special plots and data structures (see [https://docs.juliaplots.org/latest/ecosystem/](https://docs.juliaplots.org/latest/ecosystem/)), e.g.:
|
||||||
|
|
||||||
- `StatsPlots.jl` direct plotting of _Dataframes_, special statistical plots, etc. or
|
- `StatsPlots.jl`: direct plotting of _DataFrames_, special statistical plots, etc.
|
||||||
- `GraphRecipes.jl` [Plotting of graph structures](https://docs.juliaplots.org/latest/GraphRecipes/examples/)
|
- `GraphRecipes.jl`: [plotting of graph structures](https://docs.juliaplots.org/latest/GraphRecipes/examples/)
|
||||||
|
|
||||||
|
|
||||||
### A Bar Chart
|
### A Bar Chart
|
||||||
|
|
||||||
For the last example, we load a package that provides over 700 free (_"public domain"_) datasets, including, for example:
|
For the last example, we load a package providing over 700 free (_"public domain"_) datasets, including:
|
||||||
|
|
||||||
- the passenger list of the _Titanic_,
|
- the _Titanic_ passenger list,
|
||||||
- fuel consumption data of American cars from the 70s or
|
- fuel consumption data for American cars from the 70s, or
|
||||||
- historical exchange rates
|
- historical exchange rates:
|
||||||
|
|
||||||
provides:
|
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
using RDatasets
|
using RDatasets
|
||||||
@@ -425,7 +422,7 @@ The dataset ["Motor Trend Car Road Tests"](https://rdrr.io/r/datasets/mtcars.htm
|
|||||||
cars = dataset("datasets", "mtcars")
|
cars = dataset("datasets", "mtcars")
|
||||||
```
|
```
|
||||||
|
|
||||||
We only need the two columns `cars.Model` and `cars.MPG` for the plot, the fuel consumption in _miles per gallon_ (more means more economical!)
|
We only need two columns for the plot: `cars.Model` and `cars.MPG`, the fuel consumption in _miles per gallon_ (higher is more economical).
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
theme(:bright)
|
theme(:bright)
|
||||||
@@ -445,8 +442,8 @@ bar(cars.Model, cars.MPG,
|
|||||||
|
|
||||||
### What is Missing: Animation
|
### What is Missing: Animation
|
||||||
|
|
||||||
Please refer to the [documentation](https://docs.juliaplots.org/latest/animations/) and only an example
|
See the [documentation](https://docs.juliaplots.org/latest/animations/); here is an example
|
||||||
(from <https://www.juliafordatascience.com/animations-with-plots-jl/>) is given:
|
(from <https://www.juliafordatascience.com/animations-with-plots-jl/>):
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
#| error: false
|
#| error: false
|
||||||
@@ -465,7 +462,7 @@ gif(anim, fps=50)
|
|||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
:::: {.content-visible when-format="pdf"}
|
:::: {.content-visible when-format="typst"}
|
||||||
```{julia}
|
```{julia}
|
||||||
#| echo: false
|
#| echo: false
|
||||||
Random.seed!(123)
|
Random.seed!(123)
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ myactive_module() = Main.Notebook
|
|||||||
Base.active_module() = myactive_module()
|
Base.active_module() = myactive_module()
|
||||||
```
|
```
|
||||||
|
|
||||||
# Working with Julia: REPL, Packages, Introspection
|
# Working with Julia: The REPL, Packages, and Introspection
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
#| error: false
|
#| error: false
|
||||||
@@ -30,9 +30,9 @@ Base.active_module() = myactive_module()
|
|||||||
# https://github.com/JuliaLang/julia/blob/master/base/show.jl#L3073-L3077
|
# https://github.com/JuliaLang/julia/blob/master/base/show.jl#L3073-L3077
|
||||||
```
|
```
|
||||||
|
|
||||||
## Documentation
|
## Official Documentation
|
||||||
|
|
||||||
The official Julia documentation [https://docs.julialang.org/](https://docs.julialang.org/) contains numerous overviews, including:
|
The official Julia documentation [https://docs.julialang.org/](https://docs.julialang.org/) contains several overviews, including:
|
||||||
|
|
||||||
- [https://docs.julialang.org/en/v1/base/punctuation/](https://docs.julialang.org/en/v1/base/punctuation/) List of symbols
|
- [https://docs.julialang.org/en/v1/base/punctuation/](https://docs.julialang.org/en/v1/base/punctuation/) List of symbols
|
||||||
- [https://docs.julialang.org/en/v1/manual/unicode-input/](https://docs.julialang.org/en/v1/manual/unicode-input/) List of special Unicode symbols and their input methods via tab completion in Julia
|
- [https://docs.julialang.org/en/v1/manual/unicode-input/](https://docs.julialang.org/en/v1/manual/unicode-input/) List of special Unicode symbols and their input methods via tab completion in Julia
|
||||||
@@ -45,7 +45,7 @@ The official Julia documentation [https://docs.julialang.org/](https://docs.juli
|
|||||||
After starting Julia in a terminal, you can enter both Julia code and various commands:
|
After starting Julia in a terminal, you can enter both Julia code and various commands:
|
||||||
|
|
||||||
:::{.narrow}
|
:::{.narrow}
|
||||||
| Command | Effect |
|
| Command | Action |
|
||||||
| :----------------------------| :------------------------ |
|
| :----------------------------| :------------------------ |
|
||||||
| `exit()` or `Ctrl-d` | exit Julia |
|
| `exit()` or `Ctrl-d` | exit Julia |
|
||||||
| `Ctrl-c` | interrupt |
|
| `Ctrl-c` | interrupt |
|
||||||
@@ -55,13 +55,13 @@ After starting Julia in a terminal, you can enter both Julia code and various co
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
The REPL has several modes:
|
The REPL supports several modes:
|
||||||
|
|
||||||
| Mode | Prompt | Start mode | Exit mode |
|
| Mode | Prompt | Start mode | Exit mode |
|
||||||
| :- | :- | :- | :- |
|
| :- | :- | :- | :- |
|
||||||
| default| `julia>` | | `Ctrl-d` (exits Julia) |
|
| default| `julia>` | | `Ctrl-d` (exits Julia) |
|
||||||
| Package manager | `pkg>` | `]` | `backspace` |
|
| Package manager | `pkg>` | `]` | `backspace` |
|
||||||
| Help | `help?>` | `?`| `backspace `|
|
| Help | `help?>` | `?`| `backspace`|
|
||||||
|Shell | `shell>` | `;` | `backspace`|
|
|Shell | `shell>` | `;` | `backspace`|
|
||||||
|
|
||||||
:::
|
:::
|
||||||
@@ -98,15 +98,14 @@ In a Jupyter notebook, the modes are usable as single-line commands in their own
|
|||||||
|
|
||||||
## The Package Manager
|
## The Package Manager
|
||||||
|
|
||||||
An important part of the _Julia ecosystem_ is the numerous packages that extend Julia.
|
An important part of the _Julia ecosystem_ is the extensive collection of packages that extend Julia's functionality.
|
||||||
|
|
||||||
- Some packages are part of every Julia installation and only need to be activated with a `using Packagename` statement.
|
- Some packages are part of every Julia installation and only need to be activated with `using Packagename`.
|
||||||
- They form the so-called _standard library_, which includes
|
- They form the so-called _standard library_, which includes
|
||||||
- `LinearAlgebra`, `Statistics`, `SparseArrays`, `Printf`, `Pkg`, and others.
|
- `LinearAlgebra`, `Statistics`, `SparseArrays`, `Printf`, `Pkg`, and others.
|
||||||
- Over 9000 packages are officially registered, see [https://julialang.org/packages/](https://julialang.org/packages/).
|
- Over 10000 packages are officially registered, see [https://julialang.org/packages/](https://julialang.org/packages/).
|
||||||
- These can be downloaded and installed with just a few keystrokes.
|
- These can be downloaded and installed with just a few keystrokes using the _package manager_ `Pkg`.
|
||||||
- The _package manager_ `Pkg` is used for this purpose.
|
- `Pkg` can be called in two ways:
|
||||||
- It can be used in two ways:
|
|
||||||
- as normal Julia statements that can also be in a `.jl` program file:
|
- as normal Julia statements that can also be in a `.jl` program file:
|
||||||
```{.Julia}
|
```{.Julia}
|
||||||
using Pkg
|
using Pkg
|
||||||
@@ -124,42 +123,43 @@ An important part of the _Julia ecosystem_ is the numerous packages that extend
|
|||||||
|
|
||||||
| Function | `pkg` - Mode | Explanation |
|
| Function | `pkg` - Mode | Explanation |
|
||||||
|:------------------------|:--------------------------| :-------------------------------------------------------|
|
|:------------------------|:--------------------------| :-------------------------------------------------------|
|
||||||
| `Pkg.add("MyPack")` | `pkg> add MyPack` | add `MyPack.jl` to current environment |
|
| `Pkg.add("PackageXY")` | `pkg> add PackageXY` | add to current environment |
|
||||||
| `Pkg.rm("MyPack")` | `pkg> remove MyPack` | remove `MyPack.jl` from current environment |
|
| `Pkg.rm("PackageXY")` | `pkg> remove PackageXY` | remove from current environment |
|
||||||
| `Pkg.update()` | `pkg> update` | update packages in current environment |
|
| `Pkg.update()` | `pkg> update` | update packages in current environment |
|
||||||
| `Pkg.activate("mydir")` | `pkg> activate mydir` | activate directory as current environment |
|
| `Pkg.activate("mydir")` | `pkg> activate mydir` | activate directory as current environment |
|
||||||
| `Pkg.status()` | `pkg> status` | list packages |
|
| `Pkg.status()` | `pkg> status` | list packages |
|
||||||
| `Pkg.instantiate()` | `pg> instantiate` | install all packages according to `Project.toml` |
|
| `Pkg.instantiate()` | `pkg> instantiate` | install all packages according to `Project.toml` |
|
||||||
|
|
||||||
|
: {tbl-colwidths="[35,40,25]"}
|
||||||
|
|
||||||
### Installed Packages and Environments
|
### Installed Packages and Environments
|
||||||
|
|
||||||
- Julia and the package manager maintain
|
- Julia's package manager maintains:
|
||||||
1. a list of packages explicitly installed with the command `Pkg.add()` or `]add` with exact version specifications in a file `Project.toml` and
|
1. a list of packages explicitly installed with the command `Pkg.add()` or `]add` with exact version specifications in a file `Project.toml` and
|
||||||
2. a list of all packages installed as implicit dependencies in the file `Manifest.toml`.
|
2. a list of all packages installed as implicit dependencies in the file `Manifest.toml`.
|
||||||
- The directory in which these files are located is the `environment` and is displayed with `Pkg.status()` or `]status`.
|
- The directory in which these files are located is the `environment` and is displayed with `Pkg.status()` or `]status`.
|
||||||
- In the normal case, this looks like this:
|
- Without an expplicit project environment, this looks as follows:
|
||||||
|
|
||||||
```
|
```
|
||||||
(@v1.10) pkg> status
|
(@v1.10) pkg> status
|
||||||
Status `~/.julia/environments/v1.10/Project.toml`
|
Status `~/.julia/environments/v1.12/Project.toml`
|
||||||
[6e4b80f9] BenchmarkTools v1.5.0
|
[6e4b80f9] BenchmarkTools v1.6.3
|
||||||
[5fb14364] OhMyREPL v0.5.24
|
[5fb14364] OhMyREPL v0.5.29
|
||||||
[91a5bcdd] Plots v1.40.4
|
[91a5bcdd] JuliaFormatter v2.3.0
|
||||||
[295af30f] Revise v3.5.14
|
[295af30f] Revise v3.13.2
|
||||||
```
|
```
|
||||||
|
|
||||||
- You can use separate `environments` for different projects. You can either start Julia with
|
- You can use separate `environments` for different projects. You can either start Julia with
|
||||||
```shell
|
```shell
|
||||||
julia --project=path/to/myproject
|
julia --project=path/to/myproject
|
||||||
```
|
```
|
||||||
or activate the environment in Julia with `Pkg.activate("path/to/myproject")`. Then `Project.toml, Manifest.toml` are created and managed there. (The installation of package files still takes place somewhere under `$HOME/.julia`)
|
or activate the environment in Julia with `Pkg.activate("path/to/myproject")`. Then `Project.toml` and `Manifest.toml` files are created and managed there. (The installation of package files still takes place somewhere under `$HOME/.julia`)
|
||||||
|
|
||||||
|
|
||||||
### For Installing Packages on Our Jupyter Server `misun103`:
|
### Installing Packages on our Jupyter Server `misun103`:
|
||||||
|
|
||||||
- There is a central repository in which all packages mentioned in this course are already installed.
|
- A central repository already contains all packages mentioned in this course.
|
||||||
- There you have no write permissions.
|
- You have no write permissions there.
|
||||||
- However, you can install additional packages in your `HOME`. As a first command, you need to activate the current directory:
|
- However, you can install additional packages in your `HOME`. As a first command, you need to activate the current directory:
|
||||||
|
|
||||||
|
|
||||||
@@ -181,14 +181,14 @@ After that, you can install packages with `add` in the pkg-mode:
|
|||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
Warning! This can take a long time! Many packages have complex dependencies and trigger the installation of further packages. Many packages are precompiled during installation. You can see the installation progress in the REPL, but unfortunately not in the Jupyter notebook.
|
Package installation can take a significant amount of time. Many packages have complex dependencies and trigger the installation of further packages. Many packages are precompiled during installation. You can see the installation progress in the REPL, but unfortunately not in the Jupyter notebook.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## The Julia JIT _(just in time)_ Compiler: Introspection
|
## The Julia JIT _(just in time)_ Compiler: Introspection
|
||||||
|
|
||||||
Julia is built on the tools of the _LLVM Compiler Infrastructure Project_.
|
The Julia compiler is based on the _LLVM Compiler Infrastructure Project_.
|
||||||
|
|
||||||
:::{.narrow}
|
:::{.narrow}
|
||||||
Stages of Compilation
|
Stages of Compilation
|
||||||
@@ -214,7 +214,7 @@ end
|
|||||||
|
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
p = Meta.parse( "function f(x,y); z=x^2+log(y); return 2x; end ")
|
p = Meta.parse( "function f(x,y); z=x^2+log(y); return 2x; end")
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ engine: julia
|
|||||||
# Containers
|
# Containers
|
||||||
|
|
||||||
Julia offers a wide selection of container types with largely similar interfaces.
|
Julia offers a wide selection of container types with largely similar interfaces.
|
||||||
We introduce `Tuple`, `Range`, and `Dict` here, and in the next chapter we will cover `Array`, `Vector`, and `Matrix`.
|
This chapter introduces `Tuple`, `Range`, and `Dict`; the next chapter covers `Array`, `Vector`, and `Matrix`.
|
||||||
|
|
||||||
These containers are:
|
These containers are:
|
||||||
|
|
||||||
@@ -24,14 +24,14 @@ and some are also
|
|||||||
Furthermore, there are several common functions, e.g.,
|
Furthermore, there are several common functions, e.g.,
|
||||||
|
|
||||||
- `length(container)` --- number of elements
|
- `length(container)` --- number of elements
|
||||||
- `eltype(container)` --- type of elements
|
- `eltype(container)` --- element type
|
||||||
- `isempty(container)` --- test whether container is empty
|
- `isempty(container)` --- test if container is empty
|
||||||
- `empty!(container)` --- empties container (only if mutable)
|
- `empty!(container)` --- empties the container (if mutable)
|
||||||
|
|
||||||
|
|
||||||
## Tuples
|
## Tuples
|
||||||
|
|
||||||
A tuple is an immutable container of elements. It is therefore not possible to add new elements or change the value of an element.
|
A tuple is an immutable container of elements. You cannot add new elements or change existing values.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -54,13 +54,13 @@ Tuples are frequently used as function return values to return more than one obj
|
|||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
# Integer division and remainder:
|
# Integer division and remainder:
|
||||||
# quotient and remainder are assigned to variables q and r
|
# Assign quotient and remainder to variables `q` and `r`:
|
||||||
|
|
||||||
q, r = divrem(71, 6)
|
q, r = divrem(71, 6)
|
||||||
@show q r;
|
@show q r;
|
||||||
```
|
```
|
||||||
As you can see here, parentheses can be omitted in certain constructs.
|
Parentheses can be omitted in certain constructs.
|
||||||
This *implicit tuple packing/unpacking* is also commonly used in multiple assignments:
|
This *implicit tuple packing/unpacking* is commonly used in multiple assignments:
|
||||||
|
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
@@ -72,9 +72,7 @@ x, y, z = 12, 17, 203
|
|||||||
y
|
y
|
||||||
```
|
```
|
||||||
|
|
||||||
Some functions require tuples as arguments or always return tuples. Then you sometimes need a tuple with a single element.
|
Some functions require tuples as arguments or always return tuples. A single-element tuple is written as:
|
||||||
|
|
||||||
This is written as:
|
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
x = (13,) # a 1-element tuple
|
x = (13,) # a 1-element tuple
|
||||||
@@ -95,7 +93,7 @@ We have already used *range* objects in numerical `for` loops.
|
|||||||
r = 1:1000
|
r = 1:1000
|
||||||
typeof(r)
|
typeof(r)
|
||||||
```
|
```
|
||||||
There are various *range* types. As you can see, they are parameterized types based on the numeric type, and `UnitRange` is, for example, a *range* with step size 1. Their constructors are usually named `range()`.
|
There are various *range* types. `UnitRange`, for example, is a *range* with step size 1. Their constructors are typically all named `range()`.
|
||||||
|
|
||||||
The colon is a special syntax.
|
The colon is a special syntax.
|
||||||
|
|
||||||
@@ -104,7 +102,7 @@ The colon is a special syntax.
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
*Ranges* are obviously iterable, not mutable, but indexable.
|
*Ranges* are iterable, immutable, and indexable.
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
(3:100)[20] # the 20th element
|
(3:100)[20] # the 20th element
|
||||||
@@ -113,14 +111,11 @@ The colon is a special syntax.
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
Recall the semantics of the `for` loop: `for i in 1:1000` means **not**:
|
Recall the semantics of the `for` loop: `for i in 1:1000` does **not** mean 'increment the loop variable `i` by one each iteration'; **rather**, it means 'successively assign the values 1, 2, 3, ..., 1000 to the loop variable from the container'.
|
||||||
|
|
||||||
- 'The loop variable `i` is incremented by one in each iteration' **but rather**
|
Creating this container explicitly would be very inefficient.
|
||||||
- 'The loop variable is successively assigned the values 1,2,3,...,1000 from the container'.
|
|
||||||
|
|
||||||
However, it would be very inefficient to actually create this container explicitly.
|
- _Ranges_ are "lazy" vectors never stored as concrete lists. This makes them ideal as `for` loop iterators: memory-efficient and fast.
|
||||||
|
|
||||||
- _Ranges_ are "lazy" vectors that are never really stored as a concrete list anywhere. This makes them so useful as iterators in `for` loops: memory-efficient and fast.
|
|
||||||
- They are "recipes" or generators that respond to the query "Give me your next element!".
|
- They are "recipes" or generators that respond to the query "Give me your next element!".
|
||||||
- In fact, the supertype `AbstractRange` is a subtype of `AbstractVector`.
|
- In fact, the supertype `AbstractRange` is a subtype of `AbstractVector`.
|
||||||
|
|
||||||
@@ -138,7 +133,7 @@ The macro `@allocated` outputs how many bytes of memory were allocated during th
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
The function `collect()` is used to convert to a "real" vector.
|
The `collect()` function converts a range to a concrete vector.
|
||||||
|
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
@@ -150,186 +145,183 @@ Quite useful, e.g., when preparing data for plotting, is the *range* type `LinRa
|
|||||||
```{julia}
|
```{julia}
|
||||||
LinRange(2, 50, 300)
|
LinRange(2, 50, 300)
|
||||||
```
|
```
|
||||||
`LinRange(start, stop, n)` generates an equidistant list of `n` values where the first and last are the specified limits.
|
`LinRange(start, stop, n)` generates `n` equidistant values from start to stop. Use `collect()` to obtain the corresponding vector if needed.
|
||||||
With `collect()` you can also obtain the corresponding vector if needed.
|
|
||||||
|
|
||||||
|
|
||||||
## Dictionaries
|
## Dictionaries
|
||||||
|
|
||||||
- _Dictionaries_ (German: "associative list" or "lookup table" or ...) are special containers.
|
- _Dictionaries_ (also known as associative arrays or lookup tables) are special containers.
|
||||||
- Entries in a vector `v` are addressable by an index 1,2,3....: `v[i]`
|
- Whereas vector entries are addressed by integer indices: `v[i]`; dictionary entries are addressed by more general _keys_.
|
||||||
- Entries in a _dictionary_ are addressable by more general _keys_.
|
- A dictionary is a collection of _key-value_ pairs with parameterized type `Dict{S,T}`, where `S` is the key type and `T` is the value type.
|
||||||
- A _dictionary_ is a collection of _key-value_ pairs.
|
|
||||||
- Thus, _dictionaries_ in Julia have the parameterized type `Dict{S,T}`, where `S` is the type of _keys_ and `T` is the type of _values_.
|
|
||||||
|
|
||||||
|
|
||||||
They can be created explicitly:
|
Create a dictionary explicitly:
|
||||||
```{julia}
|
```{julia}
|
||||||
# Population in 2020 in millions, source: wikipedia
|
# Population in 2020 in millions, source: wikipedia
|
||||||
|
|
||||||
EW = Dict("Berlin" => 3.66, "Hamburg" => 1.85,
|
Ppl = Dict("Berlin" => 3.66, "Hamburg" => 1.85,
|
||||||
"München" => 1.49, "Köln" => 1.08)
|
"München" => 1.49, "Köln" => 1.08)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
typeof(EW)
|
typeof(Ppl)
|
||||||
```
|
```
|
||||||
|
|
||||||
and indexed with the _keys_:
|
and indexed with the _keys_:
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
EW["Berlin"]
|
Ppl["Berlin"]
|
||||||
```
|
```
|
||||||
|
|
||||||
Of course, querying a non-existent _key_ is an error.
|
Querying a non-existent _key_ throws an error.
|
||||||
```{julia}
|
```{julia}
|
||||||
EW["Leipzig"]
|
Ppl["Leipzig"]
|
||||||
```
|
```
|
||||||
|
|
||||||
You can also ask beforehand...
|
Check beforehand with `haskey()`...
|
||||||
```{julia}
|
```{julia}
|
||||||
haskey(EW, "Leipzig")
|
haskey(Ppl, "Leipzig")
|
||||||
```
|
```
|
||||||
|
|
||||||
... or use the function `get(dict, key, default)`, which does not throw an error for non-existent keys but returns the third argument.
|
Or use `get(dict, key, default)`, which returns the default value instead of throwing an error.
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
@show get(EW, "Leipzig", -1) get(EW, "Berlin", -1);
|
@show get(Ppl, "Leipzig", -1) get(Ppl, "Berlin", -1);
|
||||||
```
|
```
|
||||||
|
|
||||||
You can also request all `keys` and `values` as special containers.
|
You can also request all `keys` and `values` as special containers.
|
||||||
```{julia}
|
```{julia}
|
||||||
keys(EW)
|
keys(Ppl)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
values(EW)
|
values(Ppl)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
You can iterate over the `keys`...
|
Iterate over the `keys`...
|
||||||
```{julia}
|
```{julia}
|
||||||
for i in keys(EW)
|
for i in keys(Ppl)
|
||||||
n = EW[i]
|
n = Ppl[i]
|
||||||
println("The city $i has $n million inhabitants.")
|
println("The city $i has $n million inhabitants.")
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
or directly over `key-value` pairs.
|
Or iterate directly over `key-value` pairs.
|
||||||
```{julia}
|
```{julia}
|
||||||
for (stadt, ew) ∈ EW
|
for (city, pop) ∈ Ppl
|
||||||
println("$stadt : $ew Million.")
|
println("$city : $pop Million.")
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
### Extending and Modifying
|
### Extending and Modifying
|
||||||
|
|
||||||
You can add additional `key-value` pairs to a `Dict`...
|
Add `key-value` pairs to a `Dict`...
|
||||||
```{julia}
|
```{julia}
|
||||||
EW["Leipzig"] = 0.52
|
Ppl["Leipzig"] = 0.52
|
||||||
EW["Dresden"] = 0.52
|
Ppl["Dresden"] = 0.52
|
||||||
EW
|
Ppl
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
and change a `value`.
|
Change a `value`:
|
||||||
```{julia}
|
```{julia}
|
||||||
# Oh, the Leipzig number was from 2010, not 2020
|
# Update: Leipzig data was from 2010, not 2020
|
||||||
|
|
||||||
EW["Leipzig"] = 0.597
|
Ppl["Leipzig"] = 0.597
|
||||||
EW
|
Ppl
|
||||||
```
|
```
|
||||||
|
|
||||||
A pair can also be deleted via its `key`.
|
Delete a pair by its `key`:
|
||||||
```{julia}
|
```{julia}
|
||||||
delete!(EW, "Dresden")
|
delete!(Ppl, "Dresden")
|
||||||
```
|
```
|
||||||
|
|
||||||
Many functions can work with `Dicts` like with other containers.
|
Many functions work with `Dicts` like other containers.
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
maximum(values(EW))
|
maximum(values(Ppl))
|
||||||
```
|
```
|
||||||
|
|
||||||
### Creating an Empty Dictionary
|
### Creating an Empty Dictionary
|
||||||
|
|
||||||
Without type specification ...
|
Without explicit types:
|
||||||
```{julia}
|
```{julia}
|
||||||
d1 = Dict()
|
d1 = Dict()
|
||||||
```
|
```
|
||||||
|
|
||||||
and with type specification:
|
With explicit types:
|
||||||
```{julia}
|
```{julia}
|
||||||
d2 = Dict{String, Int}()
|
d2 = Dict{String, Int}()
|
||||||
```
|
```
|
||||||
|
|
||||||
### Conversion to Vectors: `collect()`
|
### Conversion to Vectors: `collect()`
|
||||||
|
|
||||||
- `keys(dict)` and `values(dict)` are special data types.
|
- `keys(dict)` and `values(dict)` return special container types.
|
||||||
- The function `collect()` converts them to a `Vector` type.
|
- `collect()` converts them to `Vector`s.
|
||||||
- `collect(dict)` returns a list of type `Vector{Pair{S,T}}`
|
- `collect(dict)` returns a `Vector{Pair{S,T}}`.
|
||||||
|
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
collect(EW)
|
collect(Ppl)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
collect(keys(EW)), collect(values(EW))
|
collect(keys(Ppl)), collect(values(Ppl))
|
||||||
```
|
```
|
||||||
|
|
||||||
### Ordered Iteration over a Dictionary
|
### Ordered Iteration over a Dictionary
|
||||||
|
|
||||||
We sort the keys. As strings, they are sorted alphabetically. With the `rev` parameter, sorting is done in reverse order.
|
We sort the keys. As strings, they are sorted alphabetically. With the `rev` parameter, sorting is done in reverse order.
|
||||||
```{julia}
|
```{julia}
|
||||||
for k in sort(collect(keys(EW)), rev = true)
|
for k in sort(collect(keys(Ppl)), rev = true)
|
||||||
n = EW[k]
|
n = Ppl[k]
|
||||||
println("$k has $n million inhabitants ")
|
println("$k has $n million inhabitants ")
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
We sort `collect(dict)`. This is a vector of pairs. With `by` we define what to sort by: the second element of the pair.
|
Let's sort `collect(dict)`, a vector of pairs. Use `by` to specify the sort key: the second element of each pair.
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
for (k,v) in sort(collect(EW), by = pair -> last(pair), rev=false)
|
for (k,v) in sort(collect(Ppl), by = pair -> last(pair), rev=false)
|
||||||
println("$k has $v million inhabitants")
|
println("$k has $v million inhabitants")
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
### An Application of Dictionaries: Counting Frequencies
|
### An Application of Dictionaries: Counting Frequencies
|
||||||
|
|
||||||
We do "experimental probability" with 2 dice:
|
Let's do "experimental stochastics" with 2 dice:
|
||||||
|
|
||||||
Given `l`, a list with the results of 100,000 double dice rolls, i.e., 100,000 numbers between 2 and 12.
|
Let `l` be a vector containing 100,000 sums of two dice rolls (numbers from 2 to 12).
|
||||||
|
|
||||||
How frequently do the numbers 2 to 12 occur?
|
How frequently does each number from 2 to 12 occur?
|
||||||
|
|
||||||
|
|
||||||
We (let) roll:
|
Roll the dice:
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
|
|
||||||
l = rand(1:6, 100_000) .+ rand(1:6, 100_000)
|
l = rand(1:6, 100_000) .+ rand(1:6, 100_000)
|
||||||
```
|
```
|
||||||
|
|
||||||
We count the frequencies of the events using a dictionary. We take the event as the `key` and its frequency as the `value`.
|
Count event frequencies using a dictionary. Use the event as the `key` and its frequency as the `value`.
|
||||||
```{julia}
|
```{julia}
|
||||||
# In this case, one could also solve this with a simple vector.
|
# In this case, a simple vector would also work.
|
||||||
# A better illustration would be, e.g., word frequency in
|
# A better use case for dictionaries is word frequency in texts,
|
||||||
# a text. Then i is not an integer but a word=string
|
# where keys are strings instead of integers.
|
||||||
|
|
||||||
d = Dict{Int,Int}() # the dict for counting
|
d = Dict{Int,Int}() # dictionary for counting
|
||||||
|
|
||||||
for i in l # for each i, d[i] is incremented.
|
for i in l # for each i, increment d[i]
|
||||||
d[i] = get(d, i, 0) + 1
|
d[i] = get(d, i, 0) + 1
|
||||||
end
|
end
|
||||||
d
|
d
|
||||||
```
|
```
|
||||||
|
|
||||||
The result:
|
Result:
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
using Plots
|
using Plots
|
||||||
@@ -337,6 +329,6 @@ using Plots
|
|||||||
plot(collect(keys(d)), collect(values(d)), seriestype=:scatter)
|
plot(collect(keys(d)), collect(values(d)), seriestype=:scatter)
|
||||||
```
|
```
|
||||||
|
|
||||||
##### The explanatory image:
|
Explanatory image:
|
||||||
|
|
||||||
[https://math.stackexchange.com/questions/1204396/why-is-the-sum-of-the-rolls-of-two-dices-a-binomial-distribution-what-is-define](https://math.stackexchange.com/questions/1204396/why-is-the-sum-of-the-rolls-of-two-dices-a-binomial-distribution-what-is-define)
|
[https://math.stackexchange.com/questions/1204396/why-is-the-sum-of-the-rolls-of-two-dices-a-binomial-distribution-what-is-define](https://math.stackexchange.com/questions/1204396/why-is-the-sum-of-the-rolls-of-two-dices-a-binomial-distribution-what-is-define)
|
||||||
|
|||||||
@@ -7,17 +7,34 @@ engine: julia
|
|||||||
#| echo: false
|
#| echo: false
|
||||||
#| output: false
|
#| output: false
|
||||||
using InteractiveUtils
|
using InteractiveUtils
|
||||||
import QuartoNotebookWorker
|
##import QuartoNotebookWorker
|
||||||
Base.stdout = QuartoNotebookWorker.with_context(stdout)
|
##Base.stdout = QuartoNotebookWorker.with_context(stdout)
|
||||||
myactive_module() = Main.Notebook
|
##myactive_module() = Main.Notebook
|
||||||
Base.active_module() = myactive_module()
|
##Base.active_module() = myactive_module()
|
||||||
|
|
||||||
|
#struct M a::Int end; x = M(22); @show x
|
||||||
|
#should not print "Main.Notebook.M(22)" but only "M(22)"
|
||||||
|
function Base.show(io::IO, x::T) where T
|
||||||
|
if parentmodule(T) == @__MODULE__
|
||||||
|
# Print "TypeName(fields...)" without module prefix
|
||||||
|
print(io, nameof(T), "(")
|
||||||
|
fields = fieldnames(T)
|
||||||
|
for (i, f) in enumerate(fields)
|
||||||
|
print(io, getfield(x, f))
|
||||||
|
i < length(fields) && print(io, ", ")
|
||||||
|
end
|
||||||
|
print(io, ")")
|
||||||
|
else
|
||||||
|
invoke(Base.show, Tuple{IO, Any}, io, x)
|
||||||
|
end
|
||||||
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
# Vectors, Matrices, Arrays
|
# Vectors, Matrices, Arrays
|
||||||
|
|
||||||
## General
|
## General
|
||||||
|
|
||||||
Let us now turn to the probably most important containers for numerical mathematics:
|
We now turn to containers important for numerical computing:
|
||||||
|
|
||||||
- Vectors `Vector{T}`
|
- Vectors `Vector{T}`
|
||||||
- Matrices `Matrix{T}` with two indices
|
- Matrices `Matrix{T}` with two indices
|
||||||
@@ -30,7 +47,7 @@ In fact, `Vector{T}` is an alias for `Array{T,1}` and `Matrix{T}` is an alias fo
|
|||||||
Vector{Float64} === Array{Float64,1} && Matrix{Float64} === Array{Float64,2}
|
Vector{Float64} === Array{Float64,1} && Matrix{Float64} === Array{Float64,2}
|
||||||
```
|
```
|
||||||
|
|
||||||
When created by an explicit element list, the 'greatest common type' for the type parameter `T` is determined.
|
When created from an explicit element list, Julia determines the greatest common type for the type parameter `T`.
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
v = [33, "33", 1.2]
|
v = [33, "33", 1.2]
|
||||||
@@ -59,8 +76,8 @@ for f ∈ (length, eltype, ndims, size)
|
|||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
- The strength of the 'classical' array for scientific computing lies in the fact that it is simply a contiguous memory segment in which components of the same length (e.g. 64 bits) are stored sequentially. This makes the memory requirement minimal and the access speed to a component, both when reading and when modifying, maximal. The location of the component `v[i]` can be calculated immediately from `i`.
|
- The strength of 'classical' arrays for scientific computing is that they are simply contiguous memory segments where same-length components (e.g., 64 bits) are stored sequentially. This minimizes memory requirements and maximizes access speed for both reading and modifying components. The location of `v[i]` can be calculated directly from `i`.
|
||||||
- Julia's `Array{T,N}` (and therefore vectors and matrices) is implemented in this way for the usual numeric types `T`. The elements are stored *unboxed*. In contrast, for example, a `Vector{Any}` is implemented as a list of addresses of objects *(boxed)* and not as a list of the objects themselves.
|
- Julia's `Array{T,N}` (including vectors and matrices) is implemented this way for standard numeric types `T`. Elements are stored *unboxed*. In contrast, `Vector{Any}` is implemented as a list of object addresses *(boxed)*, not as a list of the objects themselves.
|
||||||
- Julia's `Array{T,N}` stores its elements directly *(unboxed)*, when `isbitstype(T) == true`.
|
- Julia's `Array{T,N}` stores its elements directly *(unboxed)*, when `isbitstype(T) == true`.
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
@@ -71,12 +88,12 @@ isbitstype(String)
|
|||||||
|
|
||||||
## Vectors
|
## Vectors
|
||||||
|
|
||||||
### List-like Functions
|
### Stack-like Functions
|
||||||
|
|
||||||
- `push!(vector, items...)` --- appends elements at the end of the vector
|
- `push!(vector, items...)` --- appends elements to the end
|
||||||
- `pushfirst!(vector, items...)` --- prepends elements at the beginning of the vector
|
- `pushfirst!(vector, items...)` --- prepends elements to the beginning
|
||||||
- `pop!(vector)` --- removes the last element and returns it as a result,
|
- `pop!(vector)` --- removes and returns the last element
|
||||||
- `popfirst!(vector)` --- removes the first element and returns it
|
- `popfirst!(vector)` --- removes and returns the first element
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
v = Float64[] # empty Vector{Float64}
|
v = Float64[] # empty Vector{Float64}
|
||||||
@@ -90,41 +107,41 @@ println("a= $a")
|
|||||||
push!(v, 17)
|
push!(v, 17)
|
||||||
```
|
```
|
||||||
|
|
||||||
A `push!()` can be very expensive, as new memory may need to be allocated and then the entire existing vector needs to be copied. Julia optimizes the memory management. In such a case, memory is allocated in advance, so that further `push!`s are very fast and one 'almost achieves O(1) speed'.
|
A `push!()` operation can be expensive, as it may require allocating new memory and copying the existing vector. Julia optimizes memory by preallocating space, so subsequent `push!` operations are very fast, almost achieving O(1) speed.
|
||||||
|
|
||||||
However, one should avoid operations like `push!()` or `resize()` in time-critical code and with very large arrays.
|
Avoid `push!()` or `resize()` in time-critical code with very large arrays.
|
||||||
|
|
||||||
### Further Constructors
|
### Further Constructors
|
||||||
|
|
||||||
One can create vectors with a given length and type uninitialized. This is fastest, the elements are random bit patterns.
|
You can create uninitialized vectors of a given length and type. This is the fastest method; elements contain random bit patterns.
|
||||||
```{julia}
|
```{julia}
|
||||||
# fixed length 1000, uninitialized
|
# Uninitialized vector of length 1000
|
||||||
|
|
||||||
v = Vector{Float64}(undef, 1000)
|
v = Vector{Float64}(undef, 1000)
|
||||||
v[345]
|
v[345]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
- `zeros(n)` creates a `Vector{Float64}` of length `n` and initializes with zero.
|
- `zeros(n)` creates a `Vector{Float64}` of length `n`, initialized with zeros.
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
v = zeros(7)
|
v = zeros(7)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
- `zeros(T,n)` creates a zero vector of type `T`.
|
- `zeros(T,n)` creates a zero vector of type `T`:
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
v=zeros(Int, 4)
|
v=zeros(Int, 4)
|
||||||
```
|
```
|
||||||
|
|
||||||
- `fill(x, n)` creates a `Vector{typeof(x)}` of length `n` and fills with `x`.
|
- `fill(x, n)` creates a `Vector{typeof(x)}` of length `n`, filled with `x`:
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
v = fill(sqrt(2), 5)
|
v = fill(sqrt(2), 5)
|
||||||
```
|
```
|
||||||
|
|
||||||
- `similar(v)` creates an uninitialized vector of the same type and size as `v`.
|
- `similar(v)` creates an uninitialized vector of the same type and size as `v`:
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
w = similar(v)
|
w = similar(v)
|
||||||
@@ -134,7 +151,7 @@ w = similar(v)
|
|||||||
|
|
||||||
### Construction by Implicit Loop _(list comprehension)_
|
### Construction by Implicit Loop _(list comprehension)_
|
||||||
|
|
||||||
Implicit `for` loops are another method to create vectors.
|
Implicit `for` loops provide another method to create vectors.
|
||||||
```{julia}
|
```{julia}
|
||||||
v4 = [i for i in 1.0:8]
|
v4 = [i for i in 1.0:8]
|
||||||
```
|
```
|
||||||
@@ -144,7 +161,7 @@ v4 = [i for i in 1.0:8]
|
|||||||
v5 = [log(i^2) for i in 1:4 ]
|
v5 = [log(i^2) for i in 1:4 ]
|
||||||
```
|
```
|
||||||
|
|
||||||
One can even insert an `if` clause.
|
You can even include an `if` clause.
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
v6 = [i^2 for i in 1:8 if i%3 != 2]
|
v6 = [i^2 for i in 1:8 if i%3 != 2]
|
||||||
@@ -152,24 +169,23 @@ v6 = [i^2 for i in 1:8 if i%3 != 2]
|
|||||||
|
|
||||||
### Bit Vectors {#sec-bitvec}
|
### Bit Vectors {#sec-bitvec}
|
||||||
|
|
||||||
Besides `Vector{Bool}`, there is also the special data type `BitVector` (and more generally `BitArray`) for storing arrays of truth values.
|
Besides `Vector{Bool}`, Julia provides the `BitVector` data type (and more generally `BitArray`) for storing boolean arrays.
|
||||||
|
|
||||||
While one byte is used for storing a `Bool`, storage in a BitVector is done bit by bit.
|
While a `Bool` uses one byte, `BitVector` stores values bit by bit.
|
||||||
|
|
||||||
The constructor converts a
|
The constructor converts a `Vector{Bool}` to a `BitVector`:
|
||||||
`Vector{Bool}` into a `BitVector`.
|
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
vb = BitVector([true, false, true, true])
|
vb = BitVector([true, false, true, true])
|
||||||
```
|
```
|
||||||
|
|
||||||
For the reverse direction, there is `collect()`.
|
Use `collect()` for the reverse conversion:
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
collect(vb)
|
collect(vb)
|
||||||
```
|
```
|
||||||
|
|
||||||
Bit vectors are produced, for example, as the result of element-wise comparisons (s. @sec-broadcast).
|
Bit vectors are produced, for example, by element-wise comparisons (see @sec-broadcast):
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
v4 .> 3.5
|
v4 .> 3.5
|
||||||
@@ -179,17 +195,17 @@ v4 .> 3.5
|
|||||||
|
|
||||||
### Indexing
|
### Indexing
|
||||||
|
|
||||||
Indices are ordinal numbers. Therefore, __indexing starts at 1.__
|
Indices are ordinal numbers, so __indexing starts at 1.__
|
||||||
|
|
||||||
As an index, one can use:
|
Valid indices include:
|
||||||
|
|
||||||
- Integer
|
- an integer
|
||||||
- Integer-valued range (same length or shorter)
|
- an integer-valued range (same length or shorter)
|
||||||
- Integer vector (same length or shorter)
|
- an integer vector (same length or shorter)
|
||||||
- Boolean vector or BitVector (same length)
|
- a boolean vector or BitVector (same length)
|
||||||
|
|
||||||
|
|
||||||
With indices, one can read and write array elements/parts.
|
Using indices, we can read and write array elements/parts.
|
||||||
|
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
@@ -208,7 +224,7 @@ v
|
|||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
Exceeding the index limits leads to a `BoundsError`.
|
Exceeding index limits throws a `BoundsError`.
|
||||||
|
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
@@ -238,7 +254,7 @@ v
|
|||||||
|
|
||||||
#### Indirect Indexing
|
#### Indirect Indexing
|
||||||
|
|
||||||
The indirect indexing with a *Vector of Integers/Indices* follows the formula
|
Indirect indexing with a *vector of integers* follows the formula
|
||||||
|
|
||||||
|
|
||||||
$v[ [i_1,\ i_2,\ i_3,...]] = [\ v[i_1],\ v[i_2],\ v[i_3],...]$
|
$v[ [i_1,\ i_2,\ i_3,...]] = [\ v[i_1],\ v[i_2],\ v[i_3],...]$
|
||||||
@@ -248,7 +264,7 @@ $v[ [i_1,\ i_2,\ i_3,...]] = [\ v[i_1],\ v[i_2],\ v[i_3],...]$
|
|||||||
v[ [1, 3, 4] ]
|
v[ [1, 3, 4] ]
|
||||||
```
|
```
|
||||||
|
|
||||||
is therefore equal to
|
which is the same as
|
||||||
```{julia}
|
```{julia}
|
||||||
[ v[1], v[3], v[4] ]
|
[ v[1], v[3], v[4] ]
|
||||||
```
|
```
|
||||||
@@ -259,18 +275,16 @@ is therefore equal to
|
|||||||
|
|
||||||
#### Indexing with a Vector of Truth Values
|
#### Indexing with a Vector of Truth Values
|
||||||
|
|
||||||
As an index, one can also use a `Vector{Bool}` or `BitVector` (s. @sec-bitvec) **of the same length**.
|
You can also use a `Vector{Bool}` or `BitVector` (see @sec-bitvec) **of the same length** as an index.
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
v[ [true, true, false, false, true, false, true, true] ]
|
v[ [true, true, false, false, true, false, true, true] ]
|
||||||
```
|
```
|
||||||
|
|
||||||
This is useful as one can, for example,
|
This is useful for:
|
||||||
|
|
||||||
- broadcast tests (s. @sec-broadcast),
|
|
||||||
- these tests then produce a BitVector and
|
|
||||||
- Bit vectors can be combined with bitwise operators `&` and `|` as needed.
|
|
||||||
|
|
||||||
|
- broadcast tests (see @sec-broadcast) which produce a `BitVector`, and
|
||||||
|
- combining `BitVector`s with bitwise operators `&` and `|`.
|
||||||
|
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
@@ -282,7 +296,7 @@ v[ (v .> 13) .& (v.<20) ]
|
|||||||
|
|
||||||
## Matrices and Arrays
|
## Matrices and Arrays
|
||||||
|
|
||||||
The methods presented so far for vectors also apply to higher-dimensional arrays.
|
Most methods for vectors also apply to higher-dimensional arrays.
|
||||||
|
|
||||||
One can create them uninitialized:
|
One can create them uninitialized:
|
||||||
|
|
||||||
@@ -290,12 +304,12 @@ One can create them uninitialized:
|
|||||||
A = Array{Float64,3}(undef, 6,9,3)
|
A = Array{Float64,3}(undef, 6,9,3)
|
||||||
```
|
```
|
||||||
|
|
||||||
In most functions, the dimensions can also be passed as a tuple. The above instruction can also be written as:
|
In most functions, the dimensions can also be passed as a tuple; the above can be written as:
|
||||||
```julia
|
```julia
|
||||||
A = Array{Float64, 3}(undef, (6,9,3))
|
A = Array{Float64, 3}(undef, (6,9,3))
|
||||||
```
|
```
|
||||||
|
|
||||||
Functions like `zeros()` etc. of course also work.
|
Functions like `zeros()` etc. also work.
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
m2 = zeros(3, 4, 2) # or zeros((3,4,2))
|
m2 = zeros(3, 4, 2) # or zeros((3,4,2))
|
||||||
@@ -315,7 +329,7 @@ M2 = similar(M, Float64)
|
|||||||
|
|
||||||
### Construction by Explicit Element List
|
### Construction by Explicit Element List
|
||||||
|
|
||||||
While vectors are noted in square brackets separated by commas, the notation for higher-dimensional objects is somewhat different.
|
While vectors are written in square brackets separated by commas, the notation for higher-dimensional objects is somewhat different.
|
||||||
|
|
||||||
- A matrix:
|
- A matrix:
|
||||||
|
|
||||||
@@ -345,7 +359,7 @@ M3 = [2 3 -1
|
|||||||
M2 = [2;4;; 3;5;; -1;-2]
|
M2 = [2;4;; 3;5;; -1;-2]
|
||||||
```
|
```
|
||||||
|
|
||||||
In the last example, these rules apply:
|
Here, the following rules apply:
|
||||||
|
|
||||||
- The separator is the semicolon.
|
- The separator is the semicolon.
|
||||||
- A semicolon `;` increases the 1st index.
|
- A semicolon `;` increases the 1st index.
|
||||||
@@ -354,14 +368,14 @@ In the last example, these rules apply:
|
|||||||
|
|
||||||
In the previous examples, the following syntactic enhancement (_syntactic sugar_) was applied:
|
In the previous examples, the following syntactic enhancement (_syntactic sugar_) was applied:
|
||||||
|
|
||||||
- Spaces separate like 2 semicolons -- thus also increase the 2nd index: $\quad a_{12}\quad a_{13}\quad a_{14}\ ...$
|
- Spaces separate like two semicolons -- thus increasing the 2nd index: $\quad a_{12}\quad a_{13}\quad a_{14}\ ...$
|
||||||
- Newline separates like a semicolon -- thus also increases the 1st index.
|
- Newline separates like a semicolon -- thus increasing the 1st index.
|
||||||
|
|
||||||
|
|
||||||
:::{.callout-important}
|
:::{.callout-important}
|
||||||
|
|
||||||
- Vector notation with comma as separator only works for vectors, do not mix "semicolon, space, newline"!
|
- Vector notation with comma as separator only works for vectors -- do not mix "semicolon, space, newline".
|
||||||
- Vectors, $1\!\times\!n$-matrices, and $n\!\times\!1$-matrices are three different things!
|
- Vectors, $1\!\times\!n$-matrices, and $n\!\times\!1$-matrices are three different things.
|
||||||
|
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
@@ -398,7 +412,7 @@ v = [[2,3,4], [5,6,7,8]]
|
|||||||
v[2][3]
|
v[2][3]
|
||||||
```
|
```
|
||||||
|
|
||||||
One should only do this in special cases. The array language of Julia is usually more convenient and faster.
|
You should only do this in special cases. The array notation in Julia is usually more convenient and faster.
|
||||||
|
|
||||||
### Indices, Subarrays, Slices
|
### Indices, Subarrays, Slices
|
||||||
|
|
||||||
@@ -415,7 +429,7 @@ A[2, 3] = 77.77777
|
|||||||
A
|
A
|
||||||
```
|
```
|
||||||
|
|
||||||
One can use ranges to address subarrays:
|
You can use ranges to address subarrays:
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
B = A[1:2, 1:3]
|
B = A[1:2, 1:3]
|
||||||
@@ -436,10 +450,10 @@ C = A[:, 3]
|
|||||||
E = A[3, :]
|
E = A[3, :]
|
||||||
```
|
```
|
||||||
|
|
||||||
Of course, assignments are also possible:
|
Slicing can also used on the right hand side for assignments:
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
# One can also assign to slices and subarrays
|
# You can also assign to slices and subarrays
|
||||||
|
|
||||||
A[2, :] = [1,2,3,4,5,6]
|
A[2, :] = [1,2,3,4,5,6]
|
||||||
A
|
A
|
||||||
@@ -473,7 +487,7 @@ B[3] = 300
|
|||||||
```
|
```
|
||||||
|
|
||||||
This behavior saves a lot of time and memory, but is not always desired.
|
This behavior saves a lot of time and memory, but is not always desired.
|
||||||
The function `copy()` creates a 'real' copy of the object.
|
The function `copy()` creates a true copy of the object.
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
A = [1, 2, 3]
|
A = [1, 2, 3]
|
||||||
@@ -483,7 +497,7 @@ A[1] = 100
|
|||||||
```
|
```
|
||||||
|
|
||||||
The function
|
The function
|
||||||
`deepcopy(A)` copies recursively. Copies are also created for the elements from which `A` consists.
|
`deepcopy(A)` copies recursively. It also creates copies of the elements that A contains.
|
||||||
|
|
||||||
As long as an array only contains primitive objects (numbers), `copy()` and `deepcopy()` are equivalent.
|
As long as an array only contains primitive objects (numbers), `copy()` and `deepcopy()` are equivalent.
|
||||||
|
|
||||||
@@ -510,7 +524,7 @@ A[3].age = 199
|
|||||||
### Views
|
### Views
|
||||||
|
|
||||||
When one assigns a piece of an array to a variable using *indices/ranges/slices*,
|
When one assigns a piece of an array to a variable using *indices/ranges/slices*,
|
||||||
Julia fundamentally **constructs a new object**.
|
Julia **constructs a new object**.
|
||||||
|
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
@@ -525,11 +539,11 @@ A[1, 2] = 77
|
|||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
Sometimes, however, one wants exactly this reference semantics in the sense of: "Vector `v` should be the 2nd column vector of `A` and should also remain so (i.e., change if `A` changes)."
|
Sometimes, however, one wants reference semantics in the sense of: "Vector `v` should be the 2nd column vector of `A` and should also remain so (i.e., change if `A` changes)."
|
||||||
|
|
||||||
This is called *views* in Julia: We want the variable `v` to represent only an 'alternative view' of the matrix `A`.
|
This is called a *view* in Julia: We want the variable `v` to represent only an 'alternative view' of the matrix `A`.
|
||||||
|
|
||||||
This can be achieved with the `@view` macro:
|
It can be achieved with the `@view` macro:
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
A = [1 2 3
|
A = [1 2 3
|
||||||
@@ -549,9 +563,8 @@ An example is the operator `'`, which delivers the adjoint matrix `A'` to a matr
|
|||||||
- The adjoint matrix `A'` is the transposed and element-wise complex-conjugated matrix to `A`.
|
- The adjoint matrix `A'` is the transposed and element-wise complex-conjugated matrix to `A`.
|
||||||
- The parser converts this to the function call `adjoint(A)`.
|
- The parser converts this to the function call `adjoint(A)`.
|
||||||
- For real matrices, the adjoint is equal to the transposed matrix.
|
- For real matrices, the adjoint is equal to the transposed matrix.
|
||||||
- Julia implements `adjoint()` as a _lazy function_, i.e.,
|
- Julia implements `adjoint()` as a _lazy function_, i.e., for efficiency reasons no new object is constructed. The method provides an alternative 'view' of the matrix (with swapped indices) and an alternative 'view' of the entries (with sign change in the imaginary part).
|
||||||
- for efficiency reasons, no new object is constructed, but only an alternative 'view' of the matrix ("with swapped indices") and an alternative 'view' of the entries (with sign change in the imaginary part).
|
- The adjoint of a vector produces a $1\!\times\!n$ matrix (row vector).
|
||||||
- From vectors, `adjoint()` makes a $1\!\times\!n$-matrix (a row vector).
|
|
||||||
|
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
@@ -560,7 +573,7 @@ A = [ 1. 2.
|
|||||||
B = A'
|
B = A'
|
||||||
```
|
```
|
||||||
|
|
||||||
The matrix `B` is only a modified 'view' of `A`:
|
The matrix `B` is just a modified 'view' of `A`:
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
A[1, 2] =10
|
A[1, 2] =10
|
||||||
@@ -576,10 +589,10 @@ v = [1, 2, 3]
|
|||||||
v'
|
v'
|
||||||
```
|
```
|
||||||
|
|
||||||
Another such function, which provides an alternative 'view', a different indexing, of the same data
|
Another such function, which provides an alternative 'view', a different indexing of the same data
|
||||||
is `reshape()`.
|
is `reshape()`.
|
||||||
|
|
||||||
Here, a vector with 12 entries is transformed into a 3x4 matrix.
|
Here, a vector with 12 entries is transformed into a 3x4 matrix:
|
||||||
```{julia}
|
```{julia}
|
||||||
A = [1,2,3,4,5,6,7,8,9,10,11,12]
|
A = [1,2,3,4,5,6,7,8,9,10,11,12]
|
||||||
|
|
||||||
@@ -635,22 +648,23 @@ using BenchmarkTools
|
|||||||
|
|
||||||
### Locality of Memory Access and _Caching_
|
### Locality of Memory Access and _Caching_
|
||||||
|
|
||||||
We have seen that the order of inner and outer loops makes a significant speed difference:
|
We have observed that the order of inner and outer loops significantly affects computational efficiency:
|
||||||
|
|
||||||
__It is more efficient when the innermost loop traverses the left index__, i.e., a column and not a row. The cause of this lies in the architecture of modern processors.
|
It is more efficient when the innermost loop iterates over the left index, i.e., a column rather than a row. This is due to the architecture of modern processors.
|
||||||
|
|
||||||
|
- Memory access involves multiple cache levels.
|
||||||
|
- A _cache miss_, which necessitates reloading from slower caches, slows down processing.
|
||||||
|
- To minimize the frequency of _cache misses_, processors reload larger memory blocks.
|
||||||
|
- Consequently, it is crucial to organize memory access as locally as possible.
|
||||||
|
|
||||||
- Memory access occurs over multiple cache levels.
|
|
||||||
- A _cache miss_, which triggers a reload from slower caches, slows down the process.
|
|
||||||
- Larger memory blocks are always reloaded to minimize the frequency of _cache misses_.
|
|
||||||
- Therefore, it is important to organize memory access as locally as possible.
|
|
||||||
|
|
||||||
::: {.content-visible when-format="html"}
|
::: {.content-visible when-format="html"}
|
||||||
](../images/cache.png){width="75%"}
|
](../images/cache.png){width="75%"}
|
||||||
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||
::: {.content-visible when-format="pdf"}
|
::: {.content-visible when-format="typst"}
|
||||||
](../images/cache.png){width="70%"}
|
](../images/cache.png){width="70%"}
|
||||||
:::
|
:::
|
||||||
|
|
||||||
|
|
||||||
@@ -739,13 +753,12 @@ v * w'
|
|||||||
|
|
||||||
## Broadcasting {#sec-broadcast}
|
## Broadcasting {#sec-broadcast}
|
||||||
|
|
||||||
- With _broadcasting_, operations or functions are applied __element-wise__ to arrays.
|
- With _broadcasting_, operations or functions are applied element-wise to arrays.
|
||||||
- The syntax for this is a dot _before_ an operator or _after_ a function name.
|
- Broadcasting is indicated by a dot preceding an operator or following a function name.
|
||||||
- The parser converts `f.(x,y)` to `broadcast(f, x, y)` and similarly for operators `x .⊙ y` to `broadcast(⊙, z, y)`.
|
- The parser translates `f.(x,y)` into `broadcast(f, x, y)`, and similarly, `x .⊙ y` into `broadcast(⊙, x, y)`.
|
||||||
- Operands that are missing one or more dimensions are virtually replicated in those dimensions.
|
- Operands lacking one or more dimensions are virtually replicated in those dimensions.
|
||||||
- The *broadcasting* of assignments `.=`, `.+=`,... changes the semantics. No new object is created, but the values are entered into the object on the left side (which must have the correct dimension).
|
- Broadcasting assignments such as `.=`, `.+=`, etc., alter the semantics by modifying values directly within the left-side object (which must have the appropriate dimensions) without creating a new object.
|
||||||
|
- With _broadcasting_, operations or functions are applied element-wise to arrays.
|
||||||
|
|
||||||
|
|
||||||
Some examples:
|
Some examples:
|
||||||
|
|
||||||
@@ -767,7 +780,7 @@ sqrt.(A)
|
|||||||
A.^2
|
A.^2
|
||||||
```
|
```
|
||||||
|
|
||||||
- For comparison, the result of the algebraic operations:
|
- For comparison, here the results of the algebraic operations:
|
||||||
```{julia}
|
```{julia}
|
||||||
@show A^2 A^(1/2);
|
@show A^2 A^(1/2);
|
||||||
```
|
```
|
||||||
@@ -784,10 +797,9 @@ hyp.(A, B)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
When operands have different dimensions, the operand with missing dimensions is virtually
|
When operands possess differing dimensions, the operand with fewer dimensions is effectively expanded through replication along those dimensions.
|
||||||
'grown' by replication in these dimensions.
|
|
||||||
|
|
||||||
We add a scalar to a matrix:
|
Adding a scalar to a matrix:
|
||||||
```{julia}
|
```{julia}
|
||||||
A = [ 1 2 3
|
A = [ 1 2 3
|
||||||
4 5 6]
|
4 5 6]
|
||||||
@@ -798,15 +810,12 @@ A = [ 1 2 3
|
|||||||
A .+ 300
|
A .+ 300
|
||||||
```
|
```
|
||||||
|
|
||||||
The scalar was replicated to the same dimension as the matrix by replication. We let `broadcast()` show the form of the 2nd operand after *broadcasting*:
|
The scalar was replicated to match the matrix dimensions. Let `broadcast()` illustrate the form of the second operand after broadcasting:
|
||||||
|
```
|
||||||
```{julia}
|
|
||||||
broadcast( (x,y) -> y, A, 300)
|
broadcast( (x,y) -> y, A, 300)
|
||||||
```
|
```
|
||||||
|
|
||||||
(Of course, this replication only takes place virtually. This object is not really created for other operations.)
|
(This replication occurs solely in a virtual sense; the object is not actually instantiated in memory.)
|
||||||
|
|
||||||
As another example: Matrix and (column-)vector
|
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
A .+ [10, 20]
|
A .+ [10, 20]
|
||||||
@@ -824,7 +833,7 @@ Matrix and row vector: The row vector is repeated row-wise:
|
|||||||
A .* [1,2,3]' # adjoint vector
|
A .* [1,2,3]' # adjoint vector
|
||||||
```
|
```
|
||||||
|
|
||||||
The 2nd operand is 'grown' by `broadcast()` through replication of rows.
|
The 2nd operand is grown by `broadcast()` through replication of rows.
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
broadcast((x,y)->y, A, [1,2,3]')
|
broadcast((x,y)->y, A, [1,2,3]')
|
||||||
@@ -833,11 +842,11 @@ broadcast((x,y)->y, A, [1,2,3]')
|
|||||||
|
|
||||||
#### _Broadcasting_ in Assignments
|
#### _Broadcasting_ in Assignments
|
||||||
|
|
||||||
Assignments `=`, `+=`, `/=`,..., where a name is on the left side, work in Julia such that an object is constructed from the right side and assigned this new name.
|
Assignments such as `=`, `+=`, `/=`,... in Julia involve constructing an object on the right-hand side and assigning it to a variable on the left-hand side.
|
||||||
|
|
||||||
However, when working with arrays, one often wants to reuse an existing array for efficiency reasons. The values calculated on the right side should be entered into the already existing object on the left side.
|
When working with arrays, efficiency often requires reusing existing array objects. The values computed on the right-hand side are then stored directly into the pre-existing object on the left-hand side.
|
||||||
|
|
||||||
This is achieved with the broadcast variants `.=`, `.+=`,... of the assignment operators.
|
This is accomplished using broadcast variants of assignment operators: `.=`, `.+=`,....
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
A .= 3
|
A .= 3
|
||||||
|
|||||||
@@ -18,33 +18,32 @@ Base.active_module() = myactive_module()
|
|||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
Functions process their arguments to produce a result, which they return when called.
|
Functions process their arguments to produce and return a result when called.
|
||||||
|
|
||||||
## Forms
|
## Forms of function definitions
|
||||||
|
|
||||||
Functions can be defined in different forms:
|
|
||||||
|
|
||||||
I. As a `function ... end` block
|
I. Block form: `function ... end`
|
||||||
```{julia}
|
```{julia}
|
||||||
function hyp(x,y)
|
function hyp(x,y)
|
||||||
sqrt(x^2+y^2)
|
sqrt(x^2+y^2)
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
II. As a "single-liner"
|
II. Single-line form
|
||||||
```{julia}
|
```{julia}
|
||||||
hyp(x, y) = sqrt(x^2 + y^2)
|
hyp(x, y) = sqrt(x^2 + y^2)
|
||||||
```
|
```
|
||||||
III. As anonymous functions
|
III. Anonymous functions
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
(x, y) -> sqrt(x^2 + y^2)
|
(x, y) -> sqrt(x^2 + y^2)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
### Block Form and `return`
|
### Block Form and `return` Statement
|
||||||
|
|
||||||
|
|
||||||
- With `return`, function execution is terminated and control returns to the calling context.
|
- With `return`, function execution terminates and control returns to the calling context.
|
||||||
- Without `return`, the value of the last expression is returned as the function value.
|
- Without `return`, the value of the last expression is returned as the function value.
|
||||||
|
|
||||||
The two definitions
|
The two definitions
|
||||||
@@ -57,7 +56,7 @@ function xsinrecipx(x)
|
|||||||
return x * sin(1/x)
|
return x * sin(1/x)
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
and without the second explicit `return` in the last line:
|
and the equivalent version without explicit `return` in the last line:
|
||||||
|
|
||||||
```julia
|
```julia
|
||||||
function xsinrecipx(x)
|
function xsinrecipx(x)
|
||||||
@@ -71,7 +70,7 @@ end
|
|||||||
are therefore equivalent.
|
are therefore equivalent.
|
||||||
|
|
||||||
|
|
||||||
- A function that returns "nothing" (_void functions_ in the C world) returns the value `nothing` of type `Nothing`. (Just as an object of type `Bool` can have two values, `true` and `false`, an object of type `Nothing` can only take a single value, namely `nothing`.)
|
- A function that returns `nothing` (_void functions_ in C) returns a `nothing` value of type `Nothing`. (Just as a `Bool` object has two values, `true` and `false`, a `Nothing` object has only one: `nothing`.)
|
||||||
- An empty `return` statement is equivalent to `return nothing`.
|
- An empty `return` statement is equivalent to `return nothing`.
|
||||||
|
|
||||||
|
|
||||||
@@ -97,13 +96,13 @@ a
|
|||||||
|
|
||||||
### Single-liner Form
|
### Single-liner Form
|
||||||
|
|
||||||
The single-liner form is a normal assignment where a function stands on the left side.
|
The single-liner form looks like a simple assignment:
|
||||||
|
|
||||||
```julia
|
```julia
|
||||||
hyp(x, y) = sqrt(x^2 + y^2)
|
hyp(x, y) = sqrt(x^2 + y^2)
|
||||||
```
|
```
|
||||||
|
|
||||||
Julia knows two ways to combine multiple statements into a block that can stand in place of a single statement:
|
Julia provides two ways to combine multiple statements into a block that can stand in place of a single statement:
|
||||||
|
|
||||||
- `begin ... end` block
|
- `begin ... end` block
|
||||||
- Parenthesized statements separated by semicolons.
|
- Parenthesized statements separated by semicolons.
|
||||||
@@ -126,13 +125,13 @@ hyp(x, y) = begin
|
|||||||
|
|
||||||
### Anonymous Functions
|
### Anonymous Functions
|
||||||
|
|
||||||
Anonymous functions can be "rescued from anonymity" by assigning them a name.
|
Anonymous functions can be "rescued from anonymity" by assigning them a name:
|
||||||
```julia
|
```julia
|
||||||
hyp = (x,y) -> sqrt(x^2 + y^2)
|
hyp = (x,y) -> sqrt(x^2 + y^2)
|
||||||
```
|
```
|
||||||
Their actual application is in calling a *(higher order)* function that expects a function as an argument.
|
Their actual application is in calling a *(higher order)* function that expects a function as an argument.
|
||||||
|
|
||||||
Typical applications are `map(f, collection)`, which applies a function to all elements of a collection. In Julia, `map(f(x,y), collection1, collection2)` also works:
|
Typical applications include `map(f, collection)`, which applies a function to every element of a collection. Julia also supports `map(f, collection1, collection2)` with multiple collections:
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
map( (x,y) -> sqrt(x^2 + y^2), [3, 5, 8], [4, 12, 15])
|
map( (x,y) -> sqrt(x^2 + y^2), [3, 5, 8], [4, 12, 15])
|
||||||
@@ -150,15 +149,15 @@ filter(x -> ( x%3 == 0 && x%5 == 0), 1:100 )
|
|||||||
## Argument Passing
|
## Argument Passing
|
||||||
|
|
||||||
|
|
||||||
- When calling a function, no copies are made of the objects passed as function arguments. Variables in the function refer to the original objects. Julia calls this concept _pass_by_sharing_.
|
- When calling a function, Julia does not copy objects passed as arguments. Function arguments refer to the original objects. Julia calls this concept _pass_by_sharing_.
|
||||||
- Functions can therefore effectively modify their arguments if they are _mutable_ objects, such as `Vector` or `Array`.
|
- Consequently, functions can modify their arguments if they are mutable (e.g., `Vector` or `Array`).
|
||||||
- It is a convention in Julia that the names of such argument-modifying functions end with an exclamation mark. Furthermore, the argument that is modified is usually first and is also the return value of the function.
|
- By convention, functions that modify their arguments end with an exclamation mark. The modified argument is typically the first argument and is also returned.
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
V = [1, 2, 3]
|
V = [1, 2, 3]
|
||||||
|
|
||||||
W = fill!(V, 17)
|
W = fill!(V, 17)
|
||||||
# '===' is test for identity
|
# '===' tests for identity
|
||||||
@show V W V===W; # V and W refer to the same object
|
@show V W V===W; # V and W refer to the same object
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -175,16 +174,16 @@ U = fill_first!(V, 42)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Variants of Function Arguments
|
## Function Argument Variants
|
||||||
|
|
||||||
- There are positional arguments (1st argument, 2nd argument, ...) and _keyword_ arguments, which must be addressed by name when calling.
|
- There are positional arguments (1st argument, 2nd argument, ...) and _keyword_ arguments, which must be addressed by name when calling.
|
||||||
- Both positional and _keyword_ arguments can have _default_ values. These arguments can be omitted when calling.
|
- Both positional and _keyword_ arguments can have _default_ values. These arguments can be omitted when calling.
|
||||||
- The order of declaration must be:
|
- The order of declaration must be:
|
||||||
1. Positional arguments without default value,
|
1. Positional arguments without default values,
|
||||||
2. Positional arguments with default value,
|
2. Positional arguments with default values,
|
||||||
3. --- semicolon ---,
|
3. --- semicolon ---,
|
||||||
4. comma-separated list of keyword arguments (with or without default value)
|
4. comma-separated list of keyword arguments (with or without default values)
|
||||||
- When calling, _keyword_ arguments can appear at any position in any order. You can separate them from positional arguments with a semicolon, but you don't have to.
|
- When calling, keyword arguments can appear in any order at any position. They can be separated from positional arguments with a semicolon, but this is optional.
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
fa(x, y=42; a) = println("x=$x, y=$y, a=$a")
|
fa(x, y=42; a) = println("x=$x, y=$y, a=$a")
|
||||||
@@ -204,9 +203,9 @@ fkw(y=2)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Functions are Normal Objects
|
## Functions are just Objects
|
||||||
|
|
||||||
- They can be assigned
|
- Functions can be assigned to variables
|
||||||
|
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
@@ -215,11 +214,11 @@ f2(2)
|
|||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
- They can be passed as arguments to functions.
|
- Functions can be passed as arguments to other functions.
|
||||||
|
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
# very naive numerical integration
|
# naive Riemann integration example
|
||||||
|
|
||||||
function Riemann_integrate(f, a, b; NInter=1000)
|
function Riemann_integrate(f, a, b; NInter=1000)
|
||||||
delta = (b-a)/NInter
|
delta = (b-a)/NInter
|
||||||
@@ -263,7 +262,7 @@ h(1)
|
|||||||
h(2), h(10)
|
h(2), h(10)
|
||||||
```
|
```
|
||||||
|
|
||||||
The above function `generate_add_func()` can also be defined more briefly. The inner function name `addx()` is anyway local and not available outside. So one can use an anonymous function.
|
The above function `generate_add_func()` can also be defined more briefly. The inner function name `addx` is local and inaccessible outside. An anonymous function can be used instead.
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
generate_add_func(x) = y -> x + y
|
generate_add_func(x) = y -> x + y
|
||||||
@@ -306,7 +305,7 @@ f(.2)
|
|||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
- Of course, you can also 'broadcast' these operators (see @sec-broadcast). Here a vector of functions acts element-wise on a vector of arguments:
|
- These operators can also be broadcast (see @sec-broadcast). A vector of functions is applied element-wise to a vector of arguments:
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
["a", "list", "of", "strings"] .|> [length, uppercase, reverse, titlecase]
|
["a", "list", "of", "strings"] .|> [length, uppercase, reverse, titlecase]
|
||||||
@@ -316,9 +315,9 @@ f(.2)
|
|||||||
|
|
||||||
A syntactic peculiarity for defining anonymous functions as arguments of other functions is the `do` notation.
|
A syntactic peculiarity for defining anonymous functions as arguments of other functions is the `do` notation.
|
||||||
|
|
||||||
Let `higherfunc(f,a,...)` be a function whose first argument is a function.
|
Let `higherfunc(f, a, ...)` be a function whose first argument is a function.
|
||||||
|
|
||||||
Then you can also call `higherfunc()` without the first argument and instead define the function in an immediately following `do` block:
|
The function can be called without the first argument, with the function body defined in a following `do` block:
|
||||||
|
|
||||||
```julia
|
```julia
|
||||||
higherfunc(a, b) do x, y
|
higherfunc(a, b) do x, y
|
||||||
@@ -336,7 +335,7 @@ Riemann_integrate(0, 2) do x x^2 end
|
|||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
The purpose is of course in the application with more complex functions, such as this integrand composed of two parts:
|
The `do` notation is especially useful for complex function bodies, such as this integrand defined in multiple steps:
|
||||||
```{julia}
|
```{julia}
|
||||||
r = Riemann_integrate(0, π) do x
|
r = Riemann_integrate(0, π) do x
|
||||||
z1 = sin(x)
|
z1 = sin(x)
|
||||||
@@ -351,10 +350,10 @@ r = Riemann_integrate(0, π) do x
|
|||||||
|
|
||||||
## Function-like Objects
|
## Function-like Objects
|
||||||
|
|
||||||
By defining a suitable method for a type, arbitrary objects can be made *callable*, i.e., callable like functions afterwards.
|
By defining a method for a type, objects become *callable* like functions.
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
# struct stores the coefficients of a 2nd degree polynomial
|
# struct stores coefficients of a second-degree polynomial
|
||||||
struct Poly2Grad
|
struct Poly2Grad
|
||||||
a0::Float64
|
a0::Float64
|
||||||
a1::Float64
|
a1::Float64
|
||||||
@@ -365,13 +364,13 @@ p1 = Poly2Grad(2,5,1)
|
|||||||
p2 = Poly2Grad(3,1,-0.4)
|
p2 = Poly2Grad(3,1,-0.4)
|
||||||
```
|
```
|
||||||
|
|
||||||
The following method makes this structure `callable`.
|
The following method makes this structure callable:
|
||||||
```{julia}
|
```{julia}
|
||||||
function (p::Poly2Grad)(x)
|
function (p::Poly2Grad)(x)
|
||||||
p.a2 * x^2 + p.a1 * x + p.a0
|
p.a2 * x^2 + p.a1 * x + p.a0
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
Now the objects can, if desired, also be used like functions.
|
Objects can now be used like functions:
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
@show p2(5) p1(-0.7) p1;
|
@show p2(5) p1(-0.7) p1;
|
||||||
@@ -381,7 +380,7 @@ Now the objects can, if desired, also be used like functions.
|
|||||||
|
|
||||||
## Operators and Special Forms
|
## Operators and Special Forms
|
||||||
|
|
||||||
- Infix operators like `+,*,>,∈,...` are functions.
|
- Infix operators such as `+`, `*`, `>`, `∈` are functions.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -399,7 +398,7 @@ f = +
|
|||||||
f(3, 7)
|
f(3, 7)
|
||||||
```
|
```
|
||||||
|
|
||||||
- Even constructions like `x[i]`, `a.x`, `[x; y]` are converted by the parser to function calls.
|
- Constructions like `x[i]`, `a.x`, `[x; y]` are converted by the parser to function calls.
|
||||||
|
|
||||||
:::{.narrow}
|
:::{.narrow}
|
||||||
|
|
||||||
@@ -417,7 +416,7 @@ f(3, 7)
|
|||||||
(The colon before a variable makes it into a symbol.)
|
(The colon before a variable makes it into a symbol.)
|
||||||
|
|
||||||
:::{.callout-note}
|
:::{.callout-note}
|
||||||
For these functions, one can implement one's own methods. For example, for a custom type, setting a field (`setproperty!()`) could check the validity of the value or trigger further actions.
|
For these functions, too, van be extended/overwritten by new methods. For example, for a custom type, setting a field (`setproperty!()`) could check the validity of the value or trigger further actions.
|
||||||
|
|
||||||
In principle, `get/setproperty` can also do things that have nothing to do with an actually existing field of the structure.
|
In principle, `get/setproperty` can also do things that have nothing to do with an actually existing field of the structure.
|
||||||
:::
|
:::
|
||||||
@@ -433,15 +432,15 @@ can also be written as
|
|||||||
x ⊙= y
|
x ⊙= y
|
||||||
```
|
```
|
||||||
|
|
||||||
Both forms are semantically equivalent. In particular, in both forms, a new object created on the right side is assigned to the variable `x`.
|
Both forms are semantically equivalent: a new object created on the right is assigned to `x`.
|
||||||
|
|
||||||
Memory- and time-saving __in-place-update__ of an array/vector/matrix is possible either through explicit indexing
|
Memory- and time-efficient *in-place updates* of arrays use explicit indexing:
|
||||||
```julia
|
```julia
|
||||||
for i in eachindex(x)
|
for i in eachindex(x)
|
||||||
x[i] += y[i]
|
x[i] += y[i]
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
or through the semantically equivalent _broadcast_ form (see @sec-broadcast):
|
or semantically equivalent broadcast form (see @sec-broadcast):
|
||||||
```julia
|
```julia
|
||||||
x .= x .+ y
|
x .= x .+ y
|
||||||
```
|
```
|
||||||
@@ -450,13 +449,13 @@ x .= x .+ y
|
|||||||
|
|
||||||
## Operator Precedence and Associativity {#sec-vorrang}
|
## Operator Precedence and Associativity {#sec-vorrang}
|
||||||
|
|
||||||
Expressions to be computed
|
Expressions like
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
-2^3+500/2/10==8 && 13 > 7 + 1 || 9 < 2
|
-2^3+500/2/10==8 && 13 > 7 + 1 || 9 < 2
|
||||||
```
|
```
|
||||||
|
|
||||||
are converted by the parser into a tree structure.
|
are converted by the parser into a tree structure:
|
||||||
```{julia}
|
```{julia}
|
||||||
using TreeView
|
using TreeView
|
||||||
|
|
||||||
@@ -464,16 +463,16 @@ walk_tree(Meta.parse("-2^3+500/2/10==8 && 13 > 7 + 1 || 9 < 2"))
|
|||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
- The evaluation of such expressions is regulated by
|
- Expression evaluation is governed by
|
||||||
- precedence and
|
- precedence and
|
||||||
- associativity.
|
- associativity.
|
||||||
- 'Precedence' defines which operators bind more strongly in the sense of "multiplication and division before addition and subtraction".
|
- Precedence determines which operators bind more tightly, such as multiplication before addition.
|
||||||
- 'Associativity' determines the evaluation order for operators of equal or same rank.
|
- Associativity determines the evaluation order for operators of equal precedence.
|
||||||
- [Complete documentation](https://docs.julialang.org/en/v1/manual/mathematical-operations/#Operator-Precedence-and-Associativity)
|
- [Complete documentation](https://docs.julialang.org/en/v1/manual/mathematical-operations/#Operator-Precedence-and-Associativity)
|
||||||
|
|
||||||
### Associativity
|
### Associativity
|
||||||
|
|
||||||
Both addition and subtraction as well as multiplication and division are each of equal rank and left-associative, i.e., they are evaluated from left to right.
|
Addition/subtraction and multiplication/division have equal precedence and are left-associative (evaluated left-to-right):
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
200/5/2 # evaluated left to right as (200/5)/2
|
200/5/2 # evaluated left to right as (200/5)/2
|
||||||
@@ -497,7 +496,7 @@ x += y += z = a = 20
|
|||||||
@show x y z a;
|
@show x y z a;
|
||||||
```
|
```
|
||||||
|
|
||||||
Of course, you can also query the associativity in Julia. The corresponding functions are not explicitly exported from the `Base` module, so the module name must be specified when calling.
|
Julia provides functions to query associativity. These functions are not exported from `Base`, so the module name must be specified.
|
||||||
|
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
@@ -507,7 +506,7 @@ for i in (:/, :+=, :(=), :^)
|
|||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
So the power operator is right-associative.
|
Thus, the power operator is right-associative:
|
||||||
```{julia}
|
```{julia}
|
||||||
2^3^2 # right-associative, = 2^(3^2)
|
2^3^2 # right-associative, = 2^(3^2)
|
||||||
```
|
```
|
||||||
@@ -526,9 +525,9 @@ end
|
|||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
- 11 is less than 12, so 'multiplication and division before addition and subtraction'
|
- Precedence 11 < 12 explains why multiplication/division bind tighter than addition/subtraction.
|
||||||
- The power operator `^` has a higher _precedence_.
|
- The power operator `^` has higher precedence.
|
||||||
- Assignments have the smallest _precedence_
|
- Assignments have the lowest precedence.
|
||||||
|
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
@@ -540,11 +539,11 @@ x
|
|||||||
|
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
(y = 3) < 4 # parentheses当然 override any precedence
|
(y = 3) < 4 # parentheses override any precedence
|
||||||
y
|
y
|
||||||
```
|
```
|
||||||
|
|
||||||
Once more to the example from the beginning of @sec-vorrang:
|
Returning to the example above:
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
-2^3+500/2/10==8 && 13 > 7 + 1 || 9 < 2
|
-2^3+500/2/10==8 && 13 > 7 + 1 || 9 < 2
|
||||||
@@ -557,12 +556,12 @@ for i ∈ (:^, :+, :/, :(==), :&&, :>, :|| )
|
|||||||
println(Base.operator_precedence(i))
|
println(Base.operator_precedence(i))
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
According to these precedence rules, the example expression is evaluated as follows:
|
These rules evaluate the expression as:
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
((-(2^3)+((500/2)/10)==8) && (13 > (7 + 1))) || (9 < 2)
|
((-(2^3)+((500/2)/10)==8) && (13 > (7 + 1))) || (9 < 2)
|
||||||
```
|
```
|
||||||
(This of course corresponds to the *parse-tree* shown above)
|
(as shown in the parse tree above).
|
||||||
|
|
||||||
So the precedence is:
|
So the precedence is:
|
||||||
|
|
||||||
|
|||||||
@@ -11,22 +11,22 @@ julia:
|
|||||||
using InteractiveUtils
|
using InteractiveUtils
|
||||||
```
|
```
|
||||||
|
|
||||||
# A small Example Program
|
# A Small Example Program
|
||||||
|
|
||||||
## What Skills Are Needed for Programming?
|
## What Skills Are Needed for Programming?
|
||||||
|
|
||||||
|
|
||||||
- **Thinking in algorithms:**
|
- **Thinking in algorithms:**
|
||||||
What steps are required to solve the problem? What data must be stored, what data structures must be created? What cases can occur and must be recognized and handled?
|
What steps are required to solve the problem? What data must be stored, what data structures must be created? What cases can occur and must be recognized and handled?
|
||||||
- **Implementation of the algorithm into a program:**
|
- **Implementing the algorithm in a program:**
|
||||||
What data structures, constructs, functions... does the programming language provide?
|
What data structures, constructs, functions... does the programming language provide?
|
||||||
- **Formal syntax:**
|
- **Formal syntax:**
|
||||||
Humans understand 'broken English'; computers don't understand 'broken C++' or 'broken Julia'. The syntax must be mastered.
|
Humans understand 'broken English'; computers don't understand 'broken C++' or 'broken Julia'. The syntax must be mastered.
|
||||||
- **The "ecosystem" of the language:**
|
- **The "ecosystem" of the language:**
|
||||||
How do I run my code? How do the development environments work? What options does the compiler understand? How do I install additional libraries? How do I read error messages? Where can I get information?
|
How do I run my code? How do the development environments work? What options does the compiler understand? How do I install additional libraries? How do I read error messages? Where can I get information?
|
||||||
- **The art of debugging:**
|
- **The art of debugging:**
|
||||||
Programming beginners are often happy when they have eliminated all syntax errors and the program finally 'runs'. For more complex programs, the work only just begins, as testing and finding and fixing errors in the algorithm must now be done.
|
Programming beginners are often happy when they have eliminated all syntax errors and the program finally 'runs'. For more complex programs, the work only just begins, as testing, finding, and fixing errors in the algorithm must now be done.
|
||||||
- **Sense of the efficiency and complexity of algorithms**
|
- **Intuition for the efficiency and complexity of algorithms**
|
||||||
- **Specifics of computer arithmetic**, especially floating point numbers
|
- **Specifics of computer arithmetic**, especially floating point numbers
|
||||||
|
|
||||||
These cannot all be mastered at once. Be patient with yourself and 'play' with the language.
|
These cannot all be mastered at once. Be patient with yourself and 'play' with the language.
|
||||||
@@ -81,7 +81,7 @@ for i in 1:999 # loop
|
|||||||
s += i # add to accumulator
|
s += i # add to accumulator
|
||||||
end # end test
|
end # end test
|
||||||
end # end loop
|
end # end loop
|
||||||
println(" The answer is: $s") # output result
|
println("The answer is: $s") # output result
|
||||||
```
|
```
|
||||||
:::
|
:::
|
||||||
|
|
||||||
@@ -118,21 +118,21 @@ How many starting numbers below ten million will arrive at 89?
|
|||||||
:::
|
:::
|
||||||
|
|
||||||
Programs can be developed [*top-down* and *bottom-up*](https://en.wikipedia.org/wiki/Top-down_and_bottom-up_design).
|
Programs can be developed [*top-down* and *bottom-up*](https://en.wikipedia.org/wiki/Top-down_and_bottom-up_design).
|
||||||
*Top-down* would probably mean here: "We start with a loop `for i = 1:9999999`." The other way leads over individually testable components and is often more effective. (And with a certain project size, one approaches the goal from both 'top' and 'bottom' simultaneously.)
|
*Top-down* would probably mean here: "We start with a loop `for i = 1:9999999`." The other approach works through individually testable components and is often more effective. (And with a certain project size, one approaches the goal from both 'top' and 'bottom' simultaneously.)
|
||||||
|
|
||||||
##### Function No. 1
|
##### Function No. 1
|
||||||
It should be investigated how numbers behave under repeated calculation of the 'square digit sum' (sum of squares of digits). Therefore, one should write and test a function that calculates this 'square digit sum'.
|
We want to investigate how numbers behave under repeated calculation of the 'square digit sum' (sum of squares of digits). Therefore, write and test a function that calculates this 'square digit sum'.
|
||||||
|
|
||||||
|
|
||||||
:::{.callout-caution icon="false" collapse="true" .titlenormal}
|
:::{.callout-caution icon="false" collapse="true" .titlenormal}
|
||||||
|
|
||||||
## `q2sum(n)` calculates the sum of the squares of the digits of n in the decimal system:
|
## `q2sum(n)` calculates the sum of the squares of the digits of n in base 10:
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
#| output: false
|
#| output: false
|
||||||
function q2sum(n)
|
function q2sum(n)
|
||||||
s = 0 # accumulator for the sum
|
s = 0 # accumulator for the sum
|
||||||
while n > 9 # as long as still multi-digit...
|
while n > 9 # as long as n is still multi-digit...
|
||||||
q, r = divrem(n, 10) # calculate integer quotient and remainder of division by 10
|
q, r = divrem(n, 10) # calculate integer quotient and remainder of division by 10
|
||||||
s += r^2 # add squared digit to accumulator
|
s += r^2 # add squared digit to accumulator
|
||||||
n = q # continue with the number divided by 10
|
n = q # continue with the number divided by 10
|
||||||
@@ -143,7 +143,7 @@ end
|
|||||||
```
|
```
|
||||||
|
|
||||||
:::
|
:::
|
||||||
... and let's test it now:
|
Let's test it now:
|
||||||
```{julia}
|
```{julia}
|
||||||
for i in [1, 7, 33, 111, 321, 1000, 73201]
|
for i in [1, 7, 33, 111, 321, 1000, 73201]
|
||||||
j = q2sum(i)
|
j = q2sum(i)
|
||||||
@@ -160,10 +160,10 @@ for i in 1:14
|
|||||||
println(n)
|
println(n)
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
... and we have here hit one of the '89-cycles'.
|
... and we have now hit one of the '89-cycles'.
|
||||||
|
|
||||||
|
|
||||||
#### Function No. 2
|
##### Function No. 2
|
||||||
|
|
||||||
We need to test whether repeated application of the function `q2sum()` finally leads to **1** or ends in this **89**-cycle.
|
We need to test whether repeated application of the function `q2sum()` finally leads to **1** or ends in this **89**-cycle.
|
||||||
|
|
||||||
@@ -174,11 +174,11 @@ We need to test whether repeated application of the function `q2sum()` finally l
|
|||||||
```{julia}
|
```{julia}
|
||||||
#| output: false
|
#| output: false
|
||||||
function q2test(n)
|
function q2test(n)
|
||||||
while n !=1 && n != 89 # as long as we have not reached 1 or 89....
|
while n !=1 && n != 89 # as long as we have not reached 1 or 89...
|
||||||
n = q2sum(n) # repeat
|
n = q2sum(n) # repeat
|
||||||
end
|
end
|
||||||
if n==1 return false end # not an 89-number
|
if n == 1 return false end # not an 89 cycle
|
||||||
return true # 89-number found
|
return true # 89 cycle found
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -188,7 +188,7 @@ end
|
|||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
c = 0 # once again an accumulator
|
c = 0 # once again an accumulator
|
||||||
for i = 1 : 10_000_000 - 1 # this is how you can separate thousands for better readability in Julia
|
for i = 1 : 10_000_000 - 1 # this is how you can separate thousands for better readability
|
||||||
if q2test(i) # q2test() returns a boolean, no further test needed
|
if q2test(i) # q2test() returns a boolean, no further test needed
|
||||||
c += 1 # 'if x == true' is redundant, 'if x' is perfectly sufficient
|
c += 1 # 'if x == true' is redundant, 'if x' is perfectly sufficient
|
||||||
end
|
end
|
||||||
@@ -201,7 +201,7 @@ Numbers for which the iterative square digit sum calculation ends at 1 are calle
|
|||||||
Here is the complete program again:
|
Here is the complete program again:
|
||||||
|
|
||||||
:::{.callout-caution icon="false" collapse="true" .titlenormal}
|
:::{.callout-caution icon="false" collapse="true" .titlenormal}
|
||||||
## our solution to Project Euler No 92:
|
## Our solution to Project Euler No 92:
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
function q2sum(n)
|
function q2sum(n)
|
||||||
|
|||||||
@@ -16,13 +16,15 @@ using InteractiveUtils
|
|||||||
flush(stdout)
|
flush(stdout)
|
||||||
```
|
```
|
||||||
|
|
||||||
# A Case Study in Floating-Point Arithmetic Stability
|
# A Case Study on Floating-Point Arithmetic Stability
|
||||||
|
|
||||||
|
This chapter is inspired by the example in *Christoph Überhuber*, "Computer-Numerik" Vol. 1, Springer 1995, Chap. 2.3.
|
||||||
|
|
||||||
## Calculation of $\pi$ According to Archimedes
|
## Calculation of $\pi$ According to Archimedes
|
||||||
|
|
||||||
A lower bound for $2\pi$—the circumference of the unit circle—is obtained by summing
|
A lower bound for $2\pi$ -- the circumference of the unit circle -- is obtained by summing
|
||||||
the side lengths of a regular $n$-gon inscribed in the unit circle.
|
the side lengths of a regular $n$-gon inscribed in the unit circle.
|
||||||
The figure on the left shows how one can iteratively double the number of vertices starting from a square with side length $$s_4=\sqrt{2}$$.
|
The figure on the left illustrates the iterative doubling of the number of vertices starting from a square with side length $$s_4=\sqrt{2}$$.
|
||||||
|
|
||||||
:::{.narrow}
|
:::{.narrow}
|
||||||
|
|
||||||
@@ -39,7 +41,7 @@ With
|
|||||||
$|\overline{AC}|= s_{n},\quad |\overline{AB}|= |\overline{BC}|= s_{2n},\quad |\overline{MN}| =a, \quad |\overline{NB}| =1-a,$ the Pythagorean theorem applied to triangles $MNA$ and
|
$|\overline{AC}|= s_{n},\quad |\overline{AB}|= |\overline{BC}|= s_{2n},\quad |\overline{MN}| =a, \quad |\overline{NB}| =1-a,$ the Pythagorean theorem applied to triangles $MNA$ and
|
||||||
$NBA$ respectively yields
|
$NBA$ respectively yields
|
||||||
$$
|
$$
|
||||||
a^2 + \left(\frac{s_{n}}{2}\right)^2 = 1\qquad\text{resp.} \qquad
|
a^2 + \left(\frac{s_{n}}{2}\right)^2 = 1\qquad\text{and } \qquad
|
||||||
(1-a)^2 + \left(\frac{s_{n}}{2}\right)^2 = s_{2n}^2
|
(1-a)^2 + \left(\frac{s_{n}}{2}\right)^2 = s_{2n}^2
|
||||||
$$
|
$$
|
||||||
Elimination of $a$ gives the recursion
|
Elimination of $a$ gives the recursion
|
||||||
@@ -47,8 +49,7 @@ $$
|
|||||||
s_{2n} = \sqrt{2-\sqrt{4-s_n^2}} \qquad\text{with initial value}\qquad
|
s_{2n} = \sqrt{2-\sqrt{4-s_n^2}} \qquad\text{with initial value}\qquad
|
||||||
s_4=\sqrt{2}
|
s_4=\sqrt{2}
|
||||||
$$
|
$$
|
||||||
for the length $s_n$ of one side of the inscribed regular
|
for the length $s_n$ of one side of the inscribed regular $n$-gon.
|
||||||
$n$-gon.
|
|
||||||
|
|
||||||
The sequence $(n\cdot s_n)$ converges monotonically from below to the limit $2\pi$:
|
The sequence $(n\cdot s_n)$ converges monotonically from below to the limit $2\pi$:
|
||||||
$$
|
$$
|
||||||
@@ -59,7 +60,9 @@ $$
|
|||||||
\epsilon_n = \left| \frac{n\, s_n-2 \pi}{2\pi} \right|
|
\epsilon_n = \left| \frac{n\, s_n-2 \pi}{2\pi} \right|
|
||||||
$$
|
$$
|
||||||
|
|
||||||
## Two Iteration Formulas^[by Christoph Überhuber, "Computer-Numerik" Vol. 1, Springer 1995, Chap. 2.3]. The equation
|
## Two Iteration Formulas
|
||||||
|
|
||||||
|
The equation
|
||||||
$$
|
$$
|
||||||
s_{2n} = \sqrt{2-\sqrt{4-s_n^2}}\qquad \qquad \text{(Iteration A)}
|
s_{2n} = \sqrt{2-\sqrt{4-s_n^2}}\qquad \qquad \text{(Iteration A)}
|
||||||
$$
|
$$
|
||||||
@@ -68,9 +71,9 @@ $$
|
|||||||
s_{2n} = \frac{s_n}{\sqrt{2+\sqrt{4-s_n^2}}} \qquad \qquad \text{(Iteration B)}
|
s_{2n} = \frac{s_n}{\sqrt{2+\sqrt{4-s_n^2}}} \qquad \qquad \text{(Iteration B)}
|
||||||
$$
|
$$
|
||||||
|
|
||||||
(Please verify!)
|
|
||||||
|
|
||||||
However, Iteration A is ill-conditioned and numerically unstable, as the following code demonstrates. The output shows the respective approximation for $\pi$.
|
|
||||||
|
However, Iteration A is ill-conditioned and numerically unstable, as the following code demonstrates. The output shows the approximation for $\pi$ for both formulae iteration.
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
using Printf
|
using Printf
|
||||||
@@ -102,7 +105,7 @@ for i in 3:35
|
|||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
While Iteration B stabilizes at a value correct within machine precision, Iteration A becomes unstable. A plot of the relative errors $\epsilon_i$ confirms this:
|
While Iteration B stabilizes to a value accurate within machine precision, Iteration A is unstable and diverges. A plot of the relative errors $\epsilon_i$ confirms this:
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
using PlotlyJS
|
using PlotlyJS
|
||||||
@@ -132,8 +135,8 @@ Further iterations do not improve the result; it stabilizes at a relative error
|
|||||||
|
|
||||||
Iteration A is unstable; already at $i=16$, the relative error begins to grow again.
|
Iteration A is unstable; already at $i=16$, the relative error begins to grow again.
|
||||||
|
|
||||||
The cause is a typical cancellation effect. The side lengths $s_n$ become very small, very quickly. Thus
|
The cause is a typical numerical cancellation effect. The side lengths $s_n$ become very small very quickly. Thus
|
||||||
$a_n=\sqrt{4-s_n^2}$ is only slightly smaller than 2, and when computing $s_{2n}=\sqrt{2-a_n}$, a typical cancellation effect occurs.
|
$a_n=\sqrt{4-s_n^2}$ is only slightly smaller than 2, and computing $s_{2n}=\sqrt{2-a_n}$ leads to a catastrophic cancellation.
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
setprecision(80) # precision for BigFloat
|
setprecision(80) # precision for BigFloat
|
||||||
@@ -142,7 +145,7 @@ s = sqrt(BigFloat(2))
|
|||||||
|
|
||||||
@printf " a = √(4-s^2) as BigFloat and as Float64\n\n"
|
@printf " a = √(4-s^2) as BigFloat and as Float64\n\n"
|
||||||
|
|
||||||
for $$i=$ 3:44$
|
for i= 3:44
|
||||||
s = iterationA(s)
|
s = iterationA(s)
|
||||||
x = sqrt(4-s^2)
|
x = sqrt(4-s^2)
|
||||||
if i > 20
|
if i > 20
|
||||||
@@ -151,7 +154,8 @@ for $$i=$ 3:44$
|
|||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
One sees the decrease in the number of significant digits. One also sees that using `BigFloat` with a mantissa length of 80 bits only slightly delays the onset of the cancellation effect.
|
This demonstrates the loss of significant digits. It also shows that using `BigFloat` with 80 bits of precission (mantissa length) only slightly delays
|
||||||
|
the onset of the cancellation effect.
|
||||||
|
|
||||||
**Countermeasures against unstable algorithms typically require better, stable algorithms, not more precise machine numbers.**
|
**Countermeasures against unstable algorithms typically require better, stable algorithms, not more precise machine numbers.**
|
||||||
|
|
||||||
|
|||||||
@@ -5,24 +5,24 @@ For this course, we will use the web-based development environment [Jupyterhub](
|
|||||||
For long-term work, a personal installation is recommended.
|
For long-term work, a personal installation is recommended.
|
||||||
|
|
||||||
|
|
||||||
## Installation on Personal Computer (Linux/MacOS/MS Windows)
|
## Installing on Your Own Computer (Linux/MacOS/MS Windows)
|
||||||
|
|
||||||
1. Install Julia with the installation and update manager **juliaup**: <https://github.com/JuliaLang/juliaup/>
|
1. Install Julia with the installation and update manager **juliaup**: <https://github.com/JuliaLang/juliaup/>.
|
||||||
2. Install **Visual Studio Code** as editor/IDE: <https://code.visualstudio.com/>
|
2. Install **Visual Studio Code** as editor/IDE: <https://code.visualstudio.com/>.
|
||||||
3. Install the **Julia language extension** in VS Code: <https://www.julia-vscode.org/docs/stable/gettingstarted/>
|
3. Install the **Julia language extension** in VS Code: <https://www.julia-vscode.org/docs/stable/gettingstarted/>.
|
||||||
|
|
||||||
Entry point:
|
Getting started:
|
||||||
|
|
||||||
- Create a new file with the extension `.jl` in VS Code
|
- Create a new file with the extension `.jl` in VS Code
|
||||||
- Write Julia code
|
- Write Julia code
|
||||||
- `Shift-Enter` or `Ctrl-Enter` at the end of a statement or block starts a Julia-REPL, code is copied to the REPL and executed
|
- `Shift-Enter` or `Ctrl-Enter` at the end of a statement or block starts a Julia-REPL: the code is copied to the REPL and executed
|
||||||
- [Key bindings for VS Code](https://code.visualstudio.com/docs/getstarted/keybindings#_keyboard-shortcuts-reference)
|
- [Key bindings for VS Code](https://code.visualstudio.com/docs/getstarted/keybindings#_keyboard-shortcuts-reference)
|
||||||
and [for Julia in VS Code](https://www.julia-vscode.org/docs/stable/userguide/keybindings/)
|
and [for Julia in VS Code](https://www.julia-vscode.org/docs/stable/userguide/keybindings/)
|
||||||
|
|
||||||
|
|
||||||
### The Julia-REPL
|
### The Julia-REPL
|
||||||
|
|
||||||
When Julia is started directly, the [Julia-REPL](https://docs.julialang.org/en/v1/stdlib/REPL/) (*read-eval-print* loop) starts, where one can work interactively with Julia.
|
When Julia is started directly from the command line, the [Julia-REPL](https://docs.julialang.org/en/v1/stdlib/REPL/) (*read-eval-print* loop) opens, allowing interactive work.
|
||||||
|
|
||||||
|
|
||||||
```default
|
```default
|
||||||
@@ -44,9 +44,9 @@ julia>
|
|||||||
### Jupyterhub & Jupyter
|
### Jupyterhub & Jupyter
|
||||||
|
|
||||||
- [JupyterHub](https://de.wikipedia.org/wiki/Project_Jupyter) is a multi-user server for Jupyter notebooks.
|
- [JupyterHub](https://de.wikipedia.org/wiki/Project_Jupyter) is a multi-user server for Jupyter notebooks.
|
||||||
- Jupyter is a web-based interactive programming environment that
|
- Jupyter is a web-based interactive programming environment
|
||||||
- was originally written for and in Python, but now supports many programming languages
|
- Originally written for and in Python, but now supports many programming languages
|
||||||
- In Jupyter, one works with so-called *notebooks*. These are structured text files (JSON), recognizable by the file extension `*.ipynb`.
|
- In Jupyter, one works with so-called *notebooks*: structured text files (JSON) with the file extension `*.ipynb`.
|
||||||
|
|
||||||
Our Jupyterhub server: <https://misun103.mathematik.uni-leipzig.de/>
|
Our Jupyterhub server: <https://misun103.mathematik.uni-leipzig.de/>
|
||||||
|
|
||||||
@@ -56,7 +56,7 @@ After logging into Jupyterhub, a file manager appears:
|
|||||||

|

|
||||||
:::
|
:::
|
||||||
|
|
||||||
::: {.content-visible when-format="pdf"}
|
::: {.content-visible when-format="typst"}
|
||||||
{width=50%}
|
{width=50%}
|
||||||
:::
|
:::
|
||||||
|
|
||||||
@@ -65,8 +65,8 @@ This can be used to:
|
|||||||
|
|
||||||
- open existing *notebooks*,
|
- open existing *notebooks*,
|
||||||
- create new *notebooks*,
|
- create new *notebooks*,
|
||||||
- upload files, e.g., notebooks, from a local computer,
|
- upload files, e.g., notebooks, from your local computer,
|
||||||
- end the session with `Logout` (please don't forget!).
|
- log out when finished (Please don't forget this!)
|
||||||
|
|
||||||
### Jupyter notebooks
|
### Jupyter notebooks
|
||||||
|
|
||||||
@@ -75,15 +75,15 @@ This can be used to:
|
|||||||

|

|
||||||
:::
|
:::
|
||||||
|
|
||||||
::: {.content-visible when-format="pdf"}
|
::: {.content-visible when-format="typst"}
|
||||||
{width=50%}
|
{width=50%}
|
||||||
:::
|
:::
|
||||||
|
|
||||||
|
|
||||||
*Notebooks* consist of cells. Cells can contain
|
*Notebooks* consist of cells. Cells can contain
|
||||||
|
|
||||||
- code or
|
- Code, or
|
||||||
- text/documentation (Markdown)
|
- Text/documentation (Markdown)
|
||||||
|
|
||||||
In text cells, the markup language [Markdown](https://en.wikipedia.org/wiki/Markdown) can be used for formatting and LaTeX for mathematical equations.
|
In text cells, the markup language [Markdown](https://en.wikipedia.org/wiki/Markdown) can be used for formatting and LaTeX for mathematical equations.
|
||||||
|
|
||||||
@@ -103,9 +103,9 @@ The cell currently being worked on can be in `command mode` or `edit mode`.
|
|||||||
| Save notebook | `s` | `Ctrl-s` |
|
| Save notebook | `s` | `Ctrl-s` |
|
||||||
| Rename notebook | `Menu -> File -> Rename` | `Menu -> File -> Rename` |
|
| Rename notebook | `Menu -> File -> Rename` | `Menu -> File -> Rename` |
|
||||||
| Close notebook | `Menu -> File -> Close & Halt` | `Menu -> File -> Close & Halt` |
|
| Close notebook | `Menu -> File -> Close & Halt` | `Menu -> File -> Close & Halt` |
|
||||||
| *run cell* | `Ctrl-Enter` | `Ctrl-Enter` |
|
| *Run cell* | `Ctrl-Enter` | `Ctrl-Enter` |
|
||||||
| *run cell, move to next cell* | `Shift-Enter` | `Shift-Enter` |
|
| *Run cell, move to next cell* | `Shift-Enter` | `Shift-Enter` |
|
||||||
| *run cell, insert new cell below* | `Alt-Enter` | `Alt-Enter` |
|
| *Run cell, insert new cell below* | `Alt-Enter` | `Alt-Enter` |
|
||||||
|
|
||||||
: {.striped}
|
: {.striped}
|
||||||
|
|
||||||
@@ -115,17 +115,17 @@ The cell currently being worked on can be in `command mode` or `edit mode`.
|
|||||||

|

|
||||||
:::
|
:::
|
||||||
|
|
||||||
::: {.content-visible when-format="pdf"}
|
::: {.content-visible when-format="typst"}
|
||||||
{width=50%}
|
{width=50%}
|
||||||
:::
|
:::
|
||||||
|
|
||||||
|
|
||||||
When a cell is working, its cell number becomes a `*` and
|
When a cell is working, its cell number becomes a `*` and
|
||||||
the `kernel busy` indicator (solid black dot at the top right next to the
|
the `kernel busy` indicator appears (solid black dot at the top right next to the
|
||||||
Julia version) appears. If this takes too long (an *infinite loop*
|
Julia version). If this takes too long (an *infinite loop*
|
||||||
is quickly programmed), then
|
can easily happen):
|
||||||
|
|
||||||
- click `Menu -> Kernel -> Interrupt`; if this is ineffective,
|
- click `Menu -> Kernel -> Interrupt`. If this doesn't work,
|
||||||
- click `Menu -> Kernel -> Restart`.
|
- click `Menu -> Kernel -> Restart`.
|
||||||
|
|
||||||
|
|
||||||
@@ -134,7 +134,7 @@ After a `kernel restart`, all cells containing the required definitions,
|
|||||||
`using` statements, etc. must be executed again.
|
`using` statements, etc. must be executed again.
|
||||||
|
|
||||||
|
|
||||||
**At the end of work, please always:**
|
**At the end of each session, please always:**
|
||||||
|
|
||||||
- `Menu -> File -> Save & Checkpoint`
|
- `Menu -> File -> Save & Checkpoint`
|
||||||
- `Menu -> File -> Close & Halt`
|
- `Menu -> File -> Close & Halt`
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ using InteractiveUtils
|
|||||||
|
|
||||||
# First Contact
|
# First Contact
|
||||||
|
|
||||||
This chapter helps you get started. It omits many details, and the code examples are often rather suboptimal.
|
This chapter helps you get started. It omits many details, and the code examples are often not optimal.
|
||||||
|
|
||||||
|
|
||||||
## Julia as a Calculator
|
## Julia as a Calculator
|
||||||
@@ -35,7 +35,7 @@ function Tab(s)
|
|||||||
dc = map(x->x.name, REPL.doc_completions(s))
|
dc = map(x->x.name, REPL.doc_completions(s))
|
||||||
l = filter(x->startswith(x,s), dc)
|
l = filter(x->startswith(x,s), dc)
|
||||||
println.(l)
|
println.(l)
|
||||||
return # return nothing, since broadcast println produces empty vector
|
return # return nothing, since broadcasting println produces an empty vector
|
||||||
end
|
end
|
||||||
|
|
||||||
▷ = |>
|
▷ = |>
|
||||||
@@ -43,7 +43,7 @@ end
|
|||||||
# IJulia.set_verbose(true)
|
# IJulia.set_verbose(true)
|
||||||
```
|
```
|
||||||
|
|
||||||
Compute: $\qquad 12^{1/3} + \frac{3\sqrt{2}}{\sin(0.5)-\cos(\frac{\pi}{4})\log(3)}+ e^5$
|
Compute the following: $\qquad 12^{1/3} + \frac{3\sqrt{2}}{\sin(0.5)-\cos(\frac{\pi}{4})\log(3)}+ e^5$
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
12^(1/3) + 3sqrt(2) / (sin(.5) - cos(pi/4)*log(3)) + exp(5)
|
12^(1/3) + 3sqrt(2) / (sin(.5) - cos(pi/4)*log(3)) + exp(5)
|
||||||
@@ -54,12 +54,12 @@ Note that:
|
|||||||
- Powers are written as `a^b`.
|
- Powers are written as `a^b`.
|
||||||
- The constant `π` is predefined.
|
- The constant `π` is predefined.
|
||||||
- `log()` is the natural logarithm.
|
- `log()` is the natural logarithm.
|
||||||
- The multiplication sign `a*b` can be omitted after a number if it is followed by a variable, function, or parenthesis.
|
- The multiplication operator `*` can be omitted after a number when followed by a variable, function, or opening parenthesis.
|
||||||
|
|
||||||
|
|
||||||
## The most important keys: `Tab` and `?` {#sec-tab}
|
## The most important keys: `Tab` and `?` {#sec-tab}
|
||||||
|
|
||||||
When programming, press the Tab key as soon as 2–3 letters of a word have been typed. Potential completions are then displayed or completed if the completion is unique. This saves time and you learn a lot about Julia.
|
When programming, press the Tab key as soon as you've typed 2–3 letters of a word. Potential completions are then displayed or completed if the completion is unique. This saves time and you learn a lot about Julia.
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
lo = "lo" #| hide_line
|
lo = "lo" #| hide_line
|
||||||
@@ -71,7 +71,7 @@ pri = "pri" #| hide_line
|
|||||||
pri ▷ Tab
|
pri ▷ Tab
|
||||||
```
|
```
|
||||||
|
|
||||||
The built-in Julia help `?name` for all functions and constructs is very comprehensive. Here is a rather brief example:
|
Julia's built-in help system `?name` provides comprehensive documentation for all functions and constructs. Here is a relatively brief example:
|
||||||
|
|
||||||
:::{.content-visible unless-format="typst"}
|
:::{.content-visible unless-format="typst"}
|
||||||
|
|
||||||
@@ -137,15 +137,16 @@ Expressions like `x + y = sin(2)` are therefore invalid. Only a variable name ma
|
|||||||
## Data types
|
## Data types
|
||||||
|
|
||||||
Julia is a [strongly typed](https://en.wikipedia.org/wiki/Strong_and_weak_typing) language
|
Julia is a [strongly typed](https://en.wikipedia.org/wiki/Strong_and_weak_typing) language
|
||||||
where every object possesses a type. Among the fundamental types are:
|
where every object has a type. Among the fundamental types are:
|
||||||
|
|
||||||
|
|
||||||
- Integers,
|
- Integers
|
||||||
- Floating-point numbers,
|
- Floating-point numbers
|
||||||
- Strings and
|
- Strings
|
||||||
- Booleans.
|
- Booleans.
|
||||||
|
|
||||||
The type of a variable can be determined using the `typeof()` function.
|
The type of a variable can be determined using the `typeof()` function.
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
#| warning: true
|
#| warning: true
|
||||||
#| error: true
|
#| error: true
|
||||||
@@ -156,7 +157,7 @@ end
|
|||||||
The standard floating-point number has a size of 64 bits, which corresponds to a `double` in C/C++/Java.
|
The standard floating-point number has a size of 64 bits, which corresponds to a `double` in C/C++/Java.
|
||||||
|
|
||||||
Julia is a [dynamically typed](https://en.wikipedia.org/wiki/Dynamic_typing) language.
|
Julia is a [dynamically typed](https://en.wikipedia.org/wiki/Dynamic_typing) language.
|
||||||
Variables have no type; they are typeless references (pointers) to objects.
|
Variables have no type; they are typeless references (pointers) to typed objects.
|
||||||
When people speak of the "type of a variable", they mean the type of the object currently assigned to the variable.
|
When people speak of the "type of a variable", they mean the type of the object currently assigned to the variable.
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
@@ -170,7 +171,7 @@ println( typeof(x), " - Value of x = $x" )
|
|||||||
```
|
```
|
||||||
|
|
||||||
## Print statements
|
## Print statements
|
||||||
The `println()` function puts a line break at the end, `print()` doesn't.
|
The `println()` function adds a line break at the end; `print()` does not.
|
||||||
```{julia}
|
```{julia}
|
||||||
print(y)
|
print(y)
|
||||||
print("...the line continues...")
|
print("...the line continues...")
|
||||||
@@ -180,7 +181,7 @@ println("New line")
|
|||||||
println("New line")
|
println("New line")
|
||||||
```
|
```
|
||||||
|
|
||||||
Both functions can accept a list of strings and variables as arguments. Variables can also be embedded in strings by prefixing the variable name with a dollar sign *(string interpolation)*.
|
Both functions can accept a list of string literals and variables as arguments. Variables can also be embedded in strings by prefixing the variable name with a dollar sign *(string interpolation)*.
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
x = 23
|
x = 23
|
||||||
@@ -194,22 +195,22 @@ println("x= $x ... and y= $y...$zz") # variant with string interpol
|
|||||||
:::{.callout-note .titlenormal collapse=true icon=false }
|
:::{.callout-note .titlenormal collapse=true icon=false }
|
||||||
## If you want to print a dollar sign...
|
## If you want to print a dollar sign...
|
||||||
|
|
||||||
you must prepend a *backslash*. If you want to print a *backslash*, you must double it.
|
you must escape it with a *backslash*. To print a *backslash* itself, you must double it.
|
||||||
```{julia}
|
```{julia}
|
||||||
println("One dollar: 1\$ and three backslashes: \\\\\\ ")
|
println("One dollar: 1\$ and three backslashes: \\\\\\ ")
|
||||||
```
|
```
|
||||||
:::
|
:::
|
||||||
|
|
||||||
## Functions
|
## Functions
|
||||||
Function definitions start with the keyword `function` and end with the keyword `end`. Typically, they have one or more arguments and return a computed result via a `return` statement.
|
Function definitions begin with the keyword `function` and end with the keyword `end`. Typically, they have one or more arguments and return a computed result via a `return` statement.
|
||||||
```{julia}
|
```{julia}
|
||||||
function hypotenuse(a, b) # particularly cumbersome today
|
function hypotenuse(a, b) # a bit cumbersome
|
||||||
c2 = a^2 + b^2
|
c2 = a^2 + b^2
|
||||||
c = sqrt(c2)
|
c = sqrt(c2)
|
||||||
return c
|
return c
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
After their definition, the function can be used (called). The variables `a,b,c,c2` used in the definition are local variables and are not available outside the function definition.
|
After definition, the function can be used (called). The variables `a,b,c,c2` used in the definition are local and not available outside the function definition.
|
||||||
```{julia}
|
```{julia}
|
||||||
#| error: true
|
#| error: true
|
||||||
x = 3
|
x = 3
|
||||||
@@ -225,12 +226,12 @@ hypotenuse(a, b) = sqrt(a^2+b^2)
|
|||||||
|
|
||||||
|
|
||||||
## Tests
|
## Tests
|
||||||
Tests return a Boolean value.
|
Tests return a Boolean value (`true` or `false`).
|
||||||
```{julia}
|
```{julia}
|
||||||
x = 3^2
|
x = 3^2
|
||||||
x < 2^3
|
x < 2^3
|
||||||
```
|
```
|
||||||
In addition to the usual arithmetic comparisons `==, !=, <, <= ,> ,>=`
|
In addition to the usual arithmetic comparisons `==, !=, <, <=, >, >=`
|
||||||
there are many other tests. The result of a test can also be assigned to a variable, which is then of type `Bool`. The logical operators `&&`, `||` and negation `!` can be used in tests.
|
there are many other tests. The result of a test can also be assigned to a variable, which is then of type `Bool`. The logical operators `&&`, `||` and negation `!` can be used in tests.
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
@@ -241,9 +242,9 @@ println("$test1 $test2 $test3")
|
|||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
## Conditional Evaluation
|
## Conditional Statements
|
||||||
|
|
||||||
A simple `if` has the form
|
A simple `if` statement has the form
|
||||||
```default
|
```default
|
||||||
if <test>
|
if <test>
|
||||||
<statement1>
|
<statement1>
|
||||||
@@ -252,7 +253,7 @@ if <test>
|
|||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
There can one or several `elseif` blocks and a final `else` block.
|
There can be one or more `elseif` blocks and an optional final `else` block.
|
||||||
```{julia}
|
```{julia}
|
||||||
x = sqrt(100)
|
x = sqrt(100)
|
||||||
|
|
||||||
@@ -264,7 +265,7 @@ else
|
|||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
Indentation enhances readability but is optional. Line breaks separate statements, as do semicolons. The above code block is equivalent to the following line in Julia:
|
Indentation enhances readability but is optional. Line breaks separate statements, as do semicolons. The code above is equivalent to the following single line in Julia:
|
||||||
```{julia}
|
```{julia}
|
||||||
# Please don't program like this! You will regret it!
|
# Please don't program like this! You will regret it!
|
||||||
x=sqrt(100); if x > 20 println("Strange!") else println("OK"); y = x + 3 end
|
x=sqrt(100); if x > 20 println("Strange!") else println("OK"); y = x + 3 end
|
||||||
@@ -274,7 +275,7 @@ x=sqrt(100); if x > 20 println("Strange!") else println("OK"); y = x + 3 end
|
|||||||
|
|
||||||
## Simple `for` loops
|
## Simple `for` loops
|
||||||
|
|
||||||
`for` loops for repeated execution of statements have the form
|
`for` loops for repeating the execution of statements have the form
|
||||||
```default
|
```default
|
||||||
for <counter> = start:end
|
for <counter> = start:end
|
||||||
<statement1>
|
<statement1>
|
||||||
@@ -294,8 +295,8 @@ sum
|
|||||||
|
|
||||||
|
|
||||||
## Arrays
|
## Arrays
|
||||||
One-dimensional arrays (vectors) are a simple form of containers. They can be created with square brackets
|
One-dimensional arrays (vectors) are a simple container type. They can be created with square brackets
|
||||||
and accessed by index. Indexing starts at 1.
|
and accessed by index, with indexing starting at 1.
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
v = [12, 33.2, 17, 19, 22]
|
v = [12, 33.2, 17, 19, 22]
|
||||||
@@ -321,7 +322,7 @@ v
|
|||||||
|
|
||||||
:::{.callout-note icon="false" .titlenormal collapse="true" font-variant-ligatures="no-contextual" }
|
:::{.callout-note icon="false" .titlenormal collapse="true" font-variant-ligatures="no-contextual" }
|
||||||
|
|
||||||
## Postscript: how the effect of the Tab key was simulated on this page
|
## Appendix: how the effect of the Tab key was simulated on this page
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
using REPL
|
using REPL
|
||||||
|
|||||||
@@ -31,24 +31,24 @@ for x ∈ ( 3, 3.3e4, Int16(20), Float32(3.3e4), UInt16(9) )
|
|||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
## Integer Numbers *(integers)*
|
## Integers
|
||||||
|
|
||||||
Integer numbers are fundamentally stored as bit patterns of fixed length. Therefore, the value range is finite.
|
Integers are stored as fixed-length bit patterns. Therefore, the value range is finite.
|
||||||
**Within this value range** addition, subtraction, multiplication, and integer division with remainder
|
**Within this value range** addition, subtraction, multiplication, and integer division with remainder
|
||||||
are exact operations without rounding errors.
|
are exact operations without rounding errors.
|
||||||
|
|
||||||
Integer numbers come in two types: `Signed` (with sign) and `Unsigned`, which can be viewed as machine models for ℤ and ℕ respectively.
|
Integer numbers come in two types: `Signed` and `Unsigned`, which can be viewed as machine models for ℤ and ℕ respectively.
|
||||||
|
|
||||||
|
|
||||||
### *Unsigned integers*
|
### *Unsigned integers*
|
||||||
```{julia}
|
```{julia}
|
||||||
subtypes(Unsigned)
|
subtypes(Unsigned)
|
||||||
```
|
```
|
||||||
UInts are binary numbers with n=8, 16, 32, 64, or 128 bits length and the corresponding value range of
|
`UInts` are binary numbers with a bit width of 8, 16, 32, 64, or 128 and the corresponding value range of
|
||||||
$$
|
$$
|
||||||
0 \le x < 2^n
|
0 \le x < 2^n
|
||||||
$$
|
$$
|
||||||
They are used relatively rarely in *scientific computing*. In hardware-proximate programming, they are e.g. used for handling binary data and memory addresses. Therefore, Julia displays them by default as hexadecimal numbers (with prefix `0x` and digits `0-9a-f`).
|
They are used rarely in *scientific computing*. In low-level hardware programming, they are used, e.g., for handling binary data and memory addresses. By default, Julia displays them as hexadecimal numbers (with prefix `0x` and digits `0-9a-f`).
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
x = 0x0033efef
|
x = 0x0033efef
|
||||||
@@ -83,7 +83,7 @@ $$
|
|||||||
$$
|
$$
|
||||||
-2.147.483.648 \le x \le 2.147.483.647
|
-2.147.483.648 \le x \le 2.147.483.647
|
||||||
$$
|
$$
|
||||||
The maximum value $2^{31}-1$ is conveniently a Mersenne prime:
|
By the way, the maximum value $2^{31}-1$ is a Mersenne prime:
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
using Primes
|
using Primes
|
||||||
@@ -94,13 +94,13 @@ Negative numbers are represented in two's complement:
|
|||||||
|
|
||||||
$x \Rightarrow -x$ corresponds to: _flip all bits, then add 1_
|
$x \Rightarrow -x$ corresponds to: _flip all bits, then add 1_
|
||||||
|
|
||||||
This looks like this:
|
This looks as follows:
|
||||||
|
|
||||||
::: {.content-visible when-format="html"}
|
::: {.content-visible when-format="html"}
|
||||||
{width=50%}
|
{width=50%}
|
||||||
:::
|
:::
|
||||||
|
|
||||||
::: {.content-visible when-format="pdf"}
|
::: {.content-visible when-format="typst"}
|
||||||
{width=50%}
|
{width=50%}
|
||||||
:::
|
:::
|
||||||
|
|
||||||
@@ -115,7 +115,7 @@ x = 2^62 - 10 + 2^62
|
|||||||
```{julia}
|
```{julia}
|
||||||
x + 20
|
x + 20
|
||||||
```
|
```
|
||||||
No error message, no warning! Fixed-length integers do not lie on a line, but on a circle!
|
No error message, no warning! Fixed-length integers do not lie on a line, but on a **circle.**
|
||||||
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||
@@ -155,12 +155,12 @@ The operations `+`,`-`,`*` have the usual exact arithmetic **modulo $2^{64}$**.
|
|||||||
#### Powers `a^b`
|
#### Powers `a^b`
|
||||||
|
|
||||||
- Powers `a^n` are computed exactly modulo $2^{64}$ for natural exponents `n`.
|
- Powers `a^n` are computed exactly modulo $2^{64}$ for natural exponents `n`.
|
||||||
- For negative exponents, the result is a floating-point number.
|
- For negative exponents, the result is a `Float`.
|
||||||
- `0^0` is [naturally](https://en.wikipedia.org/wiki/Zero_to_the_power_of_zero#cite_note-T4n3B-4) equal to 1.
|
- `0^0` is [naturally](https://en.wikipedia.org/wiki/Zero_to_the_power_of_zero#cite_note-T4n3B-4) equal to 1.
|
||||||
```{julia}
|
```{julia}
|
||||||
(-2)^63, 2^64, 3^(-3), 0^0
|
(-2)^63, 2^64, 3^(-3), 0^0
|
||||||
```
|
```
|
||||||
- For natural exponents, [*exponentiation by squaring*](https://de.wikipedia.org/wiki/Bin%C3%A4re_Exponentiation) is used, so for example `x^23` requires only 7 multiplications:
|
- For natural exponents, [*exponentiation by squaring*](https://en.wikipedia.org/wiki/Exponentiation_by_squaring) is used, so for example `x^23` requires only 7 multiplications:
|
||||||
$$
|
$$
|
||||||
x^{23} = \left( \left( (x^2)^2 \cdot x \right)^2 \cdot x \right)^2 \cdot x
|
x^{23} = \left( \left( (x^2)^2 \cdot x \right)^2 \cdot x \right)^2 \cdot x
|
||||||
$$
|
$$
|
||||||
@@ -176,7 +176,7 @@ x = 40/5
|
|||||||
|
|
||||||
- The functions `div(a,b)`, `rem(a,b)`, and `divrem(a,b)` compute the quotient of integer division, the corresponding remainder, or both as a tuple.
|
- The functions `div(a,b)`, `rem(a,b)`, and `divrem(a,b)` compute the quotient of integer division, the corresponding remainder, or both as a tuple.
|
||||||
- For `div(a,b)` there is the operator form `a ÷ b` (input: `\div<TAB>`), and for `rem(a,b)` the operator form `a % b`.
|
- For `div(a,b)` there is the operator form `a ÷ b` (input: `\div<TAB>`), and for `rem(a,b)` the operator form `a % b`.
|
||||||
- By default, division is "rounded toward zero", so the corresponding remainder has the same sign as the dividend `a`:
|
- By default, division uses "rounding toward zero", so the corresponding remainder has the same sign as the dividend `a`:
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
@show divrem( 27, 4)
|
@show divrem( 27, 4)
|
||||||
@@ -185,9 +185,9 @@ x = 40/5
|
|||||||
@show ( 27 ÷ -4, 27 % -4);
|
@show ( 27 ÷ -4, 27 % -4);
|
||||||
```
|
```
|
||||||
|
|
||||||
- A rounding rule other than `RoundToZero` can be specified as the third optional argument for the functions.
|
- A rounding rule other than `RoundToZero` can be specified as the third optional argument for these functions.
|
||||||
- `?RoundingMode` shows the possible rounding modes.
|
- `?RoundingMode` shows the possible rounding modes.
|
||||||
- For the rounding rule `RoundDown` ("toward minus infinity"), so that the corresponding remainder has the same sign as the divisor `b`, there are also the functions `fld(a,b)` *(floored division)* and `mod(a,b)`:
|
- For the rounding rule `RoundDown` ("toward minus infinity" -- so that the corresponding remainder has the same sign as the divisor `b`), there are also the functions `fld(a,b)` *(floored division)* and `mod(a,b)`:
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
@show divrem(-27, 4, RoundDown)
|
@show divrem(-27, 4, RoundDown)
|
||||||
@@ -195,14 +195,14 @@ x = 40/5
|
|||||||
@show (fld( 27, -4), mod( 27, -4));
|
@show (fld( 27, -4), mod( 27, -4));
|
||||||
```
|
```
|
||||||
|
|
||||||
For all rounding modes holds:
|
For all rounding modes, the following holds:
|
||||||
```
|
```
|
||||||
div(a, b, RoundingMode) * b + rem(a, b, RoundingMode) = a
|
div(a, b, RoundingMode) * b + rem(a, b, RoundingMode) = a
|
||||||
```
|
```
|
||||||
|
|
||||||
#### The `BigInt` Type
|
#### The `BigInt` Type
|
||||||
|
|
||||||
The `BigInt` type allows arbitrary-length integers. The required memory is dynamically allocated.
|
The `BigInt` type supports arbitrary-precision integers with dynamically allocated memory.
|
||||||
|
|
||||||
Numeric constants automatically have a sufficiently large type:
|
Numeric constants automatically have a sufficiently large type:
|
||||||
|
|
||||||
@@ -217,7 +217,7 @@ z = 10_000_000_000_000_000_000_000_000_000_000_000_000_000 # 10 sextillion
|
|||||||
@show typeof(z);
|
@show typeof(z);
|
||||||
```
|
```
|
||||||
|
|
||||||
Usually, one must explicitly request the `BigInt` type to avoid modulo $2^{64}$ arithmetic:
|
In most cases, you must explicitly specify the `BigInt` type to avoid modulo $2^{64}$ arithmetic:
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
@show 3^300 BigInt(3)^300;
|
@show 3^300 BigInt(3)^300;
|
||||||
@@ -225,7 +225,7 @@ Usually, one must explicitly request the `BigInt` type to avoid modulo $2^{64}$
|
|||||||
|
|
||||||
*Arbitrary precision arithmetic* comes at a cost of significant memory and computation time.
|
*Arbitrary precision arithmetic* comes at a cost of significant memory and computation time.
|
||||||
|
|
||||||
We compare the time and memory requirements for summing 10 million integers as `Int64` and as `BigInt`.
|
We compare the time and memory requirements for summing 10 million integers as `Int64` versus `BigInt`.
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
# 10^7 random numbers, uniformly distributed between -10^7 and 10^7
|
# 10^7 random numbers, uniformly distributed between -10^7 and 10^7
|
||||||
@@ -235,7 +235,7 @@ vec_int = rand(-10^7:10^7, 10^7)
|
|||||||
vec_bigint = BigInt.(vec_int)
|
vec_bigint = BigInt.(vec_int)
|
||||||
```
|
```
|
||||||
|
|
||||||
An initial impression of the time and memory requirements is provided by the `@time` macro:
|
The `@time` macro provides a rough estimate of the required time and memory:
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
@time x = sum(vec_int)
|
@time x = sum(vec_int)
|
||||||
@@ -246,7 +246,7 @@ An initial impression of the time and memory requirements is provided by the `@t
|
|||||||
@show x typeof(x);
|
@show x typeof(x);
|
||||||
```
|
```
|
||||||
|
|
||||||
Due to Julia's just-in-time compilation, a single execution of a function is not very informative. The `BenchmarkTools` package provides the `@benchmark` macro, which calls a function multiple times and displays the execution times as a histogram.
|
Due to Julia's just-in-time compilation, timing a single function call is not very informative. The `BenchmarkTools` package provides the `@benchmark` macro, which calls a function multiple times and displays the execution times as a histogram.
|
||||||
|
|
||||||
:::{.ansitight}
|
:::{.ansitight}
|
||||||
```{julia}
|
```{julia}
|
||||||
@@ -263,8 +263,8 @@ using BenchmarkTools
|
|||||||
The `BigInt` addition is more than 30 times slower.
|
The `BigInt` addition is more than 30 times slower.
|
||||||
|
|
||||||
:::{.content-hidden unless-format="xxx"}
|
:::{.content-hidden unless-format="xxx"}
|
||||||
The following function should compute the sum of all numbers from 1 to n using arithmetic of type T.
|
The following function computes the sum of all numbers from 1 to n using arithmetic of type T.
|
||||||
Due to the *type promotion rules*, it is sufficient for `T ≥ Int64` to initialize the accumulator variable with a number of type T.
|
Due to *type promotion rules*, it is sufficient to initialize the accumulator with a value of type T (for `T ≥ Int64`).
|
||||||
```{julia}
|
```{julia}
|
||||||
function mysum(n, T)
|
function mysum(n, T)
|
||||||
s = T(0)
|
s = T(0)
|
||||||
@@ -303,7 +303,7 @@ using BenchmarkTools
|
|||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
The computation of $\sum_{n=1}^{10000000} n$ takes on my PC an average of 2 nanoseconds with standard 64-bit integers and over one second in *arbitrary precision arithmetic*, during which nearly 500MB of memory is also allocated.
|
The computation of $\sum_{n=1}^{10000000} n$ takes on my PC an average of 2 milliseconds with standard 64-bit integers and over one second in *arbitrary precision arithmetic*, during which nearly 500MB of memory is also allocated.
|
||||||
|
|
||||||
:::
|
:::
|
||||||
:::
|
:::
|
||||||
@@ -311,9 +311,8 @@ The computation of $\sum_{n=1}^{10000000} n$ takes on my PC an average of 2 nano
|
|||||||
|
|
||||||
## Floating-Point Numbers
|
## Floating-Point Numbers
|
||||||
|
|
||||||
From _floating point numbers_, one can form German **[Gleit|Fließ]--[Komma|Punkt]--Zahlen**, and indeed all 4 variants appear in the literature.
|
|
||||||
|
|
||||||
In numerical mathematics, one also often speaks of **machine numbers**.
|
In numerical mathematics, the term **machine numbers** is also commonly used.
|
||||||
|
|
||||||
|
|
||||||
### Basic Idea
|
### Basic Idea
|
||||||
@@ -340,9 +339,9 @@ holds.
|
|||||||
|
|
||||||
## Machine Numbers
|
## Machine Numbers
|
||||||
|
|
||||||
The set of machine numbers $𝕄(b, p, e_{min}, e_{max})$ is characterized by the base $b$ used, the mantissa length $p$, and the value range of the exponent $\{e_{min}, ... ,e_{max}\}$.
|
The set of machine numbers $𝕄(b, p, e_{min}, e_{max})$ is characterized by the base $b$, the mantissa length $p$, and the value range of the exponent $\{e_{min}, ... ,e_{max}\}$.
|
||||||
|
|
||||||
In our convention, the mantissa of a normalized machine number has one digit (of base $b$) nonzero before the decimal point and $p-1$ digits after the decimal point.
|
In our convention, the mantissa of a normalized machine number has one digit (of base $b$) before the decimal point and $p-1$ digits after the decimal point.
|
||||||
|
|
||||||
If $b=2$, one needs only $p-1$ bits to store the mantissa of normalized floating-point numbers.
|
If $b=2$, one needs only $p-1$ bits to store the mantissa of normalized floating-point numbers.
|
||||||
|
|
||||||
@@ -353,7 +352,7 @@ The IEEE 754 standard, implemented by most modern processors and programming lan
|
|||||||
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||
### Structure of `Float64` according to [IEEE 754 standard](https://de.wikipedia.org/wiki/IEEE_754)
|
### Structure of `Float64` according to the [IEEE 754 standard](https://en.wikipedia.org/wiki/IEEE_754)
|
||||||
|
|
||||||
|
|
||||||
::: {.content-visible when-format="html"}
|
::: {.content-visible when-format="html"}
|
||||||
@@ -361,7 +360,7 @@ The IEEE 754 standard, implemented by most modern processors and programming lan
|
|||||||
](../images/1024px-IEEE_754_Double_Floating_Point_Format.png)
|
](../images/1024px-IEEE_754_Double_Floating_Point_Format.png)
|
||||||
:::
|
:::
|
||||||
|
|
||||||
::: {.content-visible when-format="pdf"}
|
::: {.content-visible when-format="typst"}
|
||||||
{width="70%"}
|
](../images/1024px-IEEE_754_Double_Floating_Point_Format.png){width="70%"}
|
||||||
:::
|
:::
|
||||||
@@ -372,7 +371,7 @@ The IEEE 754 standard, implemented by most modern processors and programming lan
|
|||||||
- The values $E=0$ and $E=(11111111111)_2=2047$ are reserved for encoding special values such as
|
- The values $E=0$ and $E=(11111111111)_2=2047$ are reserved for encoding special values such as
|
||||||
$\pm0, \pm\infty$, NaN _(Not a Number)_ and subnormal numbers.
|
$\pm0, \pm\infty$, NaN _(Not a Number)_ and subnormal numbers.
|
||||||
- 52 bits for the (shortened) mantissa $M,\quad 0\le M<1$, corresponding to approximately 16 decimal digits
|
- 52 bits for the (shortened) mantissa $M,\quad 0\le M<1$, corresponding to approximately 16 decimal digits
|
||||||
- Thus, the following number is represented:
|
- Thus, the number represented is:
|
||||||
$$ x=(-1)^S \cdot(1+M)\cdot 2^{E-1023}$$
|
$$ x=(-1)^S \cdot(1+M)\cdot 2^{E-1023}$$
|
||||||
|
|
||||||
An example:
|
An example:
|
||||||
@@ -380,7 +379,7 @@ An example:
|
|||||||
x = 27.56640625
|
x = 27.56640625
|
||||||
bitstring(x)
|
bitstring(x)
|
||||||
```
|
```
|
||||||
This can be done more nicely:
|
This can be displayed more clearly:
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
function printbitsf64(x::Float64)
|
function printbitsf64(x::Float64)
|
||||||
@@ -410,9 +409,9 @@ $$
|
|||||||
x = (1 + 1/2 + 1/8 + 1/16 + 1/32 + 1/256 + 1/4096) * 2^4
|
x = (1 + 1/2 + 1/8 + 1/16 + 1/32 + 1/256 + 1/4096) * 2^4
|
||||||
```
|
```
|
||||||
|
|
||||||
- The machine numbers 𝕄 form a finite, discrete subset of ℝ. There is a smallest and a largest machine number, and apart from these, all x∈𝕄 have a predecessor and successor in 𝕄.
|
- The set of machine numbers 𝕄 forms a finite, discrete subset of ℝ. There exists a smallest and a largest machine number; all other elements x∈𝕄 have both a predecessor and successor in 𝕄.
|
||||||
- What is the successor of x in 𝕄? To do this, we set the smallest mantissa bit from 0 to 1.
|
- What is the successor of x in 𝕄? To do this, we set the smallest mantissa bit from 0 to 1.
|
||||||
- Converting a string of zeros and ones into the corresponding machine number is possible e.g. as follows:
|
- Converting a string of zeros and ones into the corresponding machine number:
|
||||||
|
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
@@ -445,8 +444,8 @@ printbitsf64(z)
|
|||||||
@show nextfloat(1.) - 1 2^-52 eps(Float64);
|
@show nextfloat(1.) - 1 2^-52 eps(Float64);
|
||||||
```
|
```
|
||||||
|
|
||||||
- Machine epsilon is a measure of the relative distance between machine numbers and quantifies the statement: "64-bit floating-point numbers have a precision of approximately 16 decimal digits."
|
- Machine epsilon measures the relative distance between machine numbers and quantifies the statement: "64-bit floating-point numbers have a precision of approximately 16 decimal digits."
|
||||||
- Machine epsilon is something completely different from the smallest positive floating-point number:
|
- Machine epsilon should not be confused with the smallest positive floating-point number:
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
floatmin(Float64)
|
floatmin(Float64)
|
||||||
@@ -456,10 +455,10 @@ floatmin(Float64)
|
|||||||
$$
|
$$
|
||||||
\epsilon' = \frac{\epsilon}{2}\approx 1.1\times 10^{-16}
|
\epsilon' = \frac{\epsilon}{2}\approx 1.1\times 10^{-16}
|
||||||
$$
|
$$
|
||||||
is the maximum relative error that can occur when rounding a real number to the nearest machine number.
|
This is the maximum relative error that can occur when rounding a real number to the nearest machine number.
|
||||||
- Since numbers in the interval $(1-\epsilon',1+\epsilon']$ are rounded to the machine number $1$, one can also define $\epsilon'$ as: *the largest number for which in machine arithmetic still holds: $1+\epsilon' = 1$.*
|
- Since numbers in the interval $(1-\epsilon',1+\epsilon']$ are rounded to the machine number $1$, one can also define $\epsilon'$ as: *the largest number for which $1+\epsilon' = 1$ still holds in machine arithmetic.*
|
||||||
|
|
||||||
In this way, one can also compute machine epsilon:
|
This allows to compute machine epsilon using the floating point arithmetic:
|
||||||
|
|
||||||
:::{.ansitight}
|
:::{.ansitight}
|
||||||
|
|
||||||
@@ -491,7 +490,7 @@ Eps
|
|||||||
- In the interval $[1,2)$ there are $2^{52}$ equidistant machine numbers.
|
- In the interval $[1,2)$ there are $2^{52}$ equidistant machine numbers.
|
||||||
- After that, the exponent increases by 1 and the mantissa $M$ is reset to 0. Thus, the interval $[2,4)$ again contains $2^{52}$ equidistant machine numbers, as does the interval $[4,8)$ up to $[2^{1023}, 2^{1024})$.
|
- After that, the exponent increases by 1 and the mantissa $M$ is reset to 0. Thus, the interval $[2,4)$ again contains $2^{52}$ equidistant machine numbers, as does the interval $[4,8)$ up to $[2^{1023}, 2^{1024})$.
|
||||||
- Likewise, in the intervals $\ [\frac{1}{2},1), \ [\frac{1}{4},\frac{1}{2}),...$ there are $2^{52}$ equidistant machine numbers each, down to $[2^{-1022}, 2^{-1021})$.
|
- Likewise, in the intervals $\ [\frac{1}{2},1), \ [\frac{1}{4},\frac{1}{2}),...$ there are $2^{52}$ equidistant machine numbers each, down to $[2^{-1022}, 2^{-1021})$.
|
||||||
- This forms the set $𝕄_+$ of positive machine numbers, and it is
|
- This forms the set $𝕄_+$ of positive machine numbers, and we have
|
||||||
$$
|
$$
|
||||||
𝕄 = -𝕄_+ \cup \{0\} \cup 𝕄_+
|
𝕄 = -𝕄_+ \cup \{0\} \cup 𝕄_+
|
||||||
$$
|
$$
|
||||||
@@ -512,10 +511,10 @@ printbitsf64(floatmin(Float64))
|
|||||||
|
|
||||||
## Rounding to Machine Numbers
|
## Rounding to Machine Numbers
|
||||||
|
|
||||||
- The mapping rd: ℝ $\rightarrow$ 𝕄 should round to the nearest representable number.
|
- The map rd: ℝ $\rightarrow$ 𝕄 should round to the nearest representable number.
|
||||||
- Standard rounding mode: _round to nearest, ties to even_
|
- Standard rounding mode is _round to nearest, ties to even_:
|
||||||
If one lands exactly in the middle between two machine numbers *(tie)*, one chooses the one whose last mantissa bit is 0.
|
when a value falls exactly midway between two machine numbers *(tie)*, the one with 0 as its last mantissa bit is selected.
|
||||||
- Justification: this way, in 50% of the cases one rounds up and in 50% down, thus avoiding a "statistical drift" in longer calculations.
|
- Justification: this way, we round up in 50% of the cases and down in 50% of the cases, thus avoiding a "statistical drift" in longer calculations.
|
||||||
- It holds:
|
- It holds:
|
||||||
$$
|
$$
|
||||||
\frac{|x-\text{rd}(x)|}{|x|} \le \frac{1}{2} \epsilon
|
\frac{|x-\text{rd}(x)|}{|x|} \le \frac{1}{2} \epsilon
|
||||||
@@ -524,7 +523,7 @@ $$
|
|||||||
|
|
||||||
## Machine Number Arithmetic
|
## Machine Number Arithmetic
|
||||||
|
|
||||||
The machine numbers, as a subset of ℝ, are not algebraically closed. Even the sum of two machine numbers will generally not be a machine number.
|
The machine numbers, as a subset of ℝ, are not algebraically closed. Even the sum of two machine numbers is generally not representable as a machine number.
|
||||||
|
|
||||||
:::{.callout-important}
|
:::{.callout-important}
|
||||||
The IEEE 754 standard requires that machine number arithmetic produces the *rounded exact result*:
|
The IEEE 754 standard requires that machine number arithmetic produces the *rounded exact result*:
|
||||||
@@ -534,7 +533,7 @@ $$
|
|||||||
a \oplus b = \text{rd}(a + b)
|
a \oplus b = \text{rd}(a + b)
|
||||||
$$
|
$$
|
||||||
The same must hold for the implementation of standard functions such as
|
The same must hold for the implementation of standard functions such as
|
||||||
`sqrt()`, `log()`, `sin()` ...: they also return the machine number closest to the exact result.
|
`sqrt()`, `log()`, `sin()`, ... -- they also return the machine number closest to the exact result.
|
||||||
:::
|
:::
|
||||||
|
|
||||||
|
|
||||||
@@ -570,7 +569,7 @@ $$
|
|||||||
$$
|
$$
|
||||||
|
|
||||||
|
|
||||||
One should also be reminded that even "simple" decimal fractions often cannot be represented exactly as machine numbers:
|
One should also remember that even "simple" decimal fractions cannot always be represented exactly as machine numbers:
|
||||||
|
|
||||||
$$
|
$$
|
||||||
\begin{aligned}
|
\begin{aligned}
|
||||||
@@ -598,7 +597,7 @@ Consequence:
|
|||||||
0.2 + 0.1
|
0.2 + 0.1
|
||||||
```
|
```
|
||||||
|
|
||||||
When outputting a machine number, the binary fraction must be converted to a decimal fraction. One can also display more digits of this decimal fraction expansion:
|
When outputting a machine number, the binary fraction must be converted to a decimal fraction. Julia can display more digits of this decimal fraction expansion:
|
||||||
```{julia}
|
```{julia}
|
||||||
using Printf
|
using Printf
|
||||||
@printf("%.30f", 0.1)
|
@printf("%.30f", 0.1)
|
||||||
@@ -607,11 +606,11 @@ using Printf
|
|||||||
```{julia}
|
```{julia}
|
||||||
@printf("%.30f", 0.3)
|
@printf("%.30f", 0.3)
|
||||||
```
|
```
|
||||||
The binary fraction mantissa of a machine number can have a long or even infinitely periodic decimal expansion. Therefore,
|
The binary fraction mantissa of a machine number can have a long or even infinitely periodic decimal expansion. But
|
||||||
one should not be misled into thinking there is "higher precision"!
|
one should not be misled into thinking that this is "higher precision"!
|
||||||
|
|
||||||
:::{.callout-important}
|
:::{.callout-important}
|
||||||
Moral: when testing `Float`s for equality, one should almost always define a realistic accuracy `epsilon` appropriate to the problem and
|
Key message: When testing `Float`s for equality, one should almost always define a realistic tolerance `epsilon` appropriate to the problem and
|
||||||
test:
|
test:
|
||||||
|
|
||||||
```julia
|
```julia
|
||||||
@@ -628,15 +627,15 @@ end
|
|||||||
The gap between zero and the smallest normalized machine number $2^{-1022} \approx 2.22\times 10^{-308}$
|
The gap between zero and the smallest normalized machine number $2^{-1022} \approx 2.22\times 10^{-308}$
|
||||||
is filled with subnormal machine numbers.
|
is filled with subnormal machine numbers.
|
||||||
|
|
||||||
For understanding, let's take a simple model:
|
Let's look at a simple model:
|
||||||
|
|
||||||
- Let 𝕄(10,4,±5) be the set of machine numbers to base 10 with 4 mantissa digits (one before the decimal point, 3 after) and the exponent range -5 ≤ E ≤ 5.
|
- Let 𝕄(10,4,±5) be the set of machine numbers to base 10 with 4 mantissa digits (one before the decimal point, 3 after) and the exponent range -5 ≤ E ≤ 5.
|
||||||
- Then the normalized representation (nonzero leading digit)
|
- Then the normalized representation (nonzero leading digit)
|
||||||
of e.g. 1234.0 is 1.234e3 and of 0.00789 is 7.890e-3.
|
of e.g. 1234.0 is 1.234e3 and of 0.00789 is 7.890e-3.
|
||||||
- It is important that machine numbers are kept normalized at every computation step. Only then is the mantissa length fully utilized and the accuracy is maximum.
|
- It is essential that machine numbers remain normalized at each computation step. Only then is the full mantissa length utilized, maximizing accuracy.
|
||||||
- The smallest positive normalized number in our model is `x = 1.000e-5`. Already `x/2` would have to be rounded to 0.
|
- The smallest positive normalized number in our model is `x = 1.000e-5`. Already `x/2` would have to be rounded to 0.
|
||||||
- Here, for many applications, it is advantageous to allow also subnormal *(subnormal)* numbers and represent `x/2` as `0.500e-5` or `x/20` as `0.050e-5`.
|
- But for many applications, it is advantageous to allow also subnormal numbers and represent `x/2` as `0.500e-5` or `x/20` as `0.050e-5`.
|
||||||
- This *gradual underflow* is当然 associated with a loss of valid digits and thus accuracy.
|
- This *gradual underflow* is of course associated with a loss of significant digits and thus accuracy.
|
||||||
|
|
||||||
In the `Float` data type, such *subnormal values* are represented by an exponent field in which all bits are equal to zero:
|
In the `Float` data type, such *subnormal values* are represented by an exponent field in which all bits are equal to zero:
|
||||||
|
|
||||||
@@ -663,7 +662,7 @@ end
|
|||||||
|
|
||||||
## Special Values
|
## Special Values
|
||||||
|
|
||||||
Floating-point arithmetic knows some special values, e.g.
|
Floating-point arithmetic defines certain special values, e.g.,
|
||||||
```{julia}
|
```{julia}
|
||||||
nextfloat(floatmax(Float64))
|
nextfloat(floatmax(Float64))
|
||||||
```
|
```
|
||||||
@@ -675,16 +674,16 @@ for x ∈ (NaN, Inf, -Inf, -0.0)
|
|||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
- An exponent overflow *(overflow)* leads to the result `Inf` or `-Inf`.
|
- An exponent overflow leads to the result `Inf` or `-Inf`.
|
||||||
```{julia}
|
```{julia}
|
||||||
2/0, -3/0, floatmax(Float64) * 1.01, exp(1300)
|
2/0, -3/0, floatmax(Float64) * 1.01, exp(1300)
|
||||||
```
|
```
|
||||||
- One can continue calculating with it:
|
- One can continue calculating with these values:
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
-Inf + 20, Inf/30, 23/-Inf, sqrt(Inf), Inf * 0, Inf - Inf
|
-Inf + 20, Inf/30, 23/-Inf, sqrt(Inf), Inf * 0, Inf - Inf
|
||||||
```
|
```
|
||||||
- `NaN` *(Not a Number)* stands for the result of an operation that is undefined. All further operations with `NaN` also result in `NaN`.
|
- `NaN` *(Not a Number)* represents the result of an undefined operation. All further operations with `NaN` also result in `NaN`.
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
0/0, Inf - Inf, 2.3NaN, sqrt(NaN)
|
0/0, Inf - Inf, 2.3NaN, sqrt(NaN)
|
||||||
@@ -698,7 +697,7 @@ y = Inf - Inf
|
|||||||
@show x==y NaN==NaN isfinite(NaN) isinf(NaN) isnan(x) isnan(y);
|
@show x==y NaN==NaN isfinite(NaN) isinf(NaN) isnan(x) isnan(y);
|
||||||
```
|
```
|
||||||
|
|
||||||
- There is a "minus zero". It signals an exponent underflow *(underflow)* of a magnitude that has become too small *negative* quantity.
|
- There is a "minus zero". It signals a numerical underflow of a small *negative* quantity.
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
@show 23/-Inf -2/exp(1200) -0.0==0.0;
|
@show 23/-Inf -2/exp(1200) -0.0==0.0;
|
||||||
@@ -709,7 +708,7 @@ y = Inf - Inf
|
|||||||
Julia has the [usual mathematical functions](https://docs.julialang.org/en/v1/manual/mathematical-operations/#Rounding-functions)
|
Julia has the [usual mathematical functions](https://docs.julialang.org/en/v1/manual/mathematical-operations/#Rounding-functions)
|
||||||
`sqrt, exp, log, log2, log10, sin, cos,..., asin, acos,..., sinh,..., gcd, lcm, factorial,...,abs, max, min,...`,
|
`sqrt, exp, log, log2, log10, sin, cos,..., asin, acos,..., sinh,..., gcd, lcm, factorial,...,abs, max, min,...`,
|
||||||
|
|
||||||
including e.g. the [rounding functions](https://de.wikipedia.org/wiki/Abrundungsfunktion_und_Aufrundungsfunktion)
|
including e.g. the [rounding functions](https://en.wikipedia.org/wiki/Floor_and_ceiling_functions)
|
||||||
|
|
||||||
- `floor(T,x)` = $\lfloor x \rfloor$
|
- `floor(T,x)` = $\lfloor x \rfloor$
|
||||||
- `ceil(T,x)` = $\lceil x \rceil$
|
- `ceil(T,x)` = $\lceil x \rceil$
|
||||||
@@ -722,7 +721,7 @@ floor(3.4), floor(Int64, 3.5), floor(Int64, -3.5)
|
|||||||
ceil(3.4), ceil(Int64, 3.5), ceil(Int64, -3.5)
|
ceil(3.4), ceil(Int64, 3.5), ceil(Int64, -3.5)
|
||||||
```
|
```
|
||||||
|
|
||||||
Also worth noting is `atan(y, x)`, the [two-argument arctangent](https://de.wikipedia.org/wiki/Arctan2). In other programming languages, it is often implemented as a function with its own name *atan2*.
|
Also worth noting is `atan(y, x)`, the two-argument arctangent (known as `atan2` in many programming languages, see [atan2](https://en.wikipedia.org/wiki/Atan2)).
|
||||||
This solves the problem of converting from Cartesian to polar coordinates without awkward case distinctions.
|
This solves the problem of converting from Cartesian to polar coordinates without awkward case distinctions.
|
||||||
|
|
||||||
- `atan(y,x)` is the angle of the polar coordinates of (x,y) in the interval $(-\pi,\pi]$. In the 1st and 4th quadrants, it is therefore equal to `atan(y/x)`
|
- `atan(y,x)` is the angle of the polar coordinates of (x,y) in the interval $(-\pi,\pi]$. In the 1st and 4th quadrants, it is therefore equal to `atan(y/x)`
|
||||||
@@ -732,9 +731,9 @@ atan(3, -2), atan(-3, 2), atan(-3/2)
|
|||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
## Conversion Strings $\Longleftrightarrow$ Numbers
|
## Conversion Between Strings and Numbers
|
||||||
|
|
||||||
Conversion is possible with the functions `parse()` and `string()`.
|
Use the functions `parse()` and `string()` for such conversions:
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
parse(Int64, "1101", base=2)
|
parse(Int64, "1101", base=2)
|
||||||
|
|||||||
@@ -9,10 +9,27 @@ engine: julia
|
|||||||
#| echo: false
|
#| echo: false
|
||||||
#| output: false
|
#| output: false
|
||||||
using InteractiveUtils
|
using InteractiveUtils
|
||||||
import QuartoNotebookWorker
|
|
||||||
Base.stdout = QuartoNotebookWorker.with_context(stdout)
|
function Base.show(io::IO, x::T) where T
|
||||||
myactive_module() = Main.Notebook
|
if parentmodule(T) == @__MODULE__
|
||||||
Base.active_module() = myactive_module()
|
# Print "TypeName(fields...)" without module prefix
|
||||||
|
print(io, nameof(T), "(")
|
||||||
|
fields = fieldnames(T)
|
||||||
|
for (i, f) in enumerate(fields)
|
||||||
|
print(io, getfield(x, f))
|
||||||
|
i < length(fields) && print(io, ", ")
|
||||||
|
end
|
||||||
|
print(io, ")")
|
||||||
|
else
|
||||||
|
invoke(Base.show, Tuple{IO, Any}, io, x)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
##import QuartoNotebookWorker
|
||||||
|
##Base.stdout = QuartoNotebookWorker.with_context(stdout)
|
||||||
|
##myactive_module() = Main.Notebook
|
||||||
|
##Base.active_module() = myactive_module()
|
||||||
# https://github.com/JuliaLang/julia/blob/master/base/show.jl#L516-L520
|
# https://github.com/JuliaLang/julia/blob/master/base/show.jl#L516-L520
|
||||||
# https://github.com/JuliaLang/julia/blob/master/base/show.jl#L3073-L3077
|
# https://github.com/JuliaLang/julia/blob/master/base/show.jl#L3073-L3077
|
||||||
```
|
```
|
||||||
@@ -28,25 +45,23 @@ We want to introduce a new numeric type **complex numbers in polar representatio
|
|||||||
A first attempt could look like this:
|
A first attempt could look like this:
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
struct PComplex1{T <: AbstractFloat} <: Number
|
struct PComplex{T <: AbstractFloat} <: Number
|
||||||
r :: T
|
r :: T
|
||||||
ϕ :: T
|
ϕ :: T
|
||||||
end
|
end
|
||||||
|
|
||||||
z1 = PComplex1(-32.0, 33.0)
|
#const PComplex = Main.Notebook.PComplex #| hide_line
|
||||||
z2 = PComplex1{Float32}(12, 13)
|
#Base.show(io::IO, ::Type{PComplex}) = print(io, "PComplex") #| hide_line
|
||||||
|
z1 = PComplex(-32.0, 33.0)
|
||||||
|
z2 = PComplex{Float32}(12, 13)
|
||||||
@show z1 z2;
|
@show z1 z2;
|
||||||
```
|
```
|
||||||
|
|
||||||
:::{.callout-warning collapse="true" .titlenormal}
|
|
||||||
##
|
|
||||||
It is not possible to redefine a `struct` once it has been defined in a Julia session. Therefore, I use different names. Another possibility is, for example, the use of [`ProtoStructs.jl`](https://juliahub.com/ui/Packages/General/ProtoStructs).
|
|
||||||
:::
|
|
||||||
|
|
||||||
Julia automatically provides *default constructors*:
|
Julia automatically provides *default constructors*:
|
||||||
|
|
||||||
- the constructor `PComplex1`, where the type `T` is inferred from the passed arguments, and
|
- The constructor `PComplex` infers type `T` from the arguments, and
|
||||||
- constructors `PComplex{Float64},...` with explicit type specification. Here, the arguments are attempted to be converted to the requested type.
|
- Constructors like `PComplex{Float64}` accept explicit type specifications. Arguments are converted to the requested type.
|
||||||
|
|
||||||
------
|
------
|
||||||
|
|
||||||
@@ -93,9 +108,9 @@ end
|
|||||||
```{julia}
|
```{julia}
|
||||||
z1 = PComplex{Float64}(-3.3, 7π+1)
|
z1 = PComplex{Float64}(-3.3, 7π+1)
|
||||||
```
|
```
|
||||||
For explicitly specifying an *inner constructor*, we pay a price: Julia's *default constructors* are no longer available.
|
However, explicitly specifying an *inner constructor* has a consequence: Julia's *default constructors* are no longer available.
|
||||||
|
|
||||||
The constructor without explicit type specification in curly braces, which takes over the type of the arguments, is also desired:
|
The constructor without explicit type specification, which infers the type from the arguments, is also needed:
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
PComplex(r::T, ϕ::T) where {T<:AbstractFloat} = PComplex{T}(r,ϕ)
|
PComplex(r::T, ϕ::T) where {T<:AbstractFloat} = PComplex{T}(r,ϕ)
|
||||||
@@ -134,14 +149,14 @@ Details will be discussed in a later chapter.
|
|||||||
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||
The angle bracket symbol `∠` is not available as an operator symbol. We use `⋖` as an alternative. This can be entered in Julia as `\lessdot<tab>`.
|
The angle bracket symbol `∠` is not available as a Julia operator. We use `⋖` as an alternative, entered as `\lessdot<Tab>`.
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
⋖(r::Real, ϕ::Real) = PComplex(r, π*ϕ/180)
|
⋖(r::Real, ϕ::Real) = PComplex(r, π*ϕ/180)
|
||||||
|
|
||||||
z3 = 2. ⋖ 90.
|
z3 = 2. ⋖ 90.
|
||||||
```
|
```
|
||||||
(The type annotation -- `Real` instead of `AbstractFloat` -- is a preview of further constructors to come. For now, the operator `⋖` only works with `Float`s.)
|
(The type annotation `Real` instead of `AbstractFloat` anticipates further constructors. Currently, the operator `⋖` works only with `Float64`.)
|
||||||
|
|
||||||
|
|
||||||
Of course, we also want the output to look nice. Details can be found in the [documentation](https://docs.julialang.org/en/v1/manual/types/#man-custom-pretty-printing).
|
Of course, we also want the output to look nice. Details can be found in the [documentation](https://docs.julialang.org/en/v1/manual/types/#man-custom-pretty-printing).
|
||||||
@@ -150,7 +165,7 @@ Of course, we also want the output to look nice. Details can be found in the [do
|
|||||||
using Printf
|
using Printf
|
||||||
|
|
||||||
function Base.show(io::IO, z::PComplex)
|
function Base.show(io::IO, z::PComplex)
|
||||||
# we print the phase in degrees, rounded to tenths of a degree,
|
# print phase in degrees, rounded to one decimal place
|
||||||
p = z.ϕ * 180/π
|
p = z.ϕ * 180/π
|
||||||
sp = @sprintf "%.1f" p
|
sp = @sprintf "%.1f" p
|
||||||
print(io, z.r, "⋖", sp, '°')
|
print(io, z.r, "⋖", sp, '°')
|
||||||
@@ -162,20 +177,20 @@ end
|
|||||||
|
|
||||||
## Methods for `PComplex`
|
## Methods for `PComplex`
|
||||||
|
|
||||||
For our type to be a proper member of the family of types derived from `Number`, we need a whole lot more. Arithmetic, comparison operators, conversions, etc. must be defined.
|
For our type to be a proper member of the family of types derived from `Number`, additional functionality is required: arithmetic operations, comparison operators, and conversions must all be defined.
|
||||||
|
|
||||||
|
|
||||||
We limit ourselves to multiplication and square roots.
|
We focus on multiplication and square root operations.
|
||||||
|
|
||||||
|
|
||||||
:::{.callout-note collapse="true"}
|
:::{.callout-note collapse="false"}
|
||||||
## Modules
|
## Modules
|
||||||
|
|
||||||
- To add to the `methods` of existing functions and operations, one must address them with their 'full name'.
|
- Adding methods to existing functions requires using their fully qualified names.
|
||||||
- All objects belong to a namespace or `module`.
|
- All objects belong to a namespace or `module`.
|
||||||
- Most basic functions belong to the module `Base`, which is always loaded without explicit `using ...` by default.
|
- Most basic functions belong to `Base`, which is loaded automatically.
|
||||||
- As long as one does not define own modules, own definitions are in the module `Main`.
|
- Without user-defined modules, definitions reside in `Main`.
|
||||||
- The macro `@which`, applied to a name, shows in which module the name is defined.
|
- The macro `@which` applied to a name shows its defining module.
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
f(x) = 3x^3
|
f(x) = 3x^3
|
||||||
@@ -191,7 +206,7 @@ println("Module for addition: $wp, Module for sqrt: $ws")
|
|||||||
:::
|
:::
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
qwurzel(z::PComplex) = PComplex(sqrt(z.r), z.ϕ / 2)
|
sqrt_polar(z::PComplex) = PComplex(sqrt(z.r), z.ϕ / 2)
|
||||||
```
|
```
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
@@ -212,9 +227,9 @@ The function `sqrt()` already has some methods:
|
|||||||
length(methods(sqrt))
|
length(methods(sqrt))
|
||||||
```
|
```
|
||||||
|
|
||||||
Now it will have one more method:
|
Adding one more method:
|
||||||
```{julia}
|
```{julia}
|
||||||
Base.sqrt(z::PComplex) = qwurzel(z)
|
Base.sqrt(z::PComplex) = sqrt_polar(z)
|
||||||
|
|
||||||
length(methods(sqrt))
|
length(methods(sqrt))
|
||||||
```
|
```
|
||||||
@@ -223,26 +238,26 @@ length(methods(sqrt))
|
|||||||
sqrt(z2)
|
sqrt(z2)
|
||||||
```
|
```
|
||||||
|
|
||||||
and now for multiplication:
|
For multiplication:
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
Base.:*(x::PComplex, y::PComplex) = PComplex(x.r * y.r, x.ϕ + y.ϕ)
|
Base.:*(x::PComplex, y::PComplex) = PComplex(x.r * y.r, x.ϕ + y.ϕ)
|
||||||
|
|
||||||
@show z1 * z2;
|
@show z1 * z2;
|
||||||
```
|
```
|
||||||
(Since the operator symbol is not a normal name, the colon must be with `Base.` in the composition.)
|
(Since `:` is not a valid identifier character, it must be qualified with `Base.`)
|
||||||
|
|
||||||
We can, however, not yet multiply with other numeric types. One could now define a large number of corresponding methods. Julia provides one more mechanism for *numeric types* that simplifies this somewhat.
|
However, multiplication with other numeric types is not yet supported. Many corresponding methods could be defined, but Julia provides another mechanism for *numeric types* that simplifies this:
|
||||||
|
|
||||||
|
|
||||||
## Type Promotion and Conversion
|
## Type Promotion and Conversion
|
||||||
|
|
||||||
In Julia, one can naturally use the most diverse numeric types side by side.
|
Julia supports freely mixing various numeric types:
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
1//3 + 5 + 5.2 + 0xff
|
1//3 + 5 + 5.2 + 0xff
|
||||||
```
|
```
|
||||||
If one looks at the numerous methods defined, for example, for `+` and `*`, one finds among them a kind of 'catch-all definition'
|
Among the numerous methods defined for `+` and `*`, we find a catch-all definition:
|
||||||
|
|
||||||
```julia
|
```julia
|
||||||
+(x::Number, y::Number) = +(promote(x,y)...)
|
+(x::Number, y::Number) = +(promote(x,y)...)
|
||||||
@@ -251,7 +266,7 @@ If one looks at the numerous methods defined, for example, for `+` and `*`, one
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
(The 3 dots are the splat operator, which decomposes the tuple returned by promote() back into its components.)
|
(The three dots form the splat operator, which decomposes the tuple returned by `promote()` into its components.)
|
||||||
|
|
||||||
Since the method with the types `(Number, Number)` is very general, it is only used when more specific methods do not apply.
|
Since the method with the types `(Number, Number)` is very general, it is only used when more specific methods do not apply.
|
||||||
|
|
||||||
@@ -273,12 +288,12 @@ z = promote(BigInt(33), 27)
|
|||||||
The function `promote()` uses two helpers, the functions
|
The function `promote()` uses two helpers, the functions
|
||||||
`promote_type(T1, T2)` and `convert(T, x)`
|
`promote_type(T1, T2)` and `convert(T, x)`
|
||||||
|
|
||||||
As usual in Julia, one can extend this mechanism with [custom *promotion rules* and `convert(T,x)` methods.](https://docs.julialang.org/en/v1/manual/conversion-and-promotion/)
|
As usual in Julia, we can extend this mechanism with our own custom [*promotion rules* and `convert(T,x)` methods.](https://docs.julialang.org/en/v1/manual/conversion-and-promotion/)
|
||||||
|
|
||||||
|
|
||||||
### The Function `promote_type(T1, T2,...)`
|
### The Function `promote_type(T1, T2,...)`
|
||||||
|
|
||||||
It determines to which type conversion should take place. Arguments are types, not values.
|
It determines to which type the conversion should take place. Arguments are types, not values.
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
@show promote_type(Rational{Int64}, ComplexF64, Float32);
|
@show promote_type(Rational{Int64}, ComplexF64, Float32);
|
||||||
@@ -303,7 +318,7 @@ z = convert(Int64, 23.00)
|
|||||||
z = convert(Int64, 2.3)
|
z = convert(Int64, 2.3)
|
||||||
```
|
```
|
||||||
|
|
||||||
The special role of `convert()` lies in the fact that it is used *implicitly* and automatically at various points:
|
The special role of `convert()` is that it is called implicitly at various points:
|
||||||
|
|
||||||
> [The following language constructs call convert](https://docs.julialang.org/en/v1/manual/conversion-and-promotion/#When-is-convert-called?):
|
> [The following language constructs call convert](https://docs.julialang.org/en/v1/manual/conversion-and-promotion/#When-is-convert-called?):
|
||||||
>
|
>
|
||||||
@@ -315,14 +330,14 @@ The special role of `convert()` lies in the fact that it is used *implicitly* an
|
|||||||
|
|
||||||
-- and of course in `promote()`
|
-- and of course in `promote()`
|
||||||
|
|
||||||
For self-defined data types, one can extend convert() with further methods.
|
For user-defined types, `convert()` can be extended with custom methods.
|
||||||
|
|
||||||
For data types within the Number hierarchy, there is again a 'catch-all definition'
|
Within the `Number` hierarchy, a generic method handles conversions:
|
||||||
```julia
|
```julia
|
||||||
convert(::Type{T}, x::Number) where {T<:Number} = T(x)
|
convert(::Type{T}, x::Number) where {T<:Number} = T(x)
|
||||||
```
|
```
|
||||||
|
|
||||||
So: If for a type `T` from the hierarchy `T<:Number` there exists a constructor `T(x)` with a numeric argument `x`, then this constructor `T(x)` is automatically used for conversions. (Of course, more specific methods for `convert()` can also be defined, which then have priority.)
|
Therefore: If a type `T<:Number` has a constructor `T(x)` accepting a numeric argument, this constructor is automatically used for conversions. (More specific methods for `convert()` can also be defined and will take priority.)
|
||||||
|
|
||||||
|
|
||||||
### Further Constructors for `PComplex`
|
### Further Constructors for `PComplex`
|
||||||
@@ -330,7 +345,7 @@ So: If for a type `T` from the hierarchy `T<:Number` there exists a constructor
|
|||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
|
|
||||||
## (a) r, ϕ arbitrary reals, e.g. Integers, Rationals
|
## (a) Arbitrary real types for r and ϕ (e.g., integers, rationals)
|
||||||
|
|
||||||
PComplex{T}(r::T1, ϕ::T2) where {T<:AbstractFloat, T1<:Real, T2<: Real} =
|
PComplex{T}(r::T1, ϕ::T2) where {T<:AbstractFloat, T1<:Real, T2<: Real} =
|
||||||
PComplex{T}(convert(T, r), convert(T, ϕ))
|
PComplex{T}(convert(T, r), convert(T, ϕ))
|
||||||
@@ -358,7 +373,7 @@ PComplex(z::Complex{S}) where {S<:Real} =
|
|||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
A test of the new constructors:
|
Testing the new constructors:
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
|
|
||||||
@@ -367,7 +382,7 @@ A test of the new constructors:
|
|||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
We now still need *promotion rules* that determine which type should result from `promote(x::T1, y::T2)`. This internally extends `promote_type()` with the necessary further methods.
|
*Promotion rules* are needed to determine the result type of `promote(x::T1, y::T2)`. This mechanism extends `promote_type()` with the necessary methods.
|
||||||
|
|
||||||
### *Promotion rules* for `PComplex`
|
### *Promotion rules* for `PComplex`
|
||||||
|
|
||||||
@@ -379,16 +394,16 @@ Base.promote_rule(::Type{PComplex{T}}, ::Type{S}) where {T<:AbstractFloat,S<:Rea
|
|||||||
Base.promote_rule(::Type{PComplex{T}}, ::Type{Complex{S}}) where
|
Base.promote_rule(::Type{PComplex{T}}, ::Type{Complex{S}}) where
|
||||||
{T<:AbstractFloat,S<:Real} = PComplex{promote_type(T,S)}
|
{T<:AbstractFloat,S<:Real} = PComplex{promote_type(T,S)}
|
||||||
```
|
```
|
||||||
1. Rule:
|
1. **Rule:**
|
||||||
: If a `PComplex{T}` and an `S<:Real` meet, then both should be converted to `PComplex{U}`, where `U` is the type to which `S` and `T` can both be converted (_promoted_).
|
When a `PComplex{T}` and an `S<:Real` are combined, both convert to `PComplex{U}`, where `U` is the promoted type of `S` and `T`.
|
||||||
|
|
||||||
2. Rule
|
2. **Rule**
|
||||||
: If a `PComplex{T}` and a `Complex{S}` meet, then both should be converted to `PComplex{U}`, where `U` is the type to which `S` and `T` can be converted.
|
When a `PComplex{T}` and a `Complex{S}` are combined, both convert to `PComplex{U}`, where `U` is the promoted type of `S` and `T`.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Now multiplication with arbitrary numeric types works.
|
We can now multiply with arbitrary numeric types:
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
z3, 3z3
|
z3, 3z3
|
||||||
@@ -453,7 +468,7 @@ PComplex(z::Complex{S}) where {S<:Real} =
|
|||||||
using Printf
|
using Printf
|
||||||
|
|
||||||
function Base.show(io::IO, z::PComplex)
|
function Base.show(io::IO, z::PComplex)
|
||||||
# we print the phase in degrees, rounded to tenths of a degree,
|
# print phase in degrees, rounded to one decimal place
|
||||||
p = z.ϕ * 180/π
|
p = z.ϕ * 180/π
|
||||||
sp = @sprintf "%.1f" p
|
sp = @sprintf "%.1f" p
|
||||||
print(io, z.r, "⋖", sp, '°')
|
print(io, z.r, "⋖", sp, '°')
|
||||||
@@ -475,7 +490,7 @@ Base.promote_rule(::Type{PComplex{T}}, ::Type{Complex{S}}) where
|
|||||||
|
|
||||||
:::{.content-hidden unless-format="xxx"}
|
:::{.content-hidden unless-format="xxx"}
|
||||||
|
|
||||||
Now something like `PComplex(1, 0)` does not work yet. We also want to allow other real types for `r` and `ϕ`. For simplicity, we convert everything to `Float64` here. We proceed analogously if only one real or complex argument is used.
|
`PComplex(1, 0)` is not yet supported. Other real types for `r` and `ϕ` should also be supported. For simplicity, all types are converted to `Float64`. Analogous handling applies for single real or complex arguments.
|
||||||
|
|
||||||
```julia
|
```julia
|
||||||
PComplex(r::Real, ϕ::Real) = PComplex(Float64(r), Float64(ϕ))
|
PComplex(r::Real, ϕ::Real) = PComplex(Float64(r), Float64(ϕ))
|
||||||
|
|||||||
@@ -18,15 +18,15 @@ using InteractiveUtils
|
|||||||
- Names may contain letters, digits, underscores `_`, and exclamation marks `!`.
|
- Names may contain letters, digits, underscores `_`, and exclamation marks `!`.
|
||||||
- The first character must be a letter or an underscore.
|
- The first character must be a letter or an underscore.
|
||||||
- Case is significant: `Nmax` and `NMAX` are different variables.
|
- Case is significant: `Nmax` and `NMAX` are different variables.
|
||||||
- The character set used is [Unicode](https://home.unicode.org/), which provides access to over 150 scripts and numerous symbols.
|
- The character set used is [Unicode](https://home.unicode.org/), which covers over 150 scripts and numerous symbols.
|
||||||
- There is a short [list of reserved keywords](https://docs.julialang.org/en/v1/base/base/#Keywords): `if, then, function, true, false,...`
|
- There is a short [list of reserved keywords](https://docs.julialang.org/en/v1/base/base/#Keywords): `if, then, function, true, false,...`
|
||||||
|
|
||||||
:::{.callout-tip}
|
:::{.callout-tip}
|
||||||
## Example
|
## Example
|
||||||
|
|
||||||
Permissible: `i, x, Ω, x2, DieUnbekannteZahl, neuer_Wert, 🎷, Zähler, лічильник, einself!!!!,...`
|
Permissible: `i, x, Ω, x2, TheUnknownNumber, new_Value, 🎷, Counter_2, лічильник, yes!!!!,...`
|
||||||
|
|
||||||
Impermissible: `Uwe's_Funktion, 3achsen, A#B, $this_is_not_Perl, true,...`
|
Impermissible: `Karen's_Funktion, 3achsen, A#B, $this_is_not_Perl, true,...`
|
||||||
:::
|
:::
|
||||||
|
|
||||||
----
|
----
|
||||||
@@ -35,7 +35,7 @@ Impermissible: `Uwe's_Funktion, 3achsen, A#B, $this_is_not_Perl, true,...`
|
|||||||
## Note:
|
## Note:
|
||||||
|
|
||||||
In addition to the *reserved keywords* of the core language, numerous additional functions and objects are predefined, such as the mathematical functions `sqrt(), log(), sin()`.
|
In addition to the *reserved keywords* of the core language, numerous additional functions and objects are predefined, such as the mathematical functions `sqrt(), log(), sin()`.
|
||||||
These definitions are found in the module `Base`, which Julia loads automatically at startup.
|
These definitions are found in the `Base` module, which Julia loads automatically on startup.
|
||||||
Names from `Base` can be redefined as long as they have not yet been used:
|
Names from `Base` can be redefined as long as they have not yet been used:
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
@@ -43,7 +43,7 @@ Names from `Base` can be redefined as long as they have not yet been used:
|
|||||||
log = 3
|
log = 3
|
||||||
1 + log
|
1 + log
|
||||||
```
|
```
|
||||||
Now, of course, the logarithm is broken:
|
Now, of course, `log()` no longer works:
|
||||||
```{julia}
|
```{julia}
|
||||||
#| error: true
|
#| error: true
|
||||||
x = log(10)
|
x = log(10)
|
||||||
@@ -53,7 +53,7 @@ x = log(10)
|
|||||||
|
|
||||||
## Statements
|
## Statements
|
||||||
|
|
||||||
- In normal circumstances, each line contains one statement.
|
- Usually, each line contains one statement.
|
||||||
- If a statement is recognizable as incomplete at the end of a line through
|
- If a statement is recognizable as incomplete at the end of a line through
|
||||||
- open parentheses
|
- open parentheses
|
||||||
- operators,
|
- operators,
|
||||||
@@ -84,14 +84,14 @@ x = sum([i^2 for i=1:10]);
|
|||||||
|
|
||||||
:::{.callout-warning }
|
:::{.callout-warning }
|
||||||
|
|
||||||
For multi-line statements, the line to be continued must end with an open operator or parenthesis.
|
For multi-line statements, a continued line should end with an open operator or parenthesis.
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
x = sin(π/2) +
|
x = sin(π/2) +
|
||||||
3 * cos(0)
|
3 * cos(0)
|
||||||
```
|
```
|
||||||
|
|
||||||
Therefore, the following goes wrong, but—unfortunately—**without an error message**!
|
Therefore, the following fails, but—unfortunately—**without an error message**!
|
||||||
```{julia}
|
```{julia}
|
||||||
#| error: true
|
#| error: true
|
||||||
#| warning: true
|
#| warning: true
|
||||||
@@ -122,26 +122,26 @@ x = 2 # everything from '#' to the end of the line is a comment and is ignore
|
|||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
#=
|
#=
|
||||||
Single and multi-line comments can be enclosed between `#= ... =#`. Nested comments are possible.
|
Single and multi-line comments can be enclosed in `#= ... =#`. Nested comments are possible.
|
||||||
`#=`
|
`#=`
|
||||||
i.e., unlike in C/C++/Java, the comment does not end with the first comment-end character, but the `#=...=#` pairs act like parentheses.
|
i.e., unlike in C/C++/Java, the comment does not end with the first comment-end character, but the `#=...=#` pairs act like parentheses.
|
||||||
`=#`
|
`=#`
|
||||||
The automatic 'syntax highlighter' does not yet know this, as the alternating
|
The automatic 'syntax highlighter' doesn't support this yet, as the alternating
|
||||||
gray shading of this comment shows.
|
gray shading of this comment shows.
|
||||||
=#
|
=#
|
||||||
|
|
||||||
|
|
||||||
x #= das ist ein seltener Variablenname! =# = 3
|
x #= this is a really rare name for a variable! =# = 3
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
## Data Types Part I
|
## Data Types Part I
|
||||||
|
|
||||||
- Julia is a [strongly typed](https://de.wikipedia.org/wiki/Starke_Typisierung) language. All objects have a type. Functions and operations expect arguments of the correct type.
|
- Julia is a [strongly typed](https://en.wikipedia.org/wiki/Type_safety#Strong_and_weak_typing) language. All objects have a type. Functions and operations expect arguments of the correct type.
|
||||||
- Julia is a [dynamically typed](https://de.wikipedia.org/wiki/Dynamische_Typisierung) language. Variables have no type. They are names that can be bound to objects via assignment `x = ...`.
|
- Julia is a [dynamically typed](https://en.wikipedia.org/wiki/Dynamic_programming_language) language. Variables have no type. They are names that can be bound to objects via assignment `x = ...`.
|
||||||
- When speaking of the "type of a variable", one means the type of the object currently assigned to the variable.
|
- When speaking of the "type of a variable", one means the type of the object currently assigned to the variable.
|
||||||
- Functions and operators can implement different *methods* for different argument types.
|
- Functions and operators can implement different *methods* for different argument types.
|
||||||
- Depending on the concrete argument types, it is decided at function usage which method is used ([*dynamic dispatch*](https://en.wikipedia.org/wiki/Dynamic_dispatch)).
|
- Depending on the concrete argument types, it is decided at call time which method is selected ([*dynamic dispatch*](https://en.wikipedia.org/wiki/Dynamic_dispatch)).
|
||||||
|
|
||||||
|
|
||||||
Simple basic types are, for example:
|
Simple basic types are, for example:
|
||||||
@@ -162,7 +162,7 @@ x, typeof(x), sizeof(x)
|
|||||||
```
|
```
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
x = "Hallo!"
|
x = "Hello!"
|
||||||
x, typeof(x), sizeof(x)
|
x, typeof(x), sizeof(x)
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -179,7 +179,7 @@ x, typeof(x), sizeof(x)
|
|||||||
|
|
||||||
- `sizeof()` returns the size of an object or type in bytes (1 byte = 8 bits)
|
- `sizeof()` returns the size of an object or type in bytes (1 byte = 8 bits)
|
||||||
- 64-bit integers and 64-bit floating-point numbers correspond to the instruction set of modern processors and are therefore the standard numeric types.
|
- 64-bit integers and 64-bit floating-point numbers correspond to the instruction set of modern processors and are therefore the standard numeric types.
|
||||||
- Characters/*chars* `'A'` and strings/*strings* `"A"` of length 1 are different objects.
|
- Character literals like `'A'` and single-character strings like `"A"` are different objects.
|
||||||
|
|
||||||
## Control Flow
|
## Control Flow
|
||||||
|
|
||||||
@@ -193,7 +193,7 @@ x = 33
|
|||||||
y = 44
|
y = 44
|
||||||
z = 34
|
z = 34
|
||||||
|
|
||||||
if x < y && z != x # elseif- and else-branches are optional
|
if x < y && z != x # elseif or else branches are optional
|
||||||
println("yes")
|
println("yes")
|
||||||
x += 10
|
x += 10
|
||||||
elseif x < z # any number of elseif branches
|
elseif x < z # any number of elseif branches
|
||||||
@@ -201,7 +201,7 @@ elseif x < z # any number of elseif branches
|
|||||||
elseif x == z+1
|
elseif x == z+1
|
||||||
println(" x is successor of z")
|
println(" x is successor of z")
|
||||||
else # at most one else block
|
else # at most one else block
|
||||||
println("Alles falsch")
|
println("All wrong")
|
||||||
end # value of the entire block is the value of the
|
end # value of the entire block is the value of the
|
||||||
# last evaluated statement
|
# last evaluated statement
|
||||||
```
|
```
|
||||||
@@ -211,7 +211,7 @@ Short blocks can be written on one line:
|
|||||||
if x > 10 println("x is larger than 10") end
|
if x > 10 println("x is larger than 10") end
|
||||||
```
|
```
|
||||||
|
|
||||||
The value of an `if` block can of course be assigned:
|
The value of an `if` block can be assigned:
|
||||||
```{julia}
|
```{julia}
|
||||||
y = 33
|
y = 33
|
||||||
z = if y > 10
|
z = if y > 10
|
||||||
@@ -258,7 +258,7 @@ As usual, the equality test `==` must be distinguished from the assignment opera
|
|||||||
Well, almost anything:
|
Well, almost anything:
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
3 < "vier"
|
3 < "four"
|
||||||
```
|
```
|
||||||
|
|
||||||
The error message shows a few fundamental principles of Julia:
|
The error message shows a few fundamental principles of Julia:
|
||||||
@@ -283,7 +283,7 @@ Finally: comparisons can be chained.
|
|||||||
|
|
||||||
|
|
||||||
### Tests
|
### Tests
|
||||||
Some functions of type `f(c::Char) -> Bool`
|
Some functions of type `f(c::Char) -> Bool`
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
isnumeric('a'), isnumeric('7'), isletter('a')
|
isnumeric('a'), isnumeric('7'), isletter('a')
|
||||||
@@ -315,10 +315,10 @@ x ∈ [1, 2, 33, 4, 5]
|
|||||||
3 < 4 && !(2 > 8) && !contains("aaa", "b")
|
3 < 4 && !(2 > 8) && !contains("aaa", "b")
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Conditional Evaluation (_short circuit evaluation_)
|
#### Conditional Evaluation (_short-circuit evaluation_)
|
||||||
|
|
||||||
- in `a && b` `b` is only evaluated if `a == true`
|
- In `a && b`, `b` is only evaluated if `a == true`
|
||||||
- in `a || b` `b` is only evaluated if `a == false`
|
- In `a || b`, `b` is only evaluated if `a == false`
|
||||||
|
|
||||||
(i) Thus, `if test statement end` can also be written as `test && statement`.
|
(i) Thus, `if test statement end` can also be written as `test && statement`.
|
||||||
|
|
||||||
@@ -346,7 +346,7 @@ x = 3 < 4
|
|||||||
y = 5 ∈ [1, 2, 5, 7]
|
y = 5 ∈ [1, 2, 5, 7]
|
||||||
z = x && y
|
z = x && y
|
||||||
if z # equivalent to: if 3 < 4 && 5 in [1,2,5,7]
|
if z # equivalent to: if 3 < 4 && 5 in [1,2,5,7]
|
||||||
println("Stimmt alles!")
|
println("All correct!")
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -354,7 +354,7 @@ end
|
|||||||
- There is no implicit conversion such as *"0 is false and 1 (or anything != 0) is true"*
|
- There is no implicit conversion such as *"0 is false and 1 (or anything != 0) is true"*
|
||||||
- If `x` is a numeric type, then the C idiom `if(x)` must be written as `if x != 0`.
|
- If `x` is a numeric type, then the C idiom `if(x)` must be written as `if x != 0`.
|
||||||
- There is an exception to support the _short circuit evaluation_:
|
- There is an exception to support the _short circuit evaluation_:
|
||||||
- in the constructs `a && b && c...` or `a || b || c...` the last subexpression does not need to be of type `Bool` if these constructs are not used as tests in `if` or `while`:
|
- in the constructs `a && b && c...` or `a || b || c...` the last subexpression does not need to be of type `Bool` if these constructs are not used as tests in `if` or `while`:
|
||||||
|
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
@@ -367,9 +367,9 @@ z = 3 < 4 && 10 < 50 && sqrt(3^3)
|
|||||||
z, typeof(z)
|
z, typeof(z)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Loops *(loops)*
|
## Loops
|
||||||
|
|
||||||
### The `while` ("while") loop
|
### The `while` loop
|
||||||
|
|
||||||
|
|
||||||
Syntax:
|
Syntax:
|
||||||
@@ -408,7 +408,7 @@ while i<10
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
println("Fertig!")
|
println("Done!")
|
||||||
```
|
```
|
||||||
|
|
||||||
With `break` one can also exit infinite loops:
|
With `break` one can also exit infinite loops:
|
||||||
@@ -438,19 +438,19 @@ The loop body is executed for all items from a container.
|
|||||||
Instead of `in`, $\in$ can always be used. In the header of a `for` loop, `=` can also be used.
|
Instead of `in`, $\in$ can always be used. In the header of a `for` loop, `=` can also be used.
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
for i ∈ ["Mutter", "Vater", "Tochter"]
|
for i ∈ ["Mother", "Father", "Daughter"]
|
||||||
println(i)
|
println(i)
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
Often a numerical loop counter is needed. For this purpose, there is the *range* construct. The simplest forms are
|
A numerical loop counter is often needed. For this purpose, we have the `range` construct. The simplest forms are
|
||||||
`Start:End` and `Start:Step:End`.
|
`Start:End` and `Start:Step:End`.
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
endwert = 5
|
end_value = 5
|
||||||
|
|
||||||
for i ∈ 1:endwert
|
for i ∈ 1:end_value
|
||||||
println(i^2)
|
println(i^2)
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
@@ -471,8 +471,7 @@ for k = 14 : -2.5 : 1 print(" $k") end
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### Nested Loops _(nested loops)_
|
#### Nested Loops
|
||||||
|
|
||||||
A `break` ends the innermost loop.
|
A `break` ends the innermost loop.
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
@@ -486,7 +485,7 @@ for i = 1:3
|
|||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
*Nested loops* can also be combined in a single `for` statement. Then a `break` ends the entire loop.
|
Nested loops can also be combined in a single `for` statement. Then a `break` ends the entire loop.
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
for i = 1:3, j=1:3 # essentially the same as above, but:
|
for i = 1:3, j=1:3 # essentially the same as above, but:
|
||||||
@@ -526,7 +525,7 @@ end
|
|||||||
|
|
||||||
## Unicode
|
## Unicode
|
||||||
|
|
||||||
Julia uses Unicode as its character set. This allows identifiers from non-Latin scripts (e.g., Cyrillic, Korean, Sanskrit, runes,
|
Julia uses Unicode as its character set. This allows identifiers in non-Latin scripts (e.g., Cyrillic, Korean, Sanskrit, runes,
|
||||||
emojis,...) to be used for variables, functions, etc. The question of how one can enter such characters in their editor and whether the used screen font can display them is not Julia's problem.
|
emojis,...) to be used for variables, functions, etc. The question of how one can enter such characters in their editor and whether the used screen font can display them is not Julia's problem.
|
||||||
|
|
||||||
- Some Unicode characters, e.g., `≤, ≠, ≥, π, ∈, √`, can be used instead of `<=, !=, >=, pi, in, sqrt`.
|
- Some Unicode characters, e.g., `≤, ≠, ≥, π, ∈, √`, can be used instead of `<=, !=, >=, pi, in, sqrt`.
|
||||||
@@ -586,25 +585,25 @@ x = 4
|
|||||||
|
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
Wichtig = 21
|
Important = 21
|
||||||
Wichtig! = 42 # identifiers can also contain !
|
Important! = 42 # identifiers can also contain !
|
||||||
(Wichtig, Wichtig!)
|
(Important, Important!)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
Wichtig!=88
|
Important!=88
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
Julia interprets this as the comparison `Wichtig != 88`.
|
Julia interprets this as the *comparison* `Important != 88`.
|
||||||
|
|
||||||
Spaces help:
|
Again, spaces around operators help:
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
Wichtig! = 88
|
Important! = 88
|
||||||
Wichtig!
|
Important!
|
||||||
```
|
```
|
||||||
|
|
||||||
- Operators of the form `.*`, `.+`,... have a special meaning in Julia (*broadcasting*, i.e., vectorized operations).
|
- Operators of the form `.*`, `.+`,... have a special meaning in Julia (*broadcasting*, i.e., vectorized operations).
|
||||||
|
|||||||
@@ -17,20 +17,20 @@ Base.active_module() = myactive_module()
|
|||||||
|
|
||||||
# The Julia Type System
|
# The Julia Type System
|
||||||
|
|
||||||
One can write extensive programs in Julia without using a single type declaration. This is, of course, intentional and should simplify the work of users.
|
One can write extensive programs in Julia without using a single type declaration. This is, of course, intentional and designed to simplify users' work.
|
||||||
|
|
||||||
However, we will now take a look under the hood.
|
However, for a deeper understanding we will now examine the underlying type system.
|
||||||
|
|
||||||
## The Type Hierarchy: A Case Study with Numeric Types
|
## The Type Hierarchy: A Case Study with Numeric Types
|
||||||
|
|
||||||
The type system has the structure of a tree whose root is the type `Any`. The functions `subtypes()` and `supertype()` can be used to explore the tree. They show all children or the parent of a node.
|
The type system has the structure of a tree whose root is the type `Any`. The functions `subtypes()` and `supertype()` can be used to explore the tree. `subtypes()` displays all child nodes, while `supertype()` shows the parent.
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
subtypes(Int64)
|
subtypes(Int64)
|
||||||
```
|
```
|
||||||
The result is an empty list of types. `Int64` is a so-called **concrete type** and has no subtypes.
|
The result is an empty list of types. `Int64` is a so-called **concrete type** with no subtypes.
|
||||||
|
|
||||||
Let's now climb the type hierarchy on this branch to the root (in computer science, trees are always standing on their heads).
|
Let's now traverse this branch upward to the root (in computer science, trees are typically inverted).
|
||||||
```{julia}
|
```{julia}
|
||||||
supertype(Int64)
|
supertype(Int64)
|
||||||
```
|
```
|
||||||
@@ -52,11 +52,11 @@ This would have been faster, by the way: The function `supertypes()` (with plura
|
|||||||
supertypes(Int64)
|
supertypes(Int64)
|
||||||
```
|
```
|
||||||
|
|
||||||
Now one can look at the nodes:
|
We can now examine the nodes:
|
||||||
|
|
||||||
{{< embed ../notebooks/nb-types.ipynb#nb3 >}}
|
{{< embed ../notebooks/nb-types.ipynb#nb3 >}}
|
||||||
|
|
||||||
With a small recursive function, one can quickly print out an entire (sub-)tree:
|
A simple recursive function can display the entire subtree:
|
||||||
|
|
||||||
{{< embed ../notebooks/nb-types.ipynb#nb1 >}}
|
{{< embed ../notebooks/nb-types.ipynb#nb1 >}}
|
||||||
|
|
||||||
@@ -69,23 +69,23 @@ With a small recursive function, one can quickly print out an entire (sub-)tree:
|
|||||||
::::
|
::::
|
||||||
|
|
||||||
|
|
||||||
Here again as an image (made with LaTeX/[TikZ](https://tikz.dev/tikz-trees))
|
Below is the same hierarchy as an image (made with LaTeX/[TikZ](https://tikz.dev/tikz-trees)):
|
||||||
|
|
||||||
::: {.content-visible when-format="html"}
|
::: {.content-visible when-format="html"}
|
||||||
{width=80%}
|
{width=80%}
|
||||||
:::
|
:::
|
||||||
|
|
||||||
::: {.content-visible when-format="pdf"}
|
::: {.content-visible when-format="typst"}
|
||||||
{width=60%}
|
{width=60%}
|
||||||
:::
|
:::
|
||||||
|
|
||||||
|
|
||||||
Of course, Julia has not only numeric types. The number of direct descendants (children) of `Any` is
|
Beyond numeric types, Julia includes many others. The number of direct descendants (children) of `Any` is
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
length(subtypes(Any))
|
length(subtypes(Any))
|
||||||
```
|
```
|
||||||
and with (almost) every package loaded with `using ...`, this increases.
|
This number increases with (almost) every package loaded via `using ...`.
|
||||||
|
|
||||||
## Abstract and Concrete Types
|
## Abstract and Concrete Types
|
||||||
|
|
||||||
@@ -97,18 +97,18 @@ and with (almost) every package loaded with `using ...`, this increases.
|
|||||||
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||
- Abstract types cannot be instantiated, i.e., there are no objects of this type.
|
- Abstract types cannot be instantiated; that is, no objects can have an abstract type directly.
|
||||||
- They define a set of concrete types and common methods for these types.
|
- They define a set of concrete types and common methods for these types.
|
||||||
- They can therefore be used in the definition of function types, argument types, element types of composite types, etc.
|
- They can therefore be used in the definition of function types, argument types, element types of composite types, etc.
|
||||||
|
|
||||||
|
|
||||||
For **declaring** *and* **testing** the "descent" within the type hierarchy, there is a special operator:
|
To **declare** *and* **test** the relationships in the type hierarchy, Julia provides a special operator:
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
Int64 <: Number
|
Int64 <: Number
|
||||||
```
|
```
|
||||||
|
|
||||||
To test whether an object has a certain type (or an abstract supertype of it), `isa(object, typ)` is used. It is usually used in infix form and should be read as the question `x is a T?`.
|
To test whether an object has a certain type (or an abstract supertype of it), `isa(object, typ)` is used. It is usually used in infix form and reads as the question `is x a T?`.
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
x = 17.2
|
x = 17.2
|
||||||
@@ -117,7 +117,7 @@ x = 17.2
|
|||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
Since abstract types do not define data structures, their definition is quite simple. Either they are derived directly from `Any`:
|
Since abstract types do not define data structures, they are simple to define. Either they are derived directly from `Any`:
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
abstract type MySuperType end
|
abstract type MySuperType end
|
||||||
@@ -133,24 +133,24 @@ abstract type MySpecialNumber <: Integer end
|
|||||||
supertypes(MySpecialNumber)
|
supertypes(MySpecialNumber)
|
||||||
```
|
```
|
||||||
|
|
||||||
With the definition, the abstract types are "hung" at a point in the type tree.
|
By this definition, the abstract type is attached at a specific point in the type tree.
|
||||||
|
|
||||||
## The Numeric Types `Bool` and `Irrational`
|
## The Numeric Types `Bool` and `Irrational`
|
||||||
|
|
||||||
Since they are seen in the numeric type tree, they should be briefly explained:
|
Though appearing in the numeric type tree, these types warrant brief explanation:
|
||||||
|
|
||||||
`Bool` is numeric in the sense of `true=1, false=0`:
|
`Bool` is numeric in the sense that `true=1, false=0`:
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
true + true + true, false - true, sqrt(true), true/4
|
true + true + true, false - true, sqrt(true), true/4
|
||||||
```
|
```
|
||||||
|
|
||||||
`Irrational` is the type of some predefined constants such as `π` and `ℯ`.
|
`Irrational` is the type of certain predefined constants, such as `π` and `ℯ`.
|
||||||
According to the [documentation](https://docs.julialang.org/en/v1/base/numbers/#Base.AbstractIrrational), `Irrational` is a *"Number type representing an exact irrational value, which is automatically rounded to the correct precision in arithmetic operations with other numeric quantities".*
|
According to the [documentation](https://docs.julialang.org/en/v1/base/numbers/#Base.AbstractIrrational), `Irrational` is a *"Number type representing an exact irrational value, which is automatically rounded to the correct precision in arithmetic operations with other numeric quantities".*
|
||||||
|
|
||||||
## Union Types
|
## Union Types
|
||||||
|
|
||||||
If the tree hierarchy is not sufficient, one can also define abstract types as a union of arbitrary (abstract and concrete) types.
|
When the tree structure is insufficient, abstract types can be defined as a union of arbitrary (abstract and concrete) types.
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
IntOrString = Union{Int64,String}
|
IntOrString = Union{Int64,String}
|
||||||
@@ -159,16 +159,16 @@ IntOrString = Union{Int64,String}
|
|||||||
:::{.callout-note .titlenormal}
|
:::{.callout-note .titlenormal}
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
The command `methods(<)` shows that among the over 70 methods defined for the comparison operator, some also use *union types*, e.g., there is
|
The command `methods(<)` reveals over 70 methods for the comparison operator, including methods with union type arguments, such as:
|
||||||
```julia
|
```julia
|
||||||
<(x::Union{Float16, Float32, Float64}, y::BigFloat)
|
<(x::Union{Float16, Float32, Float64}, y::BigFloat)
|
||||||
```
|
```
|
||||||
a method for comparing a machine number of fixed length with a machine number of arbitrary length.
|
a method comparing fixed-width machine numbers with arbitrary precision numbers.
|
||||||
:::
|
:::
|
||||||
|
|
||||||
## Composite (_composite_) Types: `struct`
|
## Composite Types: `struct`
|
||||||
|
|
||||||
A `struct` is a collection of several named fields and defines a concrete type.
|
A `struct` defines a concrete type as a collection of named fields.
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
abstract type Point end
|
abstract type Point end
|
||||||
@@ -185,7 +185,7 @@ mutable struct Point3D <: Point
|
|||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
As we have already seen with expressions of the form `x = Int8(33)`, type names can be used directly as constructors:
|
As seen with expressions like `x = Int8(33)`, type names can serve as constructors:
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
p1 = Point2D(1.4, 3.5)
|
p1 = Point2D(1.4, 3.5)
|
||||||
@@ -197,20 +197,20 @@ p1 isa Point3D, p1 isa Point2D, p1 isa Point
|
|||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
The fields of a `struct` can be addressed by their name with the `.` operator.
|
The fields of a `struct` are accessed by name using the `.` operator.
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
p1.y
|
p1.y
|
||||||
```
|
```
|
||||||
|
|
||||||
Since we declared our `struct` as `mutable`, we can modify the object `p1` by assigning new values to the fields.
|
Because we declared our `struct` as `mutable`, we can modify the object `p1` by assigning new values to the fields.
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
p1.x = 3333.4
|
p1.x = 3333.4
|
||||||
p1
|
p1
|
||||||
```
|
```
|
||||||
|
|
||||||
Information about the structure of a type or an object of that type is provided by `dump()`.
|
The `dump()` function displays structure information for types and objects.
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
dump(Point3D)
|
dump(Point3D)
|
||||||
@@ -225,24 +225,23 @@ dump(Point3D)
|
|||||||
|
|
||||||
:::{.callout-note .titlenormal}
|
:::{.callout-note .titlenormal}
|
||||||
|
|
||||||
## Objects, Functions, Methods
|
## Objects, Functions, and Methods
|
||||||
|
|
||||||
In classical object-oriented languages like C++/Java, objects usually have functions associated with them, which are the methods of the object.
|
In classical object-oriented languages (C++, Java, Python), methods are bound to objects.
|
||||||
|
|
||||||
In Julia, methods belong to a function and not to an object.
|
Julia takes a different approach: methods belong to functions, not to objects.
|
||||||
(An exception is the constructors, i.e., functions that have the same name as a type and create an object of that type.)
|
Constructors (functions sharing a type's name that create instances of that type) are the only exception.
|
||||||
|
|
||||||
Once one has defined a new type, one can add new or existing functions around new methods for this type.
|
When we define a new type, we can define functions specific to that type but we can also add additional methods to existing functions.
|
||||||
|
|
||||||
- A function can be defined multiple times for different argument lists (type and number).
|
- A single functions can have multiple methods for different argument types.
|
||||||
- The function then has multiple methods.
|
- At call time, Julia selects the most specific method matching the concrete argument types *(multiple dispatch)*.
|
||||||
- When calling, it is decided based on the concrete arguments which method is used *(multiple dispatch)*.
|
- Core functions in Julia often have numerous predefined methods and third-party packages or user code can extend them adding additional methods.
|
||||||
- It is typical for Julia that for standard functions, many methods are defined. These can be easily extended by further methods for own types.
|
|
||||||
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||
|
|
||||||
We implement the distance between two points as a function with two methods:
|
We define a distance function with two methods:
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
function distance(p1::Point2D, p2::Point2D)
|
function distance(p1::Point2D, p2::Point2D)
|
||||||
@@ -263,7 +262,7 @@ As mentioned earlier, `methods()` shows the method table of a function:
|
|||||||
methods(distance)
|
methods(distance)
|
||||||
```
|
```
|
||||||
|
|
||||||
The macro `@which`, applied to a full function call with concrete argument list, shows which method is selected for these concrete arguments:
|
The `@which` macro, applied to a function call with concrete arguments, shows which method is selected for these arguments:
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
@which sqrt(3.3)
|
@which sqrt(3.3)
|
||||||
@@ -279,46 +278,46 @@ Methods can also have abstract types as arguments:
|
|||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
"""
|
"""
|
||||||
Calculates the angle ϕ (in degrees) of the polar coordinates (2D) or
|
Calculate the angle ϕ (in degrees) of the polar coordinates (2D) or
|
||||||
spherical coordinates (3D) of a point
|
spherical coordinates (3D) of a point
|
||||||
"""
|
"""
|
||||||
function phi_winkel(p::Point)
|
function phi_angle(p::Point)
|
||||||
atand(p.y, p.x)
|
atand(p.y, p.x)
|
||||||
end
|
end
|
||||||
|
|
||||||
phi_winkel(p1)
|
phi_angle(p1)
|
||||||
```
|
```
|
||||||
|
|
||||||
:::{.callout-tip collapse="true"}
|
:::{.callout-tip collapse="true"}
|
||||||
A text enclosed in *triple quotes* immediately before the function definition
|
Text enclosed in *triple quotes* immediately before the function definition
|
||||||
is automatically integrated into Julia's help database:
|
is automatically integrated into Julia's help database:
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
?phi_winkel
|
?phi_angle
|
||||||
```
|
```
|
||||||
:::
|
:::
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
With *multiple dispatch*, the method is applied that is the most specific among all matching ones. Here is a function with several methods
|
With *multiple dispatch*, the method is applied that is the most specific among all matching ones. Here is a function with several methods
|
||||||
(all but the last in the short [*assignment form*](https://docs.julialang.org/en/v1/manual/functions/#man-functions) written):
|
(all but the last written in short [*assignment form*](https://docs.julialang.org/en/v1/manual/functions/#man-functions)):
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
f(x::String, y::Number) = "Args: String + Zahl"
|
f(x::String, y::Number) = "Args: String + Number"
|
||||||
f(x::String, y::Int64) = "Args: String + Int64"
|
f(x::String, y::Int64) = "Args: String + Int64"
|
||||||
f(x::Number, y::Int64) = "Args: Zahl + Int64"
|
f(x::Number, y::Int64) = "Args: Number + Int64"
|
||||||
f(x::Int64, y:: Number) = "Args: Int64 + Zahl"
|
f(x::Int64, y:: Number) = "Args: Int64 + Number"
|
||||||
f(x::Number) = "Arg: eine Zahl"
|
f(x::Number) = "Arg: a Number"
|
||||||
|
|
||||||
function f(x::Number, y::Number, z::String)
|
function f(x::Number, y::Number, z::String)
|
||||||
return "Arg: 2 x Zahl + String"
|
return "Arg: 2 × Number + String"
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
Here, the first two methods match. The second is chosen since it is more specific, `Int64 <: Number`.
|
The first two methods match; the second is chosen as it is more specific (`Int64 <: Number`):
|
||||||
```{julia}
|
```{julia}
|
||||||
f("Hello", 42)
|
f("Hello", 42)
|
||||||
```
|
```
|
||||||
It may happen that this rule does not lead to a unique result if one chooses one's methods badly.
|
Ambiguities may arise if methods are defined poorly:
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
f(42, 42)
|
f(42, 42)
|
||||||
@@ -327,20 +326,19 @@ f(42, 42)
|
|||||||
|
|
||||||
## Parametric Numeric Types: `Rational` and `Complex`
|
## Parametric Numeric Types: `Rational` and `Complex`
|
||||||
|
|
||||||
|
|
||||||
- For rational numbers (fractions), Julia uses `//` as an infix constructor:
|
- For rational numbers (fractions), Julia uses `//` as an infix constructor:
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
@show Rational(23, 17) 4//16 + 1//3;
|
@show Rational(23, 17) 4//16 + 1//3;
|
||||||
```
|
```
|
||||||
|
|
||||||
- The imaginary unit $\sqrt{-1}$ is called `im`
|
- The imaginary unit $\sqrt{-1}$ is denoted `im`
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
@show Complex(0.4) 23 + 0.5im/(1-2im);
|
@show Complex(0.4) 23 + 0.5im/(1-2im);
|
||||||
```
|
```
|
||||||
|
|
||||||
`Rational` and `Complex` consist, similar to our `Point2D`, of 2 fields: numerator and denominator or real and imaginary part.
|
Like `Point2D`, both `Rational` and `Complex` consist of two fields: numerator and denominator or real and imaginary parts.
|
||||||
|
|
||||||
However, the type of these fields is not completely fixed. `Rational` and `Complex` are _parametric_ types.
|
However, the type of these fields is not completely fixed. `Rational` and `Complex` are _parametric_ types.
|
||||||
|
|
||||||
@@ -363,7 +361,7 @@ y = 1.0 + 2.0im
|
|||||||
typeof(y)
|
typeof(y)
|
||||||
```
|
```
|
||||||
|
|
||||||
The concrete types `Rational{Int64}`, `Rational{BigInt}`,..., `Complex{Int64}`, `Complex{Float64}}`, ... are subtypes of `Rational` resp. `Complex`.
|
The concrete types `Rational{Int64}`, `Rational{BigInt}`,..., `Complex{Int64}`, `Complex{Float64}}`,... are subtypes of `Rational` and `Complex`, respectively.
|
||||||
|
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
@@ -396,7 +394,7 @@ and the second says analogously:
|
|||||||
- This type `T` must be a subtype of `Integer`.
|
- This type `T` must be a subtype of `Integer`.
|
||||||
- `MyRational` and its variants are subtypes of `Real`.
|
- `MyRational` and its variants are subtypes of `Real`.
|
||||||
|
|
||||||
Now ℚ$\subset$ ℝ, or in Julia notation `Rational <: Real`. Thus, the components of a complex number can also be rational:
|
Since ℚ$\subset$ ℝ (or `Rational <: Real` in Julia notation), the components of a complex number can also be rational:
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
z = 3//4 + 5im
|
z = 3//4 + 5im
|
||||||
@@ -406,7 +404,7 @@ dump(z)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
These structures are defined without the `mutable` attribute, therefore *immutable*:
|
These structures are declared without the `mutable` attribute, making them *immutable*:
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
x = 2.2 + 3.3im
|
x = 2.2 + 3.3im
|
||||||
@@ -414,15 +412,15 @@ println("The real part is: $(x.re)")
|
|||||||
|
|
||||||
x.re = 4.4
|
x.re = 4.4
|
||||||
```
|
```
|
||||||
This is common. We also consider the object `9` of type `Int64` as immutable.
|
This is standard practice. The object `9` (of type `Int64`) is also immutable.
|
||||||
The following, of course, still works:
|
The following, of course, still works:
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
x += 2.2
|
x += 2.2
|
||||||
```
|
```
|
||||||
Here, a new object of type `Complex{Float64}` is created and `x` is made a reference to this new object.
|
Here, a new object of type `Complex{Float64}` is created and `x` then references this new object.
|
||||||
|
|
||||||
The possibilities of the type system easily invite to play. Here we define a `struct` that can contain either a machine number or a pair of integers:
|
The type system's flexibility invites experimentation. Here we define a `struct` that can contain either a machine number or a pair of integers:
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
struct MyParms{T <: Union{Float64, Tuple{Int64, Int64}}}
|
struct MyParms{T <: Union{Float64, Tuple{Int64, Int64}}}
|
||||||
@@ -440,7 +438,7 @@ p2 = MyParms( (2, 4) )
|
|||||||
|
|
||||||
## Types as Objects
|
## Types as Objects
|
||||||
|
|
||||||
(1) Types are also objects. They are objects of one of the three "meta-types"
|
(1) Types are also objects. Each type is an instance of one of three "meta-types":
|
||||||
|
|
||||||
- `Union` (union types)
|
- `Union` (union types)
|
||||||
- `UnionAll` (parametric types)
|
- `UnionAll` (parametric types)
|
||||||
@@ -460,7 +458,7 @@ p2 = MyParms( (2, 4) )
|
|||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
These 3 concrete "meta-types" are, by the way, subtypes of the abstract "meta-type" `Type`.
|
These three concrete "meta-types" are, by the way, subtypes of the abstract "meta-type" `Type`.
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
subtypes(Type)
|
subtypes(Type)
|
||||||
@@ -468,7 +466,7 @@ subtypes(Type)
|
|||||||
|
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
(2) Thus, types can also be easily assigned to variables:
|
(2) Types can be assigned to a variable:
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
x3 = Float64
|
x3 = Float64
|
||||||
@@ -477,15 +475,13 @@ x3 = Float64
|
|||||||
|
|
||||||
:::{.callout-note collapse="true"}
|
:::{.callout-note collapse="true"}
|
||||||
|
|
||||||
This also shows that the [style guidelines in Julia](https://docs.julialang.org/en/v1/manual/style-guide/#Use-naming-conventions-consistent-with-Julia-base/) such as "Types and type variables start with uppercase letters, other variables and functions are written in lowercase." are only conventions and are not enforced by the language.
|
This demonstrates that [Julia's style guidelines](https://docs.julialang.org/en/v1/manual/style-guide/#Use-naming-conventions-consistent-with-Julia-base/) such as "Types and type variables start with uppercase letters, other variables and functions are written in lowercase." are conventions, not language-enforced rules.
|
||||||
|
|
||||||
One should still follow them to keep the code readable.
|
They should still be followed for readability.
|
||||||
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||
|
Declaring such assignments with `const` creates a *type alias*.
|
||||||
If such assignments are declared permanent with `const`, a
|
|
||||||
*type alias* is created.
|
|
||||||
|
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
@@ -497,7 +493,7 @@ typeof(z)
|
|||||||
|
|
||||||
--------
|
--------
|
||||||
|
|
||||||
(3) Types can be arguments of functions.
|
(3) Types can be function arguments.
|
||||||
|
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
@@ -513,7 +509,7 @@ z = myf(43, UInt16, Real)
|
|||||||
@show z typeof(z);
|
@show z typeof(z);
|
||||||
```
|
```
|
||||||
|
|
||||||
If one wants to define this function with type signatures, of course one can write
|
To define this function with type signatures, we can write
|
||||||
```julia
|
```julia
|
||||||
function myf(x, S::Type, T::Type) ... end
|
function myf(x, S::Type, T::Type) ... end
|
||||||
```
|
```
|
||||||
@@ -521,9 +517,9 @@ However, the (equivalent) special syntax
|
|||||||
```julia
|
```julia
|
||||||
function myf(x, ::Type{S}, ::Type{T}) where {S,T} ... end
|
function myf(x, ::Type{S}, ::Type{T}) where {S,T} ... end
|
||||||
```
|
```
|
||||||
is more common here, where one can also impose restrictions on the permissible values of the type variables `S` and `T` in the `where` clause.
|
is more common. Here we can also impose restrictions on the permissible values of the type variables `S` and `T` in the `where` clause.
|
||||||
|
|
||||||
How do I define a special method of `myf` that should only be called when `S` and `T` are equal to `Int64`? This is possible as follows:
|
How can we define a special method of `myf` that should only be called when `S` and `T` are equal to `Int64`? This is possible as follows:
|
||||||
|
|
||||||
```julia
|
```julia
|
||||||
function myf(x, ::Type{Int64}, ::Type{Int64}) ... end
|
function myf(x, ::Type{Int64}, ::Type{Int64}) ... end
|
||||||
@@ -533,24 +529,24 @@ function myf(x, ::Type{Int64}, ::Type{Int64}) ... end
|
|||||||
|
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
(4) There are numerous operations with types as arguments. We have already seen `<:(T1, T2)`, `supertype(T)`, `supertypes(T)`, `subtypes(T)`. Mentioned should be still `typejoin(T1,T2)` (next common ancestor in the type tree) and tests like `isconcretetype(T)`, `isabstracttype(T)`, `isstructtype(T)`.
|
(4) There are numerous functions with types as arguments. We have already seen `<:(T1, T2)`, `supertype(T)`, `supertypes(T)`, `subtypes(T)`. Other useful operations include `typejoin(T1,T2)` (next common ancestor in the type tree) and tests like `isconcretetype(T)`, `isabstracttype(T)`, `isstructtype(T)`.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Invariance of Parametric Types {#sec-invariance}
|
## Invariance of Parametric Types {#sec-invariance}
|
||||||
|
|
||||||
Can non-concrete types also be substituted into parametric types? Are there `Complex{AbstractFloat}` or `Complex{Union{Float32, Int16}}`?
|
Can non-concrete types also be used as parameters in parametric types? Are there `Complex{AbstractFloat}` or `Complex{Union{Float32, Int16}}`?
|
||||||
|
|
||||||
Yes, they exist; and they are concrete types, one can therefore create objects of this type.
|
Yes, such types exist. They are concrete, and objects can be instantiated.
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
z5 = Complex{Integer}(2, 0x33)
|
z5 = Complex{Integer}(2, 0x33)
|
||||||
dump(z5)
|
dump(z5)
|
||||||
```
|
```
|
||||||
This is a heterogeneous structure. Each component has an individual type `T`, for which `T<:Integer` holds.
|
This is a heterogeneous composite type. Each component has an individual type `T`, for which `T<:Integer` holds.
|
||||||
|
|
||||||
Now it holds that
|
Note that
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
Int64 <: Integer
|
Int64 <: Integer
|
||||||
@@ -560,9 +556,8 @@ but it does not hold that
|
|||||||
Complex{Int64} <: Complex{Integer}
|
Complex{Int64} <: Complex{Integer}
|
||||||
```
|
```
|
||||||
|
|
||||||
These types are both concrete. Therefore, they cannot stand in a sub/supertype relation to each other in Julia's type hierarchy. Julia's parametric types are [in the language of theoretical computer science](https://en.wikipedia.org/wiki/Covariance_and_contravariance) **invariant**.
|
These types are both concrete. Therefore, they cannot stand in a sub/supertype relation to each other in Julia's type hierarchy. Julia's parametric types are [in theoretical computer science terminology](https://en.wikipedia.org/wiki/Type_variance), **invariant**.
|
||||||
(If from `S<:T` it would follow that also `ParamType{S} <: ParamType{T}`, one would speak of **covariance**.)
|
(If `S<:T` implied `ParamType{S} <: ParamType{T}`, this would be **covariance**.)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -571,41 +566,41 @@ These types are both concrete. Therefore, they cannot stand in a sub/supertype r
|
|||||||
The usual (and in many cases recommended!) programming style in Julia is writing generic functions:
|
The usual (and in many cases recommended!) programming style in Julia is writing generic functions:
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
function fsinnfrei1(x, y)
|
function fmm(x, y)
|
||||||
return x * x * y
|
return x * x * y
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
This function works immediately with all types for which the used operations are defined.
|
This function works with any types supporting the required operations.
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
fsinnfrei1( Complex(2,3), 10), fsinnfrei1("Hello", '!')
|
fmm( Complex(2,3), 10), fmm("Hello", '!')
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
One can of course use type annotations to restrict usability or to implement different methods for different types:
|
Type annotations can restrict applicability or implement different methods for different types:
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
function fsinnfrei2(x::Number, y::AbstractFloat)
|
function fmm2(x::Number, y::AbstractFloat)
|
||||||
return x * x * y
|
return x * x * y
|
||||||
end
|
end
|
||||||
|
|
||||||
function fsinnfrei2(x::String, y::String)
|
function fmm2(x::String, y::String)
|
||||||
println("Sorry, I don't take strings!")
|
println("Sorry, I don't take strings!")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@show fsinnfrei2(18, 2.0) fsinnfrei2(18, 2);
|
@show fmm2(18, 2.0) fmm2(18, 2);
|
||||||
```
|
```
|
||||||
|
|
||||||
:::{.callout-important}
|
:::{.callout-important}
|
||||||
|
|
||||||
**Explicit type annotations are almost always irrelevant for the speed of the code!**
|
**Explicit type annotations are almost always irrelevant for the speed of the code!**
|
||||||
|
|
||||||
This is one of the most important *selling points* of Julia.
|
This is one of the most important *advantages* of Julia.
|
||||||
|
|
||||||
Once a function is called for the first time with certain types, a specialized form of the function is generated and compiled for these argument types. Thus, generic functions are usually just as fast as the specialized functions one writes in other languages.
|
Once a function is called for the first time with certain types, a specialized form of the function is generated and compiled for these argument types. Thus, generic functions are usually just as fast as the specialized functions one writes in other languages.
|
||||||
|
|
||||||
Generic functions enable the cooperation of the most diverse packages and a high level of abstraction.
|
Generic functions enable seamless integration across packages and support high-level abstraction.
|
||||||
|
|
||||||
A simple example: The `Measurements.jl` package defines a new data type `Measurement`, a value with error, and the arithmetic of this type. Thus, generic functions work automatically:
|
A simple example: The `Measurements.jl` package defines a new data type `Measurement`, a value with error, and the arithmetic of this type. Thus, generic functions work automatically:
|
||||||
|
|
||||||
@@ -637,7 +632,7 @@ A simple example: The `Measurements.jl` package defines a new data type `Measure
|
|||||||
|
|
||||||
=#
|
=#
|
||||||
using Measurements
|
using Measurements
|
||||||
zz= @which Base.show(stdout, MIME"text/latex"(), 3±2)
|
zz = @which Base.show(stdout, MIME"text/latex"(), 3±2)
|
||||||
Base.delete_method(zz)
|
Base.delete_method(zz)
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -647,13 +642,13 @@ using Measurements
|
|||||||
x = 33.56±0.3
|
x = 33.56±0.3
|
||||||
y = 2.3±0.02
|
y = 2.3±0.02
|
||||||
|
|
||||||
fsinnfrei1(x, y)
|
fmm(x, y)
|
||||||
```
|
```
|
||||||
:::
|
:::
|
||||||
|
|
||||||
## Type Parameters in Function Definitions: the `where` Clause
|
## Type Parameters in Function Definitions: the `where` Clause
|
||||||
|
|
||||||
We want to write a function that works for **all complex integers** (and only these), e.g., a [possible prime factorization in ℤ[i](https://en.wikipedia.org/wiki/Gaussian_integer). The definition
|
We want to write a function that works for **all complex integers** (and only these), e.g., an implementation of [prime factorization in ℤ[i]](https://en.wikipedia.org/wiki/Gaussian_integer). The definition
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
#| eval: false
|
#| eval: false
|
||||||
@@ -673,7 +668,7 @@ end
|
|||||||
|
|
||||||
This is to be read as:
|
This is to be read as:
|
||||||
|
|
||||||
> "The argument x should be one of the types `Complex{T}`, where the type variable `T` can be any subtype of `Integer`."
|
> "The argument `x` should be one of the types `Complex{T}`, where the type variable `T` can be any subtype of `Integer`."
|
||||||
|
|
||||||
Another example:
|
Another example:
|
||||||
```{julia}
|
```{julia}
|
||||||
@@ -683,7 +678,7 @@ function kgV(x::Complex{T}, y::Complex{S}) where {T<:Integer, S<:Integer}
|
|||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
> The arguments x and y can have different types and both must be subtypes of `Integer`.
|
> The arguments x and y can have different types, each a subtype of `Integer`.
|
||||||
|
|
||||||
|
|
||||||
If there is only one `where` clause as in the last example, one can omit the curly braces and
|
If there is only one `where` clause as in the last example, one can omit the curly braces and
|
||||||
@@ -713,16 +708,16 @@ C3 = Complex{<:Integer}
|
|||||||
C1 == C2 == C3
|
C1 == C2 == C3
|
||||||
```
|
```
|
||||||
|
|
||||||
Short syntax for simple cases, extended syntax for complex variants.
|
Short syntax for simple cases; extended syntax for complex variants.
|
||||||
|
|
||||||
Last, it should be noted that `where T` is the short form of `where T<:Any`, which introduces a completely unrestricted type variable. Thus, something like this is possible:
|
Finally, note that `where T` is shorthand for `where T<:Any`, which introduces a completely unrestricted type variable. Thus, something like this is possible:
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
function fgl(x::T, y::T) where T
|
function fgl(x::T, y::T) where T
|
||||||
println("Congratulations! x and y are of the same type!")
|
println("Congratulations! x and y are of the same type!")
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
This method requires that the arguments are exactly the same type, but otherwise arbitrary.
|
This method requires that both arguments have exactly the same type; otherwise, any type is accepted.
|
||||||
|
|
||||||
```{julia}
|
```{julia}
|
||||||
fgl(33, 44)
|
fgl(33, 44)
|
||||||
|
|||||||
38
index.qmd
38
index.qmd
@@ -9,10 +9,10 @@ julia:
|
|||||||
|
|
||||||
```{=typst}
|
```{=typst}
|
||||||
#set math.equation(numbering: none)
|
#set math.equation(numbering: none)
|
||||||
// some things need to be here, to counter the typst style
|
// Some things need to be here to counter the Typst style
|
||||||
```
|
```
|
||||||
|
|
||||||
Julia is a relatively young, modern programming language designed for *scientific computing*.
|
Julia is a relatively new, modern programming language designed for *scientific computing*.
|
||||||
|
|
||||||
A code example:
|
A code example:
|
||||||
|
|
||||||
@@ -43,20 +43,20 @@ f
|
|||||||
|
|
||||||
## History {.unnumbered}
|
## History {.unnumbered}
|
||||||
|
|
||||||
- 2009 Start of development at the *Computer Science and Artificial
|
- 2009: Development started at MIT's *Computer Science and Artificial
|
||||||
Intelligence Laboratory* of MIT
|
Intelligence Laboratory*
|
||||||
- 2012 first release v0.1
|
- 2012: First release (v0.1)
|
||||||
- 2018 Version v1.0
|
- 2018: Version 1.0 released
|
||||||
- February 2026: Version v1.12.5
|
- February 2026: Version 1.12.5
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
In their 2012 inaugural blog post [Why we created Julia](https://julialang.org/blog/2012/02/why-we-created-julia),
|
In their 2012 inaugural blog post [Why we created Julia](https://julialang.org/blog/2012/02/why-we-created-julia),
|
||||||
the developers provide an insightful and funny overview of their objectives and motivations for creating Julia.
|
the developers provide an insightful and humorous overview of their objectives and motivations for creating Julia.
|
||||||
|
|
||||||
For a picture of *Stefan Karpinski, Viral Shah, Jeff
|
A photo of *Stefan Karpinski, Viral Shah, Jeff
|
||||||
Bezanson*, and *Alan Edelman*, please
|
Bezanson*, and *Alan Edelman* can be found
|
||||||
click here: <https://news.mit.edu/2018/julia-language-co-creators-win-james-wilkinson-prize-numerical-software-1226>.
|
here: <https://news.mit.edu/2018/julia-language-co-creators-win-james-wilkinson-prize-numerical-software-1226>.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -113,16 +113,16 @@ Julia has a built-in package manager."
|
|||||||
|
|
||||||
***high-performance programming language for technical computing***
|
***high-performance programming language for technical computing***
|
||||||
|
|
||||||
- many functions for *scientific computing* built-in,
|
- many functions for *scientific computing* built-in
|
||||||
- (intentional) similarity to Python, R and Matlab,
|
- (intentional) similarity to Python, R and Matlab
|
||||||
- complex calculations in a few lines
|
- complex calculations in a few lines
|
||||||
- simple interface to other languages like C or Python
|
- simple interface to other languages like C or Python
|
||||||
|
|
||||||
***a JIT compiler***
|
***JIT compilation***
|
||||||
|
|
||||||
- interactive work possible: `read-eval-print loop (REPL)` with
|
- supports interactive workflow via the `read-eval-print loop (REPL)`
|
||||||
- just-in-time (JIT) compilation
|
- just-in-time (JIT) compilation
|
||||||
- thereby runtimes comparable to static languages like C/C++, Fortran or Rust
|
- resulting in runtimes comparable to static languages like C/C++, Fortran, or Rust
|
||||||
|
|
||||||
***a built-in package manager***
|
***a built-in package manager***
|
||||||
|
|
||||||
@@ -141,10 +141,10 @@ Julia has a built-in package manager."
|
|||||||
## Selected Links {.unnumbered}
|
## Selected Links {.unnumbered}
|
||||||
|
|
||||||
- [Documentation](https://docs.julialang.org/en/v1/) -- the official documentation
|
- [Documentation](https://docs.julialang.org/en/v1/) -- the official documentation
|
||||||
- [Cheat Sheet](https://cheatsheet.juliadocs.org/) -- "a quick & dirty overview"
|
- [Cheat Sheet](https://cheatsheet.juliadocs.org/) -- "a quick overview"
|
||||||
- [Introducing Julia](https://en.wikibooks.org/wiki/Introducing_Julia) -- a WikiBook
|
- [Introducing Julia](https://en.wikibooks.org/wiki/Introducing_Julia) -- a WikiBook
|
||||||
- [The Julia Express](http://bogumilkaminski.pl/files/julia_express.pdf) -- Julia in 16 pages
|
- [The Julia Express](http://bogumilkaminski.pl/files/julia_express.pdf) -- Julia in 16 pages
|
||||||
- [Think Julia](https://benlauwens.github.io/ThinkJulia.jl/latest/book.html) -- introduction to programming using Julia as first language
|
- [Think Julia](https://benlauwens.github.io/ThinkJulia.jl/latest/book.html) -- introduction to programming using Julia as first language
|
||||||
- The [Julia Forum](https://discourse.julialang.org/)
|
- The [Julia Forum](https://discourse.julialang.org/)
|
||||||
- For the eyes: [Examples for the Julia graphics package `Makie`](https://beautiful.makie.org/)
|
- For the eyes: [Examples for the Julia graphics package `Makie`](https://beautiful.makie.org/)
|
||||||
|
|
||||||
|
|||||||
@@ -5,11 +5,11 @@
|
|||||||
"npm": "@ai-sdk/openai-compatible",
|
"npm": "@ai-sdk/openai-compatible",
|
||||||
"name": "llama-server (local)",
|
"name": "llama-server (local)",
|
||||||
"options": {
|
"options": {
|
||||||
"baseURL": "http://127.0.0.1:8077/v1"
|
"baseURL": "http://127.0.0.1:12434/v1"
|
||||||
},
|
},
|
||||||
"models": {
|
"models": {
|
||||||
"Qwen3-coder-next-100": {
|
"Qwen3.5-122B-A10B-coding": {
|
||||||
"name": "Qwen3-coder-next-100",
|
"name": "Qwen3.5-122B-A10B-coding",
|
||||||
"tool_call": true,
|
"tool_call": true,
|
||||||
"reasoning": true,
|
"reasoning": true,
|
||||||
"limit": {
|
"limit": {
|
||||||
|
|||||||
Reference in New Issue
Block a user