{ "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 }