english improved
This commit is contained in:
@@ -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}
|
||||
function hyp(x,y)
|
||||
sqrt(x^2+y^2)
|
||||
end
|
||||
```
|
||||
II. As a "single-liner"
|
||||
II. Single-line form
|
||||
```{julia}
|
||||
hyp(x, y) = sqrt(x^2 + y^2)
|
||||
```
|
||||
III. As anonymous functions
|
||||
III. Anonymous functions
|
||||
|
||||
```{julia}
|
||||
(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.
|
||||
|
||||
The two definitions
|
||||
@@ -57,7 +56,7 @@ function xsinrecipx(x)
|
||||
return x * sin(1/x)
|
||||
end
|
||||
```
|
||||
and without the second explicit `return` in the last line:
|
||||
and the equivalent version without explicit `return` in the last line:
|
||||
|
||||
```julia
|
||||
function xsinrecipx(x)
|
||||
@@ -71,7 +70,7 @@ end
|
||||
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`.
|
||||
|
||||
|
||||
@@ -97,13 +96,13 @@ a
|
||||
|
||||
### 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
|
||||
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
|
||||
- Parenthesized statements separated by semicolons.
|
||||
@@ -126,13 +125,13 @@ hyp(x, y) = begin
|
||||
|
||||
### 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
|
||||
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.
|
||||
|
||||
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}
|
||||
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
|
||||
|
||||
|
||||
- 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_.
|
||||
- Functions can therefore effectively modify their arguments if they are _mutable_ objects, such as `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.
|
||||
- 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_.
|
||||
- Consequently, functions can modify their arguments if they are mutable (e.g., `Vector` or `Array`).
|
||||
- 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}
|
||||
V = [1, 2, 3]
|
||||
|
||||
W = fill!(V, 17)
|
||||
# '===' is test for identity
|
||||
# '===' tests for identity
|
||||
@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.
|
||||
- Both positional and _keyword_ arguments can have _default_ values. These arguments can be omitted when calling.
|
||||
- The order of declaration must be:
|
||||
1. Positional arguments without default value,
|
||||
2. Positional arguments with default value,
|
||||
1. Positional arguments without default values,
|
||||
2. Positional arguments with default values,
|
||||
3. --- semicolon ---,
|
||||
4. comma-separated list of keyword arguments (with or without default value)
|
||||
- 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.
|
||||
4. comma-separated list of keyword arguments (with or without default values)
|
||||
- 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}
|
||||
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}
|
||||
@@ -215,11 +214,11 @@ f2(2)
|
||||
```
|
||||
|
||||
|
||||
- They can be passed as arguments to functions.
|
||||
- Functions can be passed as arguments to other functions.
|
||||
|
||||
|
||||
```{julia}
|
||||
# very naive numerical integration
|
||||
# naive Riemann integration example
|
||||
|
||||
function Riemann_integrate(f, a, b; NInter=1000)
|
||||
delta = (b-a)/NInter
|
||||
@@ -263,7 +262,7 @@ h(1)
|
||||
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}
|
||||
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}
|
||||
["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.
|
||||
|
||||
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
|
||||
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}
|
||||
r = Riemann_integrate(0, π) do x
|
||||
z1 = sin(x)
|
||||
@@ -351,10 +350,10 @@ r = Riemann_integrate(0, π) do x
|
||||
|
||||
## 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}
|
||||
# struct stores the coefficients of a 2nd degree polynomial
|
||||
# struct stores coefficients of a second-degree polynomial
|
||||
struct Poly2Grad
|
||||
a0::Float64
|
||||
a1::Float64
|
||||
@@ -365,13 +364,13 @@ p1 = Poly2Grad(2,5,1)
|
||||
p2 = Poly2Grad(3,1,-0.4)
|
||||
```
|
||||
|
||||
The following method makes this structure `callable`.
|
||||
The following method makes this structure callable:
|
||||
```{julia}
|
||||
function (p::Poly2Grad)(x)
|
||||
p.a2 * x^2 + p.a1 * x + p.a0
|
||||
end
|
||||
```
|
||||
Now the objects can, if desired, also be used like functions.
|
||||
Objects can now be used like functions:
|
||||
|
||||
```{julia}
|
||||
@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
|
||||
|
||||
- Infix operators like `+,*,>,∈,...` are functions.
|
||||
- Infix operators such as `+`, `*`, `>`, `∈` are functions.
|
||||
|
||||
|
||||
|
||||
@@ -399,7 +398,7 @@ f = +
|
||||
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}
|
||||
|
||||
@@ -417,7 +416,7 @@ f(3, 7)
|
||||
(The colon before a variable makes it into a symbol.)
|
||||
|
||||
:::{.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.
|
||||
:::
|
||||
@@ -433,15 +432,15 @@ can also be written as
|
||||
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
|
||||
for i in eachindex(x)
|
||||
x[i] += y[i]
|
||||
end
|
||||
```
|
||||
or through the semantically equivalent _broadcast_ form (see @sec-broadcast):
|
||||
or semantically equivalent broadcast form (see @sec-broadcast):
|
||||
```julia
|
||||
x .= x .+ y
|
||||
```
|
||||
@@ -450,13 +449,13 @@ x .= x .+ y
|
||||
|
||||
## Operator Precedence and Associativity {#sec-vorrang}
|
||||
|
||||
Expressions to be computed
|
||||
Expressions like
|
||||
|
||||
```{julia}
|
||||
-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}
|
||||
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
|
||||
- associativity.
|
||||
- 'Precedence' defines which operators bind more strongly in the sense of "multiplication and division before addition and subtraction".
|
||||
- 'Associativity' determines the evaluation order for operators of equal or same rank.
|
||||
- Precedence determines which operators bind more tightly, such as multiplication before addition.
|
||||
- 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)
|
||||
|
||||
### 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}
|
||||
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;
|
||||
```
|
||||
|
||||
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}
|
||||
@@ -507,7 +506,7 @@ for i in (:/, :+=, :(=), :^)
|
||||
end
|
||||
```
|
||||
|
||||
So the power operator is right-associative.
|
||||
Thus, the power operator is right-associative:
|
||||
```{julia}
|
||||
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'
|
||||
- The power operator `^` has a higher _precedence_.
|
||||
- Assignments have the smallest _precedence_
|
||||
- Precedence 11 < 12 explains why multiplication/division bind tighter than addition/subtraction.
|
||||
- The power operator `^` has higher precedence.
|
||||
- Assignments have the lowest precedence.
|
||||
|
||||
|
||||
```{julia}
|
||||
@@ -540,11 +539,11 @@ x
|
||||
|
||||
|
||||
```{julia}
|
||||
(y = 3) < 4 # parentheses当然 override any precedence
|
||||
(y = 3) < 4 # parentheses override any precedence
|
||||
y
|
||||
```
|
||||
|
||||
Once more to the example from the beginning of @sec-vorrang:
|
||||
Returning to the example above:
|
||||
|
||||
```{julia}
|
||||
-2^3+500/2/10==8 && 13 > 7 + 1 || 9 < 2
|
||||
@@ -557,12 +556,12 @@ for i ∈ (:^, :+, :/, :(==), :&&, :>, :|| )
|
||||
println(Base.operator_precedence(i))
|
||||
end
|
||||
```
|
||||
According to these precedence rules, the example expression is evaluated as follows:
|
||||
These rules evaluate the expression as:
|
||||
|
||||
```{julia}
|
||||
((-(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:
|
||||
|
||||
|
||||
Reference in New Issue
Block a user