docs about loops and functions
This commit is contained in:
+34
-3
@@ -29,7 +29,21 @@ Integers and floats correspond to C's `long long int` and `double` types, so the
|
|||||||
As in most languages, the boolean type of value is one that can only assume the `true` or `false` value. These values are such that the logical negation of one (`not`) gives the other.
|
As in most languages, the boolean type of value is one that can only assume the `true` or `false` value. These values are such that the logical negation of one (`not`) gives the other.
|
||||||
|
|
||||||
### Strings
|
### Strings
|
||||||
Strings are immutable sequences of text encoded as UTF-8.
|
Strings are immutable sequences of text encoded as UTF-8. To define a string, enclose the text in single or double quotes
|
||||||
|
```py
|
||||||
|
"This is a string";
|
||||||
|
'This is another string';
|
||||||
|
```
|
||||||
|
|
||||||
|
It's possible to define strings with special characters using the `\x` notation. The available special characters are
|
||||||
|
* `\t` tab
|
||||||
|
* `\r` carriage return
|
||||||
|
* `\n` newlines
|
||||||
|
|
||||||
|
If you want to use `\`, `"` or `'` as characters, you must escape them using a `\`
|
||||||
|
```py
|
||||||
|
print("Hello, I'm \"Francesco\"!"); # This prints: Hello, I'm "Francesco"!
|
||||||
|
```
|
||||||
|
|
||||||
### The none value
|
### The none value
|
||||||
The none value is a value that's used to represent the void of a value. The token for the none value is `none`, while the none type is `None` (with a capital letter).
|
The none value is a value that's used to represent the void of a value. The token for the none value is `none`, while the none type is `None` (with a capital letter).
|
||||||
@@ -68,6 +82,7 @@ The logical operations in Noja are:
|
|||||||
* `and`
|
* `and`
|
||||||
* `or`
|
* `or`
|
||||||
* `not`
|
* `not`
|
||||||
|
|
||||||
They can only be applied to boolean values and always return a boolean value.
|
They can only be applied to boolean values and always return a boolean value.
|
||||||
|
|
||||||
Note that this isn't always true for other languages. For example python allows operands of any kind. The return value in python also isn't always a boolean. It returns the left operand if it's considered to be equivalent to `True`, else the right operand is returned.
|
Note that this isn't always true for other languages. For example python allows operands of any kind. The return value in python also isn't always a boolean. It returns the left operand if it's considered to be equivalent to `True`, else the right operand is returned.
|
||||||
@@ -118,9 +133,9 @@ The `n`-th list can be accessed using the `[]` notation:
|
|||||||
ls = [true, false, none];
|
ls = [true, false, none];
|
||||||
ls[1]; # false
|
ls[1]; # false
|
||||||
```
|
```
|
||||||
where the list value is followed by `[]` which contain the index of the item to retrieve.
|
where the list value is followed by `[..]` which contain the index of the item to retrieve.
|
||||||
|
|
||||||
The index may be evaluated dynamically, but it will trigger a runtime error if it doesn't evaluate to an integer value, aborting the execution of the whole program
|
The index may be evaluated dynamically, but it will trigger a runtime error if it doesn't evaluate to an integer value
|
||||||
```py
|
```py
|
||||||
ls = [true, false, none];
|
ls = [true, false, none];
|
||||||
ls[0 + 2]; # OK!
|
ls[0 + 2]; # OK!
|
||||||
@@ -179,3 +194,19 @@ The `[ .. ]` is actually a `List` expression. You can provide more than one valu
|
|||||||
val = coll[key0, key1];
|
val = coll[key0, key1];
|
||||||
```
|
```
|
||||||
in which case, the key will be a `List` containing the list of provided keys. Since `List`s only allow `int` keys, this only can be used on `Map`s.
|
in which case, the key will be a `List` containing the list of provided keys. Since `List`s only allow `int` keys, this only can be used on `Map`s.
|
||||||
|
|
||||||
|
## Function calls
|
||||||
|
We haven't seen how function definitions work yet, but you can imagine they work like other languages such as Python or JavaScript for now. Assuming we defined a function named `sayHello`, we can call it using the usual `()` notation:
|
||||||
|
```py
|
||||||
|
sayHello();
|
||||||
|
sayHello(1);
|
||||||
|
sayHello(1, 2, 3);
|
||||||
|
```
|
||||||
|
|
||||||
|
## The print function
|
||||||
|
One function that's always available is `print`, which takes a variable number of arguments and prints them to the standard output. It doesn't add spaces between the argument prints or newlines at the end of the print, so a call such as
|
||||||
|
```py
|
||||||
|
print("A", "B");
|
||||||
|
print("C", "D");
|
||||||
|
```
|
||||||
|
will output `ABCD`.
|
||||||
@@ -9,6 +9,7 @@ if condition: {
|
|||||||
# Executed when the condition is false
|
# Executed when the condition is false
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
Unlike expressions statements, they don't end with a `;`.
|
||||||
|
|
||||||
The condition may be any type of expression, but must evaluate to a boolean type. No implicit casts are performed.
|
The condition may be any type of expression, but must evaluate to a boolean type. No implicit casts are performed.
|
||||||
|
|
||||||
@@ -56,3 +57,18 @@ creating a chain of if-else statements.
|
|||||||
Actually the meaning of the curly brackets is to group multiple statements into one.
|
Actually the meaning of the curly brackets is to group multiple statements into one.
|
||||||
|
|
||||||
The if-else statement expects only one statement for each branch, though it's possible to provide more than one statement each by wrapping them into curly brackets.
|
The if-else statement expects only one statement for each branch, though it's possible to provide more than one statement each by wrapping them into curly brackets.
|
||||||
|
|
||||||
|
## Scopes
|
||||||
|
If-else and compound statements don't create new scopes, which means that variables defined inside one of those statements will be accessible outside of them:
|
||||||
|
```py
|
||||||
|
if 1 < 2:
|
||||||
|
a = 10;
|
||||||
|
# Here "a" is still defined.
|
||||||
|
print(a); # Prints 10
|
||||||
|
```
|
||||||
|
```py
|
||||||
|
{
|
||||||
|
a = 1;
|
||||||
|
}
|
||||||
|
print(a); # Prints 1
|
||||||
|
```
|
||||||
@@ -1 +1,61 @@
|
|||||||
# Loops
|
# Loops
|
||||||
|
You can tell the Noja interpreter to execute a block of code iteratively based on the result of an expression using while and do-while statements.
|
||||||
|
|
||||||
|
The condition may be an expression of any kind, but must always evaluate to a boolean value.
|
||||||
|
|
||||||
|
## While loops
|
||||||
|
A while statement has the following form:
|
||||||
|
```py
|
||||||
|
while condition: {
|
||||||
|
doSomething();
|
||||||
|
doSomethingElse();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
as you can see, it doesn't need an ending `;`.
|
||||||
|
|
||||||
|
when the interpreter encounters a while statement, it evaluates the condition. If the condition is true, it executes it's body. When the execution of the inner block of code is completed, the execution jumps back to the condition, evaluating it again. If it's again true, the inner code is executed again, else the execution jumps after the while statement. This mechanism can go on potentially for ever!
|
||||||
|
|
||||||
|
Like for if-else statements, when the inner block only has one statement, you can drop the `{}`
|
||||||
|
```py
|
||||||
|
while condition:
|
||||||
|
doSomething();
|
||||||
|
doSomethingElse(); # This isn't in the while loop's body!
|
||||||
|
```
|
||||||
|
|
||||||
|
## Do-while loops
|
||||||
|
Do-while loops behave very similarly to while loops, but have minor differences. Do-while loops evaluate the condition *after* each iteration.
|
||||||
|
|
||||||
|
They're used like this
|
||||||
|
```py
|
||||||
|
do {
|
||||||
|
doSomething();
|
||||||
|
doSomethingElse();
|
||||||
|
} while condition;
|
||||||
|
```
|
||||||
|
or, if the body only has one statement, like this
|
||||||
|
```py
|
||||||
|
do
|
||||||
|
doSomething();
|
||||||
|
while condition;
|
||||||
|
```
|
||||||
|
|
||||||
|
Unlike while loops, do-while loops execute at least once and must end with a `;`.
|
||||||
|
|
||||||
|
## Scoping
|
||||||
|
Loops don't create new variable scopes. When defining a variable in a loop statement (either in the condition or the body) they're defined relative to the loop's parent scope.
|
||||||
|
|
||||||
|
## Break jumps
|
||||||
|
Inside any type of loop, it's always possible to break out of it using the `break` statement.
|
||||||
|
|
||||||
|
```js
|
||||||
|
while condition: {
|
||||||
|
|
||||||
|
if shouldStopLoopin():
|
||||||
|
break;
|
||||||
|
|
||||||
|
doSomething()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
the `break` statement will immediately move the execution to after the while or do-while statement.
|
||||||
|
|
||||||
|
When inside multiple nested loops, the `break` statement will refer to the inner loop.
|
||||||
@@ -0,0 +1,165 @@
|
|||||||
|
# Functions
|
||||||
|
Function definition statements always start with the `fun` keyword, followed by it's name, argument list and then body.
|
||||||
|
|
||||||
|
Here are some examples of function definitions:
|
||||||
|
```py
|
||||||
|
# No arguments
|
||||||
|
fun sayHelloToEveryone() {
|
||||||
|
print('Hello, everyone!\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
# Some arguments
|
||||||
|
fun sayHelloTo(name) {
|
||||||
|
print('Hello, ', name, '!\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
fun sayHelloToBoth(name1, name2) {
|
||||||
|
print('Hello, ', name1, '!\n');
|
||||||
|
print('Hello, ', name2, '!\n');
|
||||||
|
}
|
||||||
|
```
|
||||||
|
When function bodies only have one statement, you can drop the brackets
|
||||||
|
```py
|
||||||
|
fun sayHelloToEveryone()
|
||||||
|
print('Hello, everyone!\n');
|
||||||
|
|
||||||
|
fun sayHelloTo(name)
|
||||||
|
print('Hello, ', name, '!\n');
|
||||||
|
|
||||||
|
fun sayHelloToBoth(name1, name2) {
|
||||||
|
print('Hello, ', name1, '!\n');
|
||||||
|
print('Hello, ', name2, '!\n');
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Like if-else and while statements, they don't need an ending `;`.
|
||||||
|
|
||||||
|
The evaluation of a function definition statement results in the definition of a variable who's name is the name of the defined function and it's value is the function itself. You may overwrite the variable or use it in an expression, although the only valid operation on it is a function call.
|
||||||
|
|
||||||
|
```py
|
||||||
|
fun abc() {}
|
||||||
|
|
||||||
|
# Now "abc" is a function
|
||||||
|
|
||||||
|
abc = 10;
|
||||||
|
|
||||||
|
# Now it's an integer!
|
||||||
|
```
|
||||||
|
|
||||||
|
## Functions calls and return values
|
||||||
|
To execute a function in an expression, it's name must be followed by an argument list enclosed in brackets `(..)`. All functions return at least one value, which may be `none` when a function has no useful value to be returned.
|
||||||
|
|
||||||
|
To specify the return value of a function, one must use the `return` statement
|
||||||
|
```py
|
||||||
|
fun myName() {
|
||||||
|
# ..do stuff..
|
||||||
|
return "Francesco";
|
||||||
|
}
|
||||||
|
|
||||||
|
fun add(a, b)
|
||||||
|
return a + b;
|
||||||
|
```
|
||||||
|
|
||||||
|
when calling a function, the resulting value of the call will be the one evaluated by the last return statement.
|
||||||
|
|
||||||
|
```py
|
||||||
|
fun add(a, b)
|
||||||
|
return a + b;
|
||||||
|
|
||||||
|
six = add(2, 3) + 1;
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that a return statement must always end with a `;`.
|
||||||
|
|
||||||
|
When a function doesn't return a value explicitly, then the `none` value is returned:
|
||||||
|
```py
|
||||||
|
fun iDontReturnExplicitly() {
|
||||||
|
1 + 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
print(iDontReturnExplicitly(), '\n'); # Prints: none
|
||||||
|
```
|
||||||
|
|
||||||
|
## Multiple returns
|
||||||
|
Functions may return more than one value:
|
||||||
|
```py
|
||||||
|
fun swap(a, b)
|
||||||
|
return b, a;
|
||||||
|
```
|
||||||
|
|
||||||
|
When a calling a function with more than one return value in an expression, only the first return value is considered.
|
||||||
|
|
||||||
|
```py
|
||||||
|
fun returnTwoThings()
|
||||||
|
return 5, "I'm ignored";
|
||||||
|
|
||||||
|
print(1 + returnTwoThings(), '\n'); # Prints 6.
|
||||||
|
```
|
||||||
|
|
||||||
|
The way to use the additional return values is through an assignment to multiple variables
|
||||||
|
|
||||||
|
```py
|
||||||
|
first, second = returnTwoThings();
|
||||||
|
print(first, '\n'); # Prints 5
|
||||||
|
print(second, '\n'); # Prints "I'm ignored"
|
||||||
|
```
|
||||||
|
|
||||||
|
An assignment to multiple variables is still an expression and it evaluates to the leftmost value
|
||||||
|
```py
|
||||||
|
fun combine(a, b)
|
||||||
|
return (a+b), (a-b);
|
||||||
|
|
||||||
|
print((x,y = combine(3, 4))); # Prints 7.
|
||||||
|
```
|
||||||
|
|
||||||
|
## Scoping
|
||||||
|
Functions are the only thing that creates a new scope. When variables are defined inside a function, they're relative to the function and the function only. When the function returns, the variable defined inside it are no longer available.
|
||||||
|
|
||||||
|
## Closures
|
||||||
|
When defining a function, the scope of the parent context is captured and it's always accessible by the function's body. When functions are defined in the global scope, this means that they can always access global variables
|
||||||
|
|
||||||
|
```py
|
||||||
|
name = "Francesco";
|
||||||
|
|
||||||
|
fun printName()
|
||||||
|
print(name);
|
||||||
|
|
||||||
|
printName(); # Prints "Francesco"
|
||||||
|
|
||||||
|
name = "Giovanni";
|
||||||
|
|
||||||
|
printName(); # Prints "Giovanni"
|
||||||
|
```
|
||||||
|
|
||||||
|
But this mechanism is cooler than that. Consider the case were a function is defined inside another one
|
||||||
|
```py
|
||||||
|
name1 = "Giovanni";
|
||||||
|
fun outerFunc() {
|
||||||
|
name2 = "Francesco";
|
||||||
|
fun innerFunc() {
|
||||||
|
print(name1, " and ", name2, " are friends!\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
outerFunc(); # Prints: Giovanni and Francesco are friends!
|
||||||
|
```
|
||||||
|
now the inner function can access both the variables of the outer function and the global scope.
|
||||||
|
|
||||||
|
This also applies when the function is returned
|
||||||
|
```py
|
||||||
|
name1 = "Giovanni";
|
||||||
|
fun outerFunc() {
|
||||||
|
name2 = "Francesco";
|
||||||
|
fun innerFunc() {
|
||||||
|
print(name1, " and ", name2, " are friends!\n");
|
||||||
|
}
|
||||||
|
return innerFunc;
|
||||||
|
}
|
||||||
|
|
||||||
|
innerFunc = outerFunc();
|
||||||
|
innerFunc(); # Prints the same thing
|
||||||
|
```
|
||||||
|
|
||||||
|
But functions may never modify the captures scopes. Variables can only be defined relative to the local scope.
|
||||||
|
|
||||||
|
## Type hints
|
||||||
|
..
|
||||||
Reference in New Issue
Block a user