From fa7752c5a8bb913df4a0cefec18d405f1f4dc7ef Mon Sep 17 00:00:00 2001 From: SebastianTi <47980118+SebastianTi@users.noreply.github.com> Date: Sun, 31 Mar 2019 21:43:54 +0200 Subject: [PATCH] Final version Conditionals and Iterations_ Julia --- 04_Iteration_and_Conditionals_Julia.ipynb | 1163 +++++++++++++++++++++ 1 file changed, 1163 insertions(+) create mode 100644 04_Iteration_and_Conditionals_Julia.ipynb diff --git a/04_Iteration_and_Conditionals_Julia.ipynb b/04_Iteration_and_Conditionals_Julia.ipynb new file mode 100644 index 0000000..c6d4024 --- /dev/null +++ b/04_Iteration_and_Conditionals_Julia.ipynb @@ -0,0 +1,1163 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Tutorial 04 - Conditionals and Iterations " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Table of contents\n", + "***\n", + "\n", + "\n", + "\n", + "* [**Conditionals**](#conditionals)\n", + " * [if - statements](#if)\n", + " * [ifelse - statements](#ifelse)\n", + " * [else - statements](#else)\n", + " * [Exception Handling](#exceptionstrycatch)\n", + " * [Ternary Operators](#ternaryoperators)\n", + " * [Boolean Operators and Short - Curcuit - Evaluation](#logicaloperators)\n", + " \n", + "* [**Iterations**](#iterations)\n", + " * [for - loops](#for)\n", + " * [while - loops](#while)\n", + " * [break and continue - statements](#breakandcontinue)\n", + " * [Nested Loops](#nestedloops)\n", + " * [Scope of Loop Variables](#scope)\n", + "* [**Solutions**](#solutions)\n", + "* [**References**](#references)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# \n", + "# Conditionals\n", + "***\n", + "\n", + "\n", + "As humans, our everyday life is characterized by numerous conscious and unconscious decisions. If it is sunny in the morning, we decide to wear sunglasses. If it is rainy, we bring an umbrella. If it is cold, we wear a jacket. While those decisions seem to be common sense to us, it is anything but common sense for a computer or a program. \n", + "\n", + "Lacking even the smallest amount of situational judgement, the machine has to be given precise definitions of scenarios and consequences in order to act accordingly. More technically speaking, we define **conditionals** that can either be ``TRUE`` or ``FALSE`` (i.e. they are of type ``bool``) and depending on the outcome, we **evaluate different blocks of code**. As those **conditionals** are designed to **alter the flow of the code** (depending on the scenario) literature often captures respective chapters with **\"Control Flow\"**.\n", + "\n", + "In the course of this tutorial, we will be concerned with different ways to implement conditionals in Julia. Although the code examples follow the **syntax of Julia**, the systematic of implementation is very much the same for most programming languages and can be easily applied in **Python, R, Java, etc.**" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## if - statements\n", + "***\n", + "\n", + "\n", + "As the overwhelming majority of complex concepts is easier to grasp following an example, we are going to introduce the boss **Boris** and his young apprentice **Alex**. **Boris** is boss of his own nursery and cultivates magnificent sunflowers in different shapes and sizes. The price of the sunflowers is determined by the **diameter of the blossom** of the flower and is summarized by the follwing table:\n", + "\n", + "diameter of the blossom|price of the sunflower (in Dollars)|voucher (in Dollars)\n", + "---|---|---- \n", + "above 40cm|5|1\n", + "below or equal to 40cm|3|0\n", + "\n", + "\n", + "\n", + "\n", + "While the smaller sunflowers sell for **3 Dollars**, the bigger sunflowers sell for **5 Dollars** and the customer receives a voucher of **1 Dollar** for his next purchase (**Boris** once found out that the vouchers increase customer loyality). The apprentice **Alex** is in charge of selling the sunflowers whenever **Boris** takes one day off. While Alex is an excellent gardener and knows everything about sunflowers, he really struggles with mathematics and has a terrible memory, so he always forgets how much he has to charge for the sunflowers.\n", + "For this purpose, **Boris** wants to implement a special function that only needs the **diameter of the sunflower as an input** and gives **the price of the sunflower and the value of the voucher as output**. This way, **Alex** only has to measure the sunflowers and enter the diameter into the program in order to obtain the desired information.\n", + "\n", + "The two flowcharts below show the systematics of the function for a big sunflower and a small sunflower" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Big sunflower (diameter = 60cm)|Small sunflower (diameter = 30cm)\n", + "---|---\n", + "\"bigsunflower\"|\"smallsunflower\"\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "Considering the flowcharts above, it is easy to see that for **different scenarios** (or inputs), the function needs to give **different outputs**. That corresponds to the understanding of conditionals we acquired above and should make it obvious, that the implementation of this task requires some form of **conditional statement ( = orange diamond shape)** and **a codeblock (= blue square)** for every possible scenario.\n", + "\n", + "Let us approach this by first transferring the flowcharts into **pseudocode**:\n", + "\n", + "\n", + "\n", + "````\n", + "if diameter is greater than 40 cm # checks the conditional (= orange diamond shape)\n", + " then price is 5 and custumer receives voucher of 1 # defines codeblock when conditional is TRUE (red scenario)\n", + " \n", + "else price is 3 and costumer receives voucher of 0 # defines codeblock when conditional is FALSE (green scenario) \n", + "\n", + "\n", + "````\n", + "\n", + "In case of Julia with it´s basic syntax, the step from **pseudocode** to real **executable code** is very straightforward:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "sunflower_price (generic function with 1 method)" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "function sunflower_price(diameter) # define the name of the function and the input parameter\n", + " \n", + " \n", + "if diameter > 40 # conditional (orange diamond shape)\n", + " price = 5 # block of code (= blue square) in case condition is TRUE (= red scenario)\n", + " voucher = 1\n", + " \n", + " else # block of code (= blue square) in case condition is FALSE (= green scenario)\n", + " price = 3 \n", + " voucher = 0\n", + " \n", + " end # end of the if-statement\n", + " \n", + "println(\"This sunflower costs $price Dollars and the customer receives a voucher of $voucher Dollars\")\n", + "end # end of the function" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Try below whether the function ``sunflower_price`` yields the desired outputs for the big sunflower (60cm diameter) and the small sunflower (30cm diameter):" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "This sunflower costs 5 Dollars and the customer receives a voucher of 1 Dollars\n" + ] + } + ], + "source": [ + "sunflower_price(60)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## ifelse - statements\n", + "***\n", + "\n", + "\n", + "\n", + "Motivated by the easy implementation of the function ``sunflower_price`` , **Boris** decides to change the prices of his sunflowers and introduces new size categories. Again, he wants to implement a function ``sunflower_price_complex`` that gives **Alex** the price to charge for a given diameter following this systematic:\n", + "\n", + "diameter of the blossom|price of the sunflower (in Dollars)|voucher (in Dollars)\n", + "---|---|--- \n", + "above 60cm|7|1,50\n", + "above 40cm|5|1\n", + "above 25cm|3|0,50\n", + "below or equal to 25cm|1|0\n", + "\n", + "\n", + "In this context, it is quite clear that we have **several conditionals** to be evaluated and therefore **several blocks of code** as well. As this is not feasable with a single ``if-statement``, we will have to utilise a sequence of ``if-statements`` that is exemplified in the flowcharts below:" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "\n", + "diameter = 70cm|diameter = 50cm\n", + "----|----\n", + "![diameter 70cm](imgs/04_loop_diameter70.gif)|![diameter 50cm](imgs/04_loop_diameter50.gif)\n", + "**diameter = 30cm**|**diameter = 20cm**\n", + "![diameter 30cm](imgs/04_loop_diameter302.gif)|![diameter 20cm](imgs/04_loop_diameter20.gif)\n", + "\n", + "\n", + "-----------------" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Luckily, most programming languages provide the possibility to add further **conditionals** with ``elseif`` so that we can **sequentially** evaluate a number of **conditionals**:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "sunflower_price_complex (generic function with 1 method)" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "function sunflower_price_complex(diameter) # define the name of the function and the name of the input parameter\n", + " \n", + " \n", + "if diameter > 60 # 1. conditional (1. orange diamond shape)\n", + " price = 7 # block of code that should be run if the condition is TRUE (= blue scenario)\n", + " voucher = 1.50\n", + " \n", + " elseif diameter > 40 # 2. conditional (2. orange diamond shape)\n", + " price = 5 # block of code that should be run if the condition is TRUE (= yellow scenario)\n", + " voucher = 1\n", + " \n", + " elseif diameter > 25 # 3. conditional (3. orange diamond shape)\n", + " price = 3 # block of code that should be run if the condition is TRUE (= violet scenario) \n", + " voucher = 0.50\n", + " \n", + " else\n", + " price = 1 # block of code that should else be run (= black scenario)\n", + " voucher = 0\n", + " \n", + " end # end of the if-statement (and all the elseifs)\n", + " \n", + "println(\"This sunflower costs $price Dollars and the costumer receives a voucher of $voucher Dollars\")\n", + " end # end of the function" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Test the functionality of the function ``sunflower_price_complex`` for the different scenarios:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "This sunflower costs 5 Dollars and the costumer receives a voucher of 1 Dollars\n" + ] + } + ], + "source": [ + "sunflower_price_complex(50)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note that the function **evaluates the conditionals chronologically**, starting with the first one and then going downwards. The second **conditional** is only evaluated if the first one is ``FALSE``. The third **conditional** is only evaluated if the first and the second are ``FALSE`` etc. This way, in each scenario **only one block of code is executed** (namely the block of code after the **first conditional** that turns out to be ``TRUE``).\n", + "While this is a crucial property of the ``elseif-statements`` it also holds us responsible for **choosing the right order of the conditionals**. Imagine for yourself what would have happened if we accidently **switched the 1. and the 3. conditional** (including their blocks of code) in the function ``sunflower_price_complex`` and **Alex** tried to evaluate the price of a big sunflower with a diameter of 65cm.
[Check your solution](#solutionelseif)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "## else - statements\n", + "------\n", + "\n", + "In the function ``sunflower_price`` as well as in the function ``sunflower_price_complex`` we used ``else - statements`` at the end of the function to capture all the cases where all **conditionals** are found to be ``FALSE``. While the ``else - statement`` is never mandatory, it was useful to make sure that ``price`` and ``voucher`` are both assigned values in **all cases**.
\n", + "Nevertheless, it also makes the functions ``sunflower_price`` and ``sunflower_price_complex`` prone for undesired behaviour in case of unexpected inputs. Just imagine what would happen if **Alex** accidently typed in a negative diameter (e.g. - 65 instead of 65)
[Check your solution](#elsesolution)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "## Exception Handling\n", + "----\n", + "We can adress this issue with the ``else - statements`` by consciously defining what to do in case of a negative input, so that **Alex** does not simply charge the wrong price, but is instead **noticed of his mistake**. By doing so, we prevent the cases with a negative input from ending in the ``else``-codeblock. We achieve this, by defining **exceptions** (i.e. special cases that should produce an error) in our code with the ``throw() - function``. Let us define a new function ``sunflower_price_complex_robust`` that has **robust behaviour in case of a negative input** and compare it to the behaviour of ``sunflower_price_complex``:" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "sunflower_price_complex_robust (generic function with 1 method)" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "function sunflower_price_complex_robust(diameter)\n", + "if diameter <= 0 # condition for throwing an error\n", + "throw(DomainError(diameter,\"the diameter must be greater than zero\" )) # throw an error with an error message\n", + "else\n", + " sunflower_price_complex(diameter) # block of code if there is no error (usual case)\n", + " end # end of the if - statement\n", + " end # end of the function" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Test the different behaviours of ``sunflower_price_complex`` and ``sunflower_price_complex_robust`` for different positive and negative inputs:" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "This sunflower costs 1 Dollars and the costumer receives a voucher of 0 Dollars\n" + ] + } + ], + "source": [ + "sunflower_price_complex(-5)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "ename": "DomainError", + "evalue": "DomainError with -5:\nthe diameter must be greater than zero", + "output_type": "error", + "traceback": [ + "DomainError with -5:\nthe diameter must be greater than zero", + "", + "Stacktrace:", + " [1] sunflower_price_complex_robust(::Int64) at .\\In[11]:3", + " [2] top-level scope at In[12]:1" + ] + } + ], + "source": [ + "sunflower_price_complex_robust(-5)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "By implementing an **addtional conditional**, we managed to throw an error in case of ``diameter <= 0``. Julia supports a wide range of built-in, but also custom **exceptions**. While this is out of scope for this tutorial, you can find an overview over the **exceptions** in Julia [here](https://docs.julialang.org/en/v1/manual/control-flow/#Built-in-Exceptions-1)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "## Ternary Operator\n", + "----\n", + "\n", + "There is an alternative way to evaluate **conditionals** without using ``if-elseif-else``. The so-called **Ternary Operator** ``? :`` is a very neat way of evaluating **conditionals**, especially when the blocks of code under the different scenarios are just single expression values rather than a longer sequence of commands. The name of the **Ternary Operator** is based on the fact that is takes **three operands** (one **conditional**, an expression that is evaluated if the **conditional** is ``TRUE`` and an expresson that is evaluated when the **conditional** is ``FALSE``)
\n", + "Let´s try to replicate the function ``sunflower_price`` using the **Ternary Operator** ``? :``:" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "sunflower_price_ternary (generic function with 1 method)" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "function sunflower_price_ternary(diameter) # function sunflower_price using a ternary operator\n", + " diameter>40 ? (price=5;voucher=1) : (price=3;voucher=0) # conditional statement with ternary operator ? :\n", + " \n", + " println(\"The price of the sunflower is $price Dollars and the costumer receives a voucher of $voucher Dollars\")\n", + " \n", + "end" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The price of the sunflower is 3 Dollars and the costumer receives a voucher of 0 Dollars\n" + ] + } + ], + "source": [ + "sunflower_price_ternary(30)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The systematic of the **Ternary Operator** ( in comparison to the ``if - elseif - else`` structure is clarified in the illustration below:\n", + "![Systematic of a Ternary Operator](imgs/04_comparison_if_ternary.PNG)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Of course, we can also replicate more complex functions like ``sunflower_price_complex`` with the **Ternary Operator** although in this case, the
``if-elseif-else`` systematic appears to be more intuitive:" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "sunflower_price_complex_ternary (generic function with 1 method)" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "function sunflower_price_complex_ternary(diameter)\n", + "diameter>60 ? (price=7;voucher=1.50) : diameter>40 ? (price=5;voucher=1) : diameter>25 ? (price=3;voucher=0.5) : (price=1;voucher=0)\n", + "\n", + " println(\"The price of the sunflower is $price Dollars and the costumer receives a voucher of $voucher Dollars\")\n", + "end" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The price of the sunflower is 5 Dollars and the costumer receives a voucher of 1 Dollars\n" + ] + } + ], + "source": [ + "sunflower_price_complex_ternary(60)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "![Sunflower_Price_Complex_Ternary_Systematic](imgs/04_comparison_if_ternary_complex.PNG)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Boolean Operators and Short - Curcuit - Evaluation\n", + "***\n", + "\n", + "In the course of the example above, we understood the consecutive evaluation of different **conditionals**. In some cases, it might be necessary to **jointly test** different **conditionals** and only execute a block of code if **both** conditionals turn out to be true. We can easily implement the joint evaluation of **conditionals** by using **Boolean Operators** such as ``&&`` or ``||``.
\n", + "**Boris** makes use of **Boolean Operators** during his special Valentine´s day promotion. On Valentine´s Day, every sunflower with ``diameter > 70cm`` that is purchased by a female, only costs $ 1. The price of all other sunflowers follows the systematics of ``price_sunflower_complex``. He implements this using the AND - Operator ``&&``:" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "valentine_promotion (generic function with 1 method)" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "function valentine_promotion(diameter,gender) # function requires the diameter and the gender as input\n", + " if diameter > 70 && gender == \"female\" # the two conditions on the diameter and the gender (both have to be TRUE)\n", + " price = 1 # price and voucher if both conditionals are TRUE\n", + " voucher = 0\n", + " println(\"This sunflower costs $price Dollars and the customer receives a voucher of $voucher Dollars\")\n", + " else\n", + " sunflower_price_complex(diameter) # block of code if one of the conditionals is FALSE\n", + " end\n", + "end" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Test the functionality of ``valentine_promotion`` for different sizes of sunflowers and genders:" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "This sunflower costs 1 Dollars and the customer receives a voucher of 0 Dollars\n" + ] + } + ], + "source": [ + "valentine_promotion(75,\"female\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note that we only give the discount if **both** ``diameter > 70cm`` **and** ``gender == \"female\"`` are ``TRUE``. As long as only one of both is ``TRUE``, we end up in the ``else`` - block.
\n", + "In contrast to that, the **OR - Operator** ``||`` only requires **one** of the two conditions to be ``TRUE`` for the whole expresson to be ``TRUE``. Boris makes use of the **OR - Operator** ``||`` in the framework of the function ``july_promotion`` for his promotion on the 1st of July, where every sunflower > 70cm **or** purchased by a female costs only $1:" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "july_promotion (generic function with 1 method)" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "function july_promotion(diameter,gender) # function requires the dieameter and the gender as input\n", + " if diameter > 70 || gender == \"female\" # the two conditions on the diameter and the gender (only one has to be TRUE)\n", + " price = 1 # price and voucher if both conditionals are TRUE\n", + " voucher = 0\n", + " println(\"This sunflower costs $price Dollars and the customer receives a voucher of $voucher Dollars\")\n", + " else\n", + " sunflower_price_complex(diameter) # block of code if one of the conditionals is FALSE\n", + " end # end of the if- statement\n", + " end # end of the function" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Test the functionality of ``july_promotion`` for different sizes of sunflowers and genders. Especially compare the outputs to those of ``valentine_promotion``" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "This sunflower costs 1 Dollars and the customer receives a voucher of 0 Dollars\n" + ] + } + ], + "source": [ + "july_promotion(45,\"female\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Whenever two conditionals are connected using a **Boolean Operator** like ``&&`` or ``||``, only the **minimum number of conditionals** is evaluated that is needed to determine whether the combination of conditionals is overall ``TRUE`` or ``FALSE``. \n", + "* In ``valentine_promotion``, the second conditional is not even evaluated if the first one turns out to be ``FALSE`` (as the combination of conditionals can never become ``TRUE`` with the first one already being ``FALSE``)\n", + "* In ``july_promotion``, the second conditional is not even evaluated if the first condition turns out to be ``TRUE`` (as one conditional being ``TRUE`` is enough for the combination of the conditionals to become ``TRUE``)\n", + "\n", + "Being aware of this systematic, we have an alternative way of implementing conditionals in Julia (the so-called **Short - Curcuit - Evaluation**):" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "surface_area_2 (generic function with 1 method)" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "function surface_area_1(diameter) # example function for a Short - Curcuit - Evaluation\n", + " diameter <= 0 && error(\"The diameter must be non- negative\")# tests whether the diameter <= 0 and gives error if TRUE\n", + " round(pi*(diameter/2)^2) # else it computes the surface area \n", + "end\n", + "\n", + "\n", + "function surface_area_2(diameter) # Example function for a Short - Curcuit - Evaluation\n", + " diameter > 0 || error(\"The diameter must be non- negative\") # tests whether diameter > 0 and gives an error if FALSE \n", + " round(pi*(diameter/2)^2) # if TRUE, it calculates the surface area\n", + "end" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Test the functionalities of ``surface_area_1`` and ``surface_area_2`` for positive and negative inputs:" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": {}, + "outputs": [ + { + "ename": "ErrorException", + "evalue": "The diameter must be non- negative", + "output_type": "error", + "traceback": [ + "The diameter must be non- negative", + "", + "Stacktrace:", + " [1] error(::String) at .\\error.jl:33", + " [2] surface_area_1(::Int64) at .\\In[24]:2", + " [3] top-level scope at In[26]:1" + ] + } + ], + "source": [ + "surface_area_1(-44)" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": {}, + "outputs": [ + { + "ename": "ErrorException", + "evalue": "The diameter must be non- negative", + "output_type": "error", + "traceback": [ + "The diameter must be non- negative", + "", + "Stacktrace:", + " [1] error(::String) at .\\error.jl:33", + " [2] surface_area_2(::Int64) at .\\In[24]:8", + " [3] top-level scope at In[28]:1" + ] + } + ], + "source": [ + "surface_area_2(-44)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In the framework of this examples, we consciously leave out the second conditional knowing that it will automatically come down to the following expressions:\n", + "* ``condition`` && ``statement`` comes down to ``condition`` **and then** ``statement`` (if the condition is ``TRUE``)\n", + "* ``condition`` || ``statement`` comes down to ``condition`` **or else** ``statement`` (if the condition is ``FALSE``)\n", + "\n", + "***\n", + "***\n", + "***" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Iterations\n", + "***\n", + "\n", + "\n", + "\n", + "Julia offers various ways for implementation of repeated evaluation of certain blocks of code. Most common in this respect are the ``for - loop`` and the ``while - loop`` which will be subject of this chapter. After retracing the general systematics of this two kinds of loops, we will be concerned with additional (partly more advanced) functionalities of loops. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## For - Loops\n", + "***\n", + "\n", + "\n", + "\n", + "\n", + "The ``for - loop`` is commonly used to apply certain operations to **every element** of an iterable container (such as an array for example). In the example below, **Boris** created a vector ``daily_diameters`` that contains the diameters of all the flowers sold on a certain day. Motivated by his interest in mathematics, **Boris** wants to calculate the surface areas of all the flowers he sold that day. In order to do that, he has to apply this operation to every element in the array ``daily_diameters``:\n", + "![formulaforthearea](imgs/04_formula_area.PNG)\n", + "He decides to implement this using a ``for - loop`` that iterates through ``daily_diameters`` and calculates the surface area of the respective sunflower:\n" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "3959.0\n", + "908.0\n", + "2463.0\n", + "415.0\n", + "2827.0\n", + "1452.0\n" + ] + } + ], + "source": [ + "daily_diameters=[71,34,56,23,60,43] # define a vector that contains the diameters of all sold sunflowers\n", + "\n", + "for i in daily_diameters # define a loop variable and an iterable container\n", + " println(round(pi*(i/2)^2)) # operation that should be applied to every element of the iterable container\n", + "end" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\"forloop\"\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In this context, the **loop - variable** ``i`` is only defined within the loop and takes one of the values in ``daily_diameters`` in every single iteration of the loop therefore allowing us to reference this value in the body of the loop (red iteration: i = 71 ; green iteration: i = 34 etc.). We can choose the name of this **loop - variable** arbitrarily but have to be aware of the fact that it is **not defined outside the loop** (more about this in the chapter on the **scope** of loop - variables).\n", + "Apart from that, we can replace the membership operator ``in`` with ``=`` or ``∈``." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## While - Loops\n", + "***\n", + "\n", + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The second major type of loop is the ``while - loop``. With **repeated evaluation** of a block of code in the body of the loop, it **resembles the essential functionality** of the ``for - loop``. The **main difference** is that **before** every iteration, the ``while - loop`` **checks a condition** and does only conduct the iteration if the condition is ``TRUE``. This stands in contrast to the **unconditional iteration** through **all** objects of an iterable container, we saw in the framework of the ``for - loop``.
\n", + "An example for the application of a ``for - loop`` would be the \"Super Saturdays\" in Boris´ nursery. Every first Saturday of the month, Boris tries to attract costumers by promising different discounts for the first few costumers. He wants to start with 0 percent discount for the first costumer and wants to increase the discount by 5 percent for every costumer until someone receives 20 percent discount (as this is nearly his complete proft margin, he does not want to give any discount above that and needs the iteration to stop as soon as the discount is above 20 percent).
\n", + "Being aware of the conditional component of his iterations, **Boris** chooses to implement this with a ``while - loop``:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "discount = 0 # initial discount is zero\n", + "\n", + "while discount <= 20 # condition that has to be TRUE so that the iteration is executed\n", + "\n", + " println(\"This costumer receives $discount percent discount !\") # block of code that is executed if the condition is TRUE\n", + " \n", + " discount = discount + 5 # increase the discount by 5 for the next costumer (resp. iteration)\n", + "\n", + "end" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "![whileloop](imgs/04_whileloop2.gif)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The ``while - loop`` has it´s name because it repeats the body of the loop **while** a condition is ``TRUE``. In this context, it is important that something in the body of the loop **changes**, so that the condition becomes ``FALSE`` at some point. In the example above, we gradually increase the discount and as soon as it is above 20 (yellow iteration), the loop comes to an end. In contrast to that, the ``for - loop`` does automatically end as soon as it iterated through all the objects in the iterable container. If the condition is ``FALSE`` in the first place, the body of the ``while - loop`` is never executed.
\n", + "Note that we could generally also implement this conditional iteration using a ``for - loop`` (for the sake of brevity, this will not be discussed in the framework of this tutorial." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Break and Continue - Statements\n", + "***\n", + "\n", + "\n", + "\n", + "In some cases, it might be necessary to terminate the loop before the end of the iterable container is reached (for the ``for - loop``) or before the condition becomes ``FALSE`` (in the case of the ``while - loop``). In these cases, we can use ``break - statements`` to prematurely terminate the iteration if a certain condition is ``TRUE``
\n", + "In the case of **Boris** and his nursery, he makes use of ``break - statements`` when he is dealing with his costumer list ``daily_costumers``. When one day, Kathrin wanted to know whether her husband Jim had already been here today, **Boris** employed a ``for - loop`` with a ``break`` that prints all the costumers **before** Kathrin:" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Claudia\n", + "Susanne\n" + ] + } + ], + "source": [ + "daily_costumers=[\"Claudia\", \"Susanne\", \"Kathrin\", \"Elisabeth\", \"Mark\", \"Jim\"] # array that contains all costumers of a day\n", + "\n", + "for i in daily_costumers # define the loop - variable and reference the iterable container\n", + " if i == \"Kathrin\" # condition that should result in a break (if TRUE)\n", + " break # jump out of the loop (if the condition above is TRUE)\n", + " end # end of the condition\n", + " println(i) # actual body of the loop (in the form of the else - statement of the condition)\n", + " end # end of the loop " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + " As soon as the defined condition becomes ``TRUE``, the ``break - statement`` stops the iteration and **\"jumps out of the loop\"**. As in the example above, ``break - statements`` are often used in ``for - loops`` to search for certain values but they are equally applicable to ``while - loops``.
\n", + "Apart from terminating the whole loop with a ``break - statement``, we also have the opportunity to only **skip single iterations** of the loop by using the ``continue - statement``. As soon as the condition is ``TRUE``, we skip the body of the loop and jump straight to the next iteration (instead of completely jumping out of the loop as with the ``break - statement``).\n", + "**Boris** makes use of the ``continue - statement`` whenever his jealous wife insists on seeing a list with all the costumers of the day and Boris needs to make sure that his ex-girlfriend Elisabeth is not on the list:" + ] + }, + { + "cell_type": "code", + "execution_count": 271, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Claudia\n", + "Susanne\n", + "Kathrin\n", + "Mark\n", + "Jim\n" + ] + } + ], + "source": [ + "daily_customers=[\"Claudia\", \"Susanne\", \"Kathrin\", \"Elisabeth\", \"Mark\", \"Jim\"]\n", + "\n", + "for i in daily_costumers\n", + " if i == \"Elisabeth\" # define a condition to skip the iteration (if condition is TRUE)\n", + " continue # skip the iteration that resulted in the condition being TRUE\n", + " end\n", + " println(i)\n", + "end" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Although ``break - statements`` as well as ``while - statements`` are useful tools to adjust loops, they are usually not mandatory (except for special kinds of loops that are out of scope for this tutorial)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Nested Loops\n", + "***\n", + "\n", + "\n", + "\n", + "Apart from the simple loops we looked at before, we also have the possibility to create **nested loops** (which is essentially a loop within a loop). For every iteration of the **outer loop**, we conduct all the iterations of the **inner loop**.\n", + "Remember the example from above, where we worked with ``daily_diameters`` to calculate the surface areas. If we have two vectors ``daily_diameters_monday`` and ``daily_diameters_tuesday`` and want to calculate the **cartesian product** of those two vectors, we accomplish this with a **nested loop** by separating the different loops with a ``,``:" + ] + }, + { + "cell_type": "code", + "execution_count": 272, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(34, 45)\n", + "(34, 64)\n", + "(34, 23)\n", + "(34, 54)\n", + "(56, 45)\n", + "(56, 64)\n", + "(56, 23)\n", + "(56, 54)\n", + "(78, 45)\n", + "(78, 64)\n", + "(78, 23)\n", + "(78, 54)\n" + ] + } + ], + "source": [ + "daily_diameters_monday = [34,56,78] # create vector for the sunflowers sold on Monday\n", + "\n", + "daily_diameters_tuesday = [45,64,23,54] # create vector for the sunflowers sold on Tuesday\n", + "\n", + "for j in daily_diameters_monday,i in daily_diameters_tuesday # create a nested loop to create the artesian product \n", + " z = (j,i) # save the current values of i and j in a tuple z \n", + " \n", + " println(z)\n", + "end" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The principle of this nested loop is **\"from the outside (loop) to the inside (loop)\"** and is exemplified in the animation below. Note, that we can use
``break - statements`` in the context of a nested loop, but that it will end both loops, not just the inner one.\n", + "\"nestedloop\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Scope of a Loop - Variable\n", + "***\n", + "\n", + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "It is important to understand that all the loops we considered so far, do introduce a **new scope** (i.e. a new \"universe\" with defined variables). The variables we define in the loops (i.e. in the **local scope** of the loop) are only known inside this **local scope** but not outside of the loop. See below that if we try to access the variable ``loopcounter`` after the loop, we get an ``UndefVarError``:" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1\n", + "4\n", + "9\n", + "16\n", + "25\n" + ] + }, + { + "ename": "UndefVarError", + "evalue": "UndefVarError: loopcounter not defined", + "output_type": "error", + "traceback": [ + "UndefVarError: loopcounter not defined", + "", + "Stacktrace:", + " [1] top-level scope at In[31]:4" + ] + } + ], + "source": [ + "for loopcounter in 1:5 # define loopcounter as iteration variable\n", + " println(loopcounter*loopcounter) # calculate the square for each iteration\n", + " end # end the loop\n", + "\n", + "print(loopcounter) # try to access the loopcounter out of the local scope of the loop \n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Apart from that, if the variable ``testvariable`` exists already before the loop (i.e. in the **global scope**), we can modify ``testvariable`` inside the loop and the modifications will persist in the **global scope** (i.e. outside the loop):" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1\n", + "4\n", + "9\n", + "16\n", + "25\n", + "testvariable after the loop: 8\n" + ] + } + ], + "source": [ + "\n", + "testvariable = 3 # define testvariable outside the loop (global scope)\n", + "\n", + "for secondloopcounter in 1:5 # set secondloopcounter as loop - variable\n", + " println(secondloopcounter * secondloopcounter) # print the square of loopcounter in every iteration\n", + " testvariable = testvariable + 1\n", + "end\n", + "\n", + "println(\"testvariable after the loop: $testvariable\")\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "While there is much more to the **scope of a variable**, this simple examples should serve as a reminder to consider the scope of the variables when referencing them inside or outside a loop.\n", + "\n", + "***" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Looking back at what we learned in the framework of this tutorial, we are now able to implement **conditionals** in the form of ``if - ifelse - statements`` and by using **Ternary Operators**. Apart from that, we know how to implement the joint evaluation of conditionals using ``&&`` and ``||`` and we are aware of the systematics of **Short - Curcuit - Evaluation**.
\n", + "When it comes to **loops**, we know how to implement repeated evaluation of code with ``while - loops`` and ``for - loops`` and we understand the usage of ``break - statements`` and ``continue - statements``. Furhtermore, we can implement **nested loops** and understand the basic implications of the **scope** of a loop variable." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Solutions\n", + "*****\n", + "\n", + "\n", + "\n", + "\n", + "### Solution for switching the 1. and the 3. conditional (ifelse - section)\n", + "\n", + "If we accidently switched the 1. and the 3. conditional, we would start by evaluating whether the diameter of the sunflower is above 25cm. For a diameter of 65 cm this would of course be ``TRUE`` and we would therefore execute the block of code after that conditional without even considering all the other conditionals after that. So the function sets ``price = 3`` and ``voucher = 0.50`` as in the violet scenario and we sell the sunflower too cheap.
\n", + "[Go back to the text](#else)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "\n", + "### Solution for accidental negative input (else - section)\n", + "\n", + "If the function ``sunflower_price_complex`` gets a negative input, all evaluated **conditionals** turn out to be ``FALSE`` and we end up in the codeblock under the ``else - statement``. Therefore, we set ``price = 1`` and ``voucher = 0`` as in the **black scenario**.
\n", + "[Go back to the text](#else)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "![Sunflowerbanner2](imgs/04_sunflower_banner.PNG)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# References\n", + "***\n", + "\n", + "\n", + "\n", + "* S. Nagar (2017), Beginning Julia Programming (Chapter 11), https://doi.org/10.1007/978-1-4842-3171-5_11\n", + "* I. Balbaert, Julia 1. 0 programming dynamic and high-performance programming to build fast scientific applications (Chapter 4)\n", + "* Julia Manual https://docs.julialang.org/en/v1.1/manual/control-flow/" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Julia 1.1.0", + "language": "julia", + "name": "julia-1.1" + }, + "language_info": { + "file_extension": ".jl", + "mimetype": "application/julia", + "name": "julia", + "version": "1.1.0" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +}