{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Control flow"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Boolean types"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
" Booleans are special data types that can have two values, `true` or `false`. \n",
" They are essential for conducting control flow.\n",
"\n",
"\n",
"\n",
"Boolean operators (copied from [here](https://docs.julialang.org/en/v1/manual/mathematical-operations/#Boolean-Operators))\n",
"\n",
"\n",
"|Expression|\tName|\n",
"|--|--|\n",
"|`!x`|\tnegation|\n",
"|`x && y`|\tshort-circuiting `and`|\n",
"|`x \\|\\| y`|\tshort-circuiting `or`|\n",
"\n",
"Numeric comparisons (copied partially from [here](https://docs.julialang.org/en/v1/manual/mathematical-operations/#Numeric-Comparisons))\n",
"|Operator|\tName|\n",
"|--|--|\n",
"|`==`\t|equality|\n",
"|`!=`, `≠`\t|inequality|\n",
"|`<`\t|less than|\n",
"|`<=`, `≤`|\tless than or equal to|\n",
"|`>`|\tgreater than|\n",
"|`>=`, `≥`|\tgreater than or equal to|\n",
"|`≈`, `isapprox` | approximate equality with _relative_ tolerance error `atol`|\n",
"|`===` | programmatically identical equality comparison |"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Most operators work exactly the same as in other languages."
]
},
{
"cell_type": "code",
"execution_count": 69,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"true"
]
},
"execution_count": 69,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"2 == 2"
]
},
{
"cell_type": "code",
"execution_count": 70,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"false"
]
},
"execution_count": 70,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"4>5"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"When I was learning coding in `Julia`, two operators were new to me: `≈` and `===`.\n",
"\n",
"Operator `≈` compares two different objects given the tolerance error `atol`:"
]
},
{
"cell_type": "code",
"execution_count": 83,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"true"
]
},
"execution_count": 83,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"≈(2000, 2000.1, atol=.5)"
]
},
{
"cell_type": "code",
"execution_count": 84,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"false"
]
},
"execution_count": 84,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"≈(2000, 2000.1; atol=.0001)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Operator `===` checks whether two objects are programmatically identical. In case of arrays or dictionaries, it means that we check whether two objects refer to the same memory slots. Illustrative example below:"
]
},
{
"cell_type": "code",
"execution_count": 85,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"x==y: true\n",
"x==z: true\n",
"x===y: true\n",
"x===z: false\n"
]
}
],
"source": [
"x = [1,2]\n",
"y = x\n",
"z = deepcopy(x)\n",
"\n",
"println(\"x==y: $(x==y)\") #both objects have the same values\n",
"println(\"x==z: $(x==z)\") #both objects have the same values\n",
"println(\"x===y: $(x===y)\") #both objects refer to the same memory\n",
"println(\"x===z: $(x===z)\") #both objects refer to different memory slots (even though they may have identical values)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The mentioned operators can be broadcasted. As a result we get arrays:"
]
},
{
"cell_type": "code",
"execution_count": 91,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"3-element BitVector:\n",
" 0\n",
" 1\n",
" 0"
]
},
"execution_count": 91,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"(1:1:3) .== (3:-1:1) #comparing [1 2 3] with [3 2 1]. It's true only for 2nd elements"
]
},
{
"cell_type": "code",
"execution_count": 92,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"2-element BitVector:\n",
" 0\n",
" 1"
]
},
"execution_count": 92,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
".![true, false] #notice the order of `.` and `!`"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Conditional evaluations"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Implementation of `if/elseif/else` is very standard and similar to other languages:\n",
"\n",
"```\n",
" if expr1 \n",
" sth1\n",
" elseif expr2\n",
" sth2\n",
" else \n",
" sth3\n",
" end\n",
"```\n",
"\n",
"\n",
"* `if` - do `sth1` if `expr1` is true\n",
"* `elseif` - do `sth2` if `expr2` is true and `expr1` is false \n",
"* `else` - do `sth3` if both `expr1` and `expr2` are false\n",
"\n",
"Example below:"
]
},
{
"cell_type": "code",
"execution_count": 95,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"nothing\n"
]
}
],
"source": [
"if 2!=2\n",
" println(\"asd\")\n",
"elseif 3>4\n",
" println(\"3>4\")\n",
"elseif 5==10\n",
" println(\"5==10\")\n",
"else\n",
" println(\"nothing\")\n",
"end"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## `?:` ternary operator"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"There is a ternary operator `?:` for short conditional evaluations. \n",
"Its syntax is `cond ? sth1 : sth2`. The result of this operation conducted on scalar is: do `sth1` if `cond` is true and `sth2` otherwise.\n"
]
},
{
"cell_type": "code",
"execution_count": 106,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"\"x is even\""
]
},
"execution_count": 106,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"x = 4\n",
"even_x = x%2==0 ? \"x is even\" : \"x is odd\""
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"(crra)=\n",
"```{prf:example}\n",
"Suppose we want to create the CRRA utlity function in `Julia`. It is defined as follows: \n",
"\n",
"$$ u(c_t, \\eta) = \\begin{cases}\\frac{c_t^{1-\\eta}-1}{1-\\eta}, \\text{if $\\eta \\neq 1$}\\\\ \n",
"\\ln (c_t), \\text{if $\\eta=1.$} \\end{cases}$$\n",
"\n",
"```\n",
"\n",
"We can use `?` to define this function:"
]
},
{
"cell_type": "code",
"execution_count": 32,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"u (generic function with 1 method)"
]
},
"execution_count": 32,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"u(cₜ, η) = η==1 ? log(cₜ) : (cₜ^(1. -η)-1.)/(1. -η)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"```{margin}\n",
"I force `Julia` to operate on floating numbers by using `1.`. \n",
"Try to use function defined as `u(cₜ, η) = η==1 ? log(cₜ) : (cₜ^(1 -η)-1)/(1 -η)`\n",
"and see what happens if you evaluate it at $c_t=3$ and $\\eta=2,$ both integers.\n",
"\n",
"```"
]
},
{
"cell_type": "code",
"execution_count": 33,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"0.6666666666666667"
]
},
"execution_count": 33,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"u(3,2)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"`?:` can be nested, just like in an example below:"
]
},
{
"cell_type": "code",
"execution_count": 40,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"x is smaller than 5 and is even\n"
]
}
],
"source": [
"x = 4\n",
"\n",
"x<5 ? ( \n",
" x%2==0 ? println(\"x is smaller than 5 and is even\") : #x<5 is true and x%2==0 is true\n",
" println(\"x is smaller than 5 and is odd\") #x<5 is true and x%2==0 is false\n",
" ) :\n",
" println(\"x is greater or equal to 5\") #x<5 is false and x%2==0 is not evaluated"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We know that the CRRA function is well defined if $\\eta\\geq 0.$ We can check this condition using nested `?`:"
]
},
{
"cell_type": "code",
"execution_count": 38,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"u (generic function with 1 method)"
]
},
"execution_count": 38,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"u(cₜ, η) = η≥0 ? \n",
" (η==1 ? log(cₜ) : (cₜ^(1. -η)-1. )/(1. -η)) :\n",
" @error \"η=$η is not allowed!\""
]
},
{
"cell_type": "code",
"execution_count": 39,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"0.6666666666666667"
]
},
"execution_count": 39,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"u(3, 2)"
]
},
{
"cell_type": "code",
"execution_count": 40,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"┌ Error: η=-1 is not allowed!\n",
"└ @ Main In[38]:3\n"
]
}
],
"source": [
"u(3, -1)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"(elementwise-question)=\n",
"### Elementwise `?:`\n",
"\n",
"(elementwise-question-example)=\n",
"```{note}\n",
"Suppose that we want to perform a following mapping for $x\\in 1,2, \\ldots, 10:$\n",
"\n",
"$$ f(x) = \\begin{cases}x^2+1, \\text{if $x$ is odd,}\\\\\n",
" -x^2, \\text{if $x$ is even.}\\\\ \\end{cases}$$\n",
"```\n",
"\n",
"Such a task can be done relatively easily in other languages.\n",
"For instance in `R` we get it by using `ifelse`: \n",
"\n",
"```\n",
" result <- ifelse(x%%2==0, -x^2, x^2 + 1) #this is `R` code. It will not work in `Julia`\n",
"```\n",
"\n",
"We might be tempted to make elementwise comparisons using `.?`."
]
},
{
"cell_type": "code",
"execution_count": 132,
"metadata": {},
"outputs": [
{
"ename": "LoadError",
"evalue": "syntax: space before \".\" not allowed in \"((x .% 2) .== 0) .\" at In[132]:2",
"output_type": "error",
"traceback": [
"syntax: space before \".\" not allowed in \"((x .% 2) .== 0) .\" at In[132]:2",
"",
"Stacktrace:",
" [1] top-level scope",
" @ In[132]:2",
" [2] eval",
" @ ./boot.jl:360 [inlined]",
" [3] include_string(mapexpr::typeof(REPL.softscope), mod::Module, code::String, filename::String)",
" @ Base ./loading.jl:1094"
]
}
],
"source": [
"x = collect( 1:10 );\n",
"result = ((x) .%2 .==0) .? -(x).^2 .: (x).^2.+1 "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"As we can see, it will not work the way we would like. \n",
"A solution to this problem is to use `map` function with an anonymous function:"
]
},
{
"cell_type": "code",
"execution_count": 115,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"10-element Vector{Int64}:\n",
" 2\n",
" -4\n",
" 10\n",
" -16\n",
" 26\n",
" -36\n",
" 50\n",
" -64\n",
" 82\n",
" -100"
]
},
"execution_count": 115,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"map(arg -> arg%2==0 ? -arg^2 : arg^2+1 , x)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"This works as intended."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"(short-circuit)=\n",
"## Short-circuit evaluations"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"`Julia` exhibits lazy evaluation of formulas. \n",
"This means that operations are computed only if they are needed.\n",
"This property has some particular consequences for two operators, `&&` and `||`.\n",
"Laziness of `Julia` together with necessary condition for `true` value in `&&` and sufficient condition for `false` in `||` can be used for conditional evaluations.\n",
"More precisely, the second (right-hand side) argument of `&&` is evaluated if and only if the first (left-hand side) argument is true.\n",
"Similarly, the second (right-hand side) argument of `||` is _not_ valuated if if the first (left-hand side) argument is _false._\n",
"\n",
"As the second argument we can put some evaluations that we would like to conduct subject to the left-hand side argument.\n",
"Simple illustrations below:"
]
},
{
"cell_type": "code",
"execution_count": 126,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"(2+2==4) It's true\n"
]
}
],
"source": [
"(2+2==4) && println(\"(2+2==4) It's true\");\n"
]
},
{
"cell_type": "code",
"execution_count": 127,
"metadata": {},
"outputs": [],
"source": [
"(2+2==5) && println(\"(2+2==5) It's true\"); # the latter is not executed as (2+2==5) is false"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"(2+2==4) || println(\"(2+2==4) It's false\"); # the latter is not executed as (2+2==4) is true"
]
},
{
"cell_type": "code",
"execution_count": 129,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"It's false\n"
]
}
],
"source": [
"(2+2==5) || println(\"(2+2==5) It's false\");"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Julia 1.6.0",
"language": "julia",
"name": "julia-1.6"
},
"language_info": {
"file_extension": ".jl",
"mimetype": "application/julia",
"name": "julia",
"version": "1.6.0"
},
"orig_nbformat": 4
},
"nbformat": 4,
"nbformat_minor": 2
}