julia_coursera/Week4_Functions.ipynb

2253 lines
50 KiB
Plaintext

{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Functions"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## In this lesson"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"- [Introduction](#Introduction)\n",
"- [Creating a simple single expression function](#Creating-a-simple-single-expression-function)\n",
"- [Multiple expression functions](#Multiple-expression-functions)\n",
"- [Flow control in a function](#Flow-control-in-a-function)\n",
"- [Using optional arguments](#Using-optional-arguments)\n",
"- [Using keyword arguments to bypass the order problem](#Using-keyword-arguments-to-bypass-the-order-problem)\n",
"- [Functions with a variable number of arguments](#Functions-with-a-variable-number-of-arguments)\n",
"- [Passing arrays as function arguments](#Passing-arrays-as-function-arguments)\n",
"- [Type parameters](#Type-parameters)\n",
"- [Stabby functions and do blocks](#Stabby-functions-and-do-blocks)\n",
"- [Using functions as arguments](#Using-functions-as-arguments)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Introduction"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Julia is a functional language. Given specific information (called arguments), a function is a keyword that executes a task according to rules designed specifically for that function. Think of arithmetical addition as a task (a function) and the values to be added as the arguments.\n",
"The term _multiple dispatch_ refers to calling the right implementation of a function based on the arguments. Note that only the positional arguments are used to look up the correct method. When the function is used again, but with different argument types, a new method is selected. This is called _overloading_."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"While we would usually think of the task of addition as a single task, adding numbers, it can in fact be seen as more than one function. One with rules for adding integers, one for adding real numbers, one for adding complex numbers, and so on. So, when we call a function (typing the specific keyword and adding the arguments is referred to as _calling the function_), we actually call a whole buch of them. Julia decides which one it is going to use based on the argument types (there is a lookup table for every function, which is stored with the function). Julia generates low-level code based on your computer's instruction set. So, when you create a function () such as...\n",
"```\n",
"function cbd(a)\n",
" return a^3\n",
"end\n",
"```\n",
"... a whole bunch of methods are created (the different implementations of a function are called _methods_). When the function is called with an integer argument, Julia will generate code that uses the CPU's integer multiplication instruction set and when a floating point value is used, the floating point multiplication instruction set will be targeted."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Let's have a look at a quintessential Julia function. You might not recognize it at first, but typing `2 + 3` is actually converted to a keyword with arguments when executed."
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"5"
]
},
"execution_count": 1,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Adding 2 and 3\n",
"2 + 3"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The `+` symbol is actual a function name. The typical _architecture_ of a Julia function is then a keyword, with a set of arguments, seperated by commas, all inside of a set of parenthesis."
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"5"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Addition as a function\n",
"+(2, 3)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[Back to the top](#In-this-lesson)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Creating a simple single expression function"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Functions in Julia can be created much like a mathematical function. Below we create a function called `f` that takes a single argument. We use the character `x` as placeholder argument. The right-hand side of the equation stipulates the task that we want the function to perform, given a value for the argument."
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"f (generic function with 1 method)"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# A function to square the argument value\n",
"f(x) = x^2"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"1 method for generic function <b>f</b>:<ul><li> f(x) in Main at In[3]:2</li> </ul>"
],
"text/plain": [
"# 1 method for generic function \"f\":\n",
"[1] f(x) in Main at In[3]:2"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"methods(f)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We can no call the function and provide an argument."
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"100"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Squaring 10\n",
"f(10)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The answer is $100$ as expected."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Whilest our function seems algebraic in nature, we can create a similar function that will act on a string."
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"p (generic function with 1 method)"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Creating a function to print input to the screen\n",
"p(x) = println(x, \" was entered!\") # The comma concatenates the two strings"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"If we now pass a string as argument and see the result."
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Julia was entered!\n"
]
}
],
"source": [
"# Passing the string \"Julia\"\n",
"p(\"Julia\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We can use more than one argument too."
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"g (generic function with 1 method)"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Simple replication of the + function for two arguments\n",
"g(x, y) = x + y"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Passing two numbers as arguments now adds the two values."
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"7"
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"g(3, 4)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[Back to the top](#In-this-lesson)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Multiple expression functions"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"With single expression functions, it was convenient to use the shortcut (almost mathematical) syntax we used above. If we want a function to do a few more things, even have flow control, we have to use function syntax. In the first example below we will have a function that takes two arguments and performs two tasks (has two expressions)."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The creation of such a proper Julia function is achieved using the `function` keyword. this is followed by the name given to our new function. It is important to stick to conventions and not use illegal words and characters. The former included reserved keywords that are already Julia functions and the latter includes leading numbers."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"A list of placeholder symbols for our arguments follow. In the function below, we use two arguments. The first task we would like the function to perform is to print the two values that are entered as arguments. The second multiplies the values. All function are completed with the `end` keyword."
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"mltpl (generic function with 1 method)"
]
},
"execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Declaring the block of code as a function using the function keyword, giving it a name,\n",
"# and listing the arguments\n",
"function mltpl(x, y)\n",
" print(\"The first value is $x and the second value is $y.\\n$x x $y is:\")\n",
" # The dollar signs are placeholders for the argument values\n",
" # The \\n combination indicates a new-line\n",
" x * y\n",
"end"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"The first value is 3 and the second value is 4.\n",
"3 x 4 is:"
]
},
{
"data": {
"text/plain": [
"12"
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"mltpl(3, 4)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The `return` keyword can be used to force a halt to the taks being performed. It is not immediately obvious how this can be helpful. Below is a demonstartion. (An example that shows the usefulness of the `return` keyword is shown in [Flow control in a function](#Flow-control-in-a-function) below.)"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"mltpl_return (generic function with 1 method)"
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# The expression (task) after the return keyword will be ignored\n",
"function mltpl_return(x, y)\n",
" print(\"The first value is $x and the second value is $y.\\n$x x $y is:\")\n",
" # The dollar signs are placeholders for the argument values\n",
" # The \\n combination indicates a new-line\n",
" return x * y\n",
" x + y # Adding addition of the two argument values after the return keyword\n",
"end"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"The first value is 3 and the second value is 4.\n",
"3 x 4 is:"
]
},
{
"data": {
"text/plain": [
"12"
]
},
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"mltpl_return(3, 4)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The omission of the `return` keyword can lead to some unexpected behaviour. Below, we print a line in the first expression, than successively add, subtract, and multiply the two argument values."
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"omit_return (generic function with 1 method)"
]
},
"execution_count": 14,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"function omit_return(x, y)\n",
" println(\"The argument values that were passed are $x and $y\")\n",
" x + y\n",
" x - y\n",
" x * y\n",
"end"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"The argument values that were passed are 3 and 4\n"
]
},
{
"data": {
"text/plain": [
"12"
]
},
"execution_count": 15,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"omit_return(3, 4)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Only the `println()` expression and the last expression were executed. We can correct this as shown below."
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"multiple_return (generic function with 1 method)"
]
},
"execution_count": 16,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"function multiple_return(x , y)\n",
" println(\"The argument values that were passed are $x and $y\")\n",
" x + y, x - y, x * y\n",
"end"
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"The argument values that were passed are 3 and 4\n"
]
},
{
"data": {
"text/plain": [
"(7, -1, 12)"
]
},
"execution_count": 17,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"multiple_return(3, 4)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We see the result of the arithmetical operations are returned as a tuple. This can be useful as we can assign a computer variable name to each of the elements in the tuple."
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"The argument values that were passed are 3 and 4\n"
]
},
{
"data": {
"text/plain": [
"(7, -1, 12)"
]
},
"execution_count": 18,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"ans1, ans2, ans3 = multiple_return(3, 4)"
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"7"
]
},
"execution_count": 19,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Calling the value in ans1\n",
"ans1"
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"-1"
]
},
"execution_count": 20,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"ans2"
]
},
{
"cell_type": "code",
"execution_count": 21,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"12"
]
},
"execution_count": 21,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"ans3"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[Back to the top](#In-this-lesson)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Flow control in a function"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"A function can have flow control, i.e. `if-else` statements as tasks. Below is an example that also makes the benefits of the use of the `return` keyword more obvious."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The aim of the function is to return the absolute value of the difference between two numbers, without the use of the `abs()` function. The latter returns the absolute value of a value."
]
},
{
"cell_type": "code",
"execution_count": 22,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"abs_diff (generic function with 1 method)"
]
},
"execution_count": 22,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"function abs_diff(x, y)\n",
" if x >= y\n",
" return x - y\n",
" end\n",
" return y - x\n",
"end"
]
},
{
"cell_type": "code",
"execution_count": 23,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"1"
]
},
"execution_count": 23,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# The absolute value of 4 - 3\n",
"abs_diff(4, 3)"
]
},
{
"cell_type": "code",
"execution_count": 24,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"2"
]
},
"execution_count": 24,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# The absolute value of 10 - 12\n",
"abs_diff(10, 12)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[Back to the top](#In-this-lesson)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Using optional arguments"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Optional arguments can be passed as arguments when a function is being created. These are provided with default values. When they are not used when calling the function, these default values are used. They can be overwritten when the argument is called, though."
]
},
{
"cell_type": "code",
"execution_count": 25,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"func (generic function with 2 methods)"
]
},
"execution_count": 25,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"function func(a, b, c = 100)\n",
" print(\" We have the values $a, $b, and $c.\")\n",
"end"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"When omitting to provide the third argument, the default of $100$ is used."
]
},
{
"cell_type": "code",
"execution_count": 26,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
" We have the values 10, 20, and 100."
]
}
],
"source": [
"# Omitting the third argument\n",
"func(10, 20)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Below, we provide a different value to the third argument."
]
},
{
"cell_type": "code",
"execution_count": 27,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
" We have the values 10, 20, and 1000."
]
}
],
"source": [
"func(10, 20, 1000)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[Back to the top](#In-this-lesson)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Using keyword arguments to bypass the order problem"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We can create function with many, many argument. Problem is, we might forget the argument order when calling the function and passing values to it. To solve this problem the semi-colon (;) can be used (usually after the ordered arguments). Let's take a look."
]
},
{
"cell_type": "code",
"execution_count": 28,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"func2 (generic function with 2 methods)"
]
},
"execution_count": 28,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# A most ridiculously long print statement (apologies)\n",
"function func2(a, b, c = 100 ; p = 100, q = \"red\")\n",
" println(\"The first ordered argument value is $(a).\")\n",
" println(\"The second ordered argumnent is $(b).\")\n",
" println(\"The third ordered argument was optional.\")\n",
" println(\"If you see a value of 100 here, you either passed a value of 100 or omitted it: $(c).\")\n",
" println(\"Let's see what happend to the keyword p: $(p).\")\n",
" println(\"Let's see what happens to the keyword q: $(q).\")\n",
" println(\"Oh yes, let's also return something useful, like multiplying $(a) and $(b), yielding:\")\n",
" return a * b\n",
"end"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We can now call the function with just the first two arguments."
]
},
{
"cell_type": "code",
"execution_count": 29,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"The first ordered argument value is 3.\n",
"The second ordered argumnent is 4.\n",
"The third ordered argument was optional.\n",
"If you see a value of 100 here, you either passed a value of 100 or omitted it: 100.\n",
"Let's see what happend to the keyword p: 100.\n",
"Let's see what happens to the keyword q: red.\n",
"Oh yes, let's also return something useful, like multiplying 3 and 4, yielding:\n"
]
},
{
"data": {
"text/plain": [
"12"
]
},
"execution_count": 29,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Calling just the first two ordered arguments\n",
"func2(3, 4)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now, let's change the default value for the third arguments and then also some of the keyword arguments."
]
},
{
"cell_type": "code",
"execution_count": 30,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"The first ordered argument value is 3.\n",
"The second ordered argumnent is 4.\n",
"The third ordered argument was optional.\n",
"If you see a value of 100 here, you either passed a value of 100 or omitted it: 5.\n",
"Let's see what happend to the keyword p: 100.\n",
"Let's see what happens to the keyword q: red.\n",
"Oh yes, let's also return something useful, like multiplying 3 and 4, yielding:\n"
]
},
{
"data": {
"text/plain": [
"12"
]
},
"execution_count": 30,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Calling something else for c\n",
"func2(3, 4, 5)"
]
},
{
"cell_type": "code",
"execution_count": 31,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"The first ordered argument value is 3.\n",
"The second ordered argumnent is 4.\n",
"The third ordered argument was optional.\n",
"If you see a value of 100 here, you either passed a value of 100 or omitted it: 100.\n",
"Let's see what happend to the keyword p: π.\n",
"Let's see what happens to the keyword q: red.\n",
"Oh yes, let's also return something useful, like multiplying 3 and 4, yielding:\n"
]
},
{
"data": {
"text/plain": [
"12"
]
},
"execution_count": 31,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Now let's have some fun with the keyword arguments\n",
"func2(3, 4, p = pi) # Using the pi Julia keyword"
]
},
{
"cell_type": "code",
"execution_count": 32,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"The first ordered argument value is 3.\n",
"The second ordered argumnent is 4.\n",
"The third ordered argument was optional.\n",
"If you see a value of 100 here, you either passed a value of 100 or omitted it: 2.\n",
"Let's see what happend to the keyword p: 100.\n",
"Let's see what happens to the keyword q: Hello!.\n",
"Oh yes, let's also return something useful, like multiplying 3 and 4, yielding:\n"
]
},
{
"data": {
"text/plain": [
"12"
]
},
"execution_count": 32,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Now for q\n",
"func2(3, 4, 2, q = \"Hello!\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The order of the keyword arguments can now be changed when calling the function. As long as we remember to use their names."
]
},
{
"cell_type": "code",
"execution_count": 33,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"The first ordered argument value is 3.\n",
"The second ordered argumnent is 4.\n",
"The third ordered argument was optional.\n",
"If you see a value of 100 here, you either passed a value of 100 or omitted it: 2.\n",
"Let's see what happend to the keyword p: 2.718281828459045.\n",
"Let's see what happens to the keyword q: It works!.\n",
"Oh yes, let's also return something useful, like multiplying 3 and 4, yielding:\n"
]
},
{
"data": {
"text/plain": [
"12"
]
},
"execution_count": 33,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Mixing the keyword arguments around\n",
"func2(3, 4, 2, q = \"It works!\", p = exp(1))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The keyword arguments can indeed be placed anywhere, simply use their names. The values before the semicolon, though has to be used, or at least interspersed in the correct order."
]
},
{
"cell_type": "code",
"execution_count": 34,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"The first ordered argument value is 3.\n",
"The second ordered argumnent is 4.\n",
"The third ordered argument was optional.\n",
"If you see a value of 100 here, you either passed a value of 100 or omitted it: 2.\n",
"Let's see what happend to the keyword p: 1.7320508075688772.\n",
"Let's see what happens to the keyword q: Bananas!.\n",
"Oh yes, let's also return something useful, like multiplying 3 and 4, yielding:\n"
]
},
{
"data": {
"text/plain": [
"12"
]
},
"execution_count": 34,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# And finally, we go bananas!\n",
"func2(q = \"Bananas!\", 3, 4, p = sqrt(3), 2)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[Back to the top](#In-this-lesson)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Functions with a variable number of arguments"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We can use three dots, as in ..., (called a splat or ellipsis) to indicate none, one, or many arguments. Let's take a look."
]
},
{
"cell_type": "code",
"execution_count": 35,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"func3 (generic function with 1 method)"
]
},
"execution_count": 35,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"function func3(args...)\n",
" print(\"I can tell you how many arguments you passed: $(length(args)).\")\n",
"end"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The function simply counts the number of arguments passed."
]
},
{
"cell_type": "code",
"execution_count": 36,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"I can tell you how many arguments you passed: 0."
]
}
],
"source": [
"# Calling nothing, nothing, nothing. Hello! Is anyone home?\n",
"func3()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Below, we take a look at what happens when we pass a variety of arguments."
]
},
{
"cell_type": "code",
"execution_count": 37,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"I can tell you how many arguments you passed: 1."
]
}
],
"source": [
"# Now someone's home!\n",
"func3(1000000)"
]
},
{
"cell_type": "code",
"execution_count": 38,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"I can tell you how many arguments you passed: 1."
]
}
],
"source": [
"# It's Julia!\n",
"func3(\"Julia\")"
]
},
{
"cell_type": "code",
"execution_count": 39,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"I can tell you how many arguments you passed: 2."
]
}
],
"source": [
"# Passing two arguments\n",
"func3(\"Hello\", \"Julia\")"
]
},
{
"cell_type": "code",
"execution_count": 40,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"I can tell you how many arguments you passed: 7."
]
}
],
"source": [
"# Passing multiple arguments of different types\n",
"func3(\"Julia\", \"is\", 1, \"in\", \"a\", 1000000, \"!\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The splat or ellipsis as indicator of allowing the use of multiple (infinite) arguments, can solve some problems. In the example below we will pass a list of strings as arguments and see what happens."
]
},
{
"cell_type": "code",
"execution_count": 41,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"surgery (generic function with 1 method)"
]
},
"execution_count": 41,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# A functions that joins strings\n",
"function surgery(string_array)\n",
" string_items = join(string_array, \", \", \" and \") # Creating a computer variable to hold\n",
" # the arguments and concatenate a comma and the word and\n",
" print(\"Today I performed the following operations: $string_items\", \"!\")\n",
"end"
]
},
{
"cell_type": "code",
"execution_count": 42,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Today I performed the following operations: colonic resection and appendectomy!"
]
}
],
"source": [
"# Passing two arguments\n",
"surgery([\"colonic resection\", \"appendectomy\"])"
]
},
{
"cell_type": "code",
"execution_count": 43,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Today I performed the following operations: a, p, p, e, n, d, e, c, t, o, m and y!"
]
}
],
"source": [
"# What if I forget the square brackets []\n",
"# The join() function will act on the characters in the string\n",
"surgery(\"appendectomy\")"
]
},
{
"cell_type": "code",
"execution_count": 44,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"splat_surgery (generic function with 1 method)"
]
},
"execution_count": 44,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Now we don't restrict the number of arguments\n",
"function splat_surgery(stringsss...)\n",
" string_items = join(stringsss, \", \", \" and \")\n",
" print(\"Today I performed the following operations: $string_items\", \"!\")\n",
"end"
]
},
{
"cell_type": "code",
"execution_count": 45,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Today I performed the following operations: appendectomy!"
]
}
],
"source": [
"splat_surgery(\"appendectomy\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"For the sake of clarity, look at the following example to see what Julia does to the args... arguments. You will note that it is actually managed as a tuple."
]
},
{
"cell_type": "code",
"execution_count": 46,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"argues (generic function with 1 method)"
]
},
"execution_count": 46,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"function argues(a, b, s...)\n",
" print(\"The argument values are: $a, $b, and $s\")\n",
"end"
]
},
{
"cell_type": "code",
"execution_count": 47,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"The argument values are: 3, 4, and (5, 6, 7, 8, \"Julia\")"
]
}
],
"source": [
"# The first two values, 3 and 4, have proper assignment, but the rest will be in a tuple\n",
"argues(3, 4, 5, 6, 7, 8, \"Julia\")"
]
},
{
"cell_type": "code",
"execution_count": 48,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"The argument values are: 3, 4, and ()"
]
}
],
"source": [
"# Now for an empty tuple\n",
"argues(3, 4)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now for some real fun. We can combine keywords and splats. Have a look at this."
]
},
{
"cell_type": "code",
"execution_count": 49,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"fun_func (generic function with 1 method)"
]
},
"execution_count": 49,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Creating a function that only contains keywords, but they are\n",
"# splats\n",
"function fun_func(; a...)\n",
" a\n",
"end"
]
},
{
"cell_type": "code",
"execution_count": 50,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"pairs(::NamedTuple) with 3 entries:\n",
" :var1 => \"Julia\"\n",
" :var2 => \"Language\"\n",
" :val1 => 3"
]
},
"execution_count": 50,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Calling the fun_func() function, remembering to give the keywords names\n",
"fun_func(var1 = \"Julia\", var2 = \"Language\", val1 = 3)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We now have a collection of (key, value) tuples, with the key coming from the name we gave the keyword argument. Moreover, it is actually a symbol which you will note by the colon (:) preceding it."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[Back to the top](#In-this-lesson)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Passing arrays as function arguments"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Once a function is defined, an array of values can be passed to it using the `map()` function."
]
},
{
"cell_type": "code",
"execution_count": 51,
"metadata": {},
"outputs": [],
"source": [
"# Creating an array\n",
"xvals = [-3, -2.5, -2, -1.5, -1, -0.5, 0, 0.5, 1, 1.5, 2, 2.5, 3];"
]
},
{
"cell_type": "code",
"execution_count": 52,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"sqr (generic function with 1 method)"
]
},
"execution_count": 52,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Creating the function\n",
"function sqr(a)\n",
" return a^2\n",
"end"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The `map()` function will now map the function to each value in the array."
]
},
{
"cell_type": "code",
"execution_count": 53,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"13-element Array{Float64,1}:\n",
" 9.0 \n",
" 6.25\n",
" 4.0 \n",
" 2.25\n",
" 1.0 \n",
" 0.25\n",
" 0.0 \n",
" 0.25\n",
" 1.0 \n",
" 2.25\n",
" 4.0 \n",
" 6.25\n",
" 9.0 "
]
},
"execution_count": 53,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Mapping the array to the function\n",
"map(sqr, xvals)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The dot notation after a function achieves the same results."
]
},
{
"cell_type": "code",
"execution_count": 54,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"13-element Array{Float64,1}:\n",
" 9.0 \n",
" 6.25\n",
" 4.0 \n",
" 2.25\n",
" 1.0 \n",
" 0.25\n",
" 0.0 \n",
" 0.25\n",
" 1.0 \n",
" 2.25\n",
" 4.0 \n",
" 6.25\n",
" 9.0 "
]
},
"execution_count": 54,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"sqr.(xvals)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[Back to the top](#In-this-lesson)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Type parameters"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"It is possible to limit a function to accepting only cenrtain argument types."
]
},
{
"cell_type": "code",
"execution_count": 55,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"m (generic function with 1 method)"
]
},
"execution_count": 55,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"function m(x::Int)\n",
" return 3 * x\n",
"end"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Using the `methods()` function, we can now see that only integers argument values are allowed."
]
},
{
"cell_type": "code",
"execution_count": 56,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"1 method for generic function <b>m</b>:<ul><li> m(x::<b>Int64</b>) in Main at In[55]:2</li> </ul>"
],
"text/plain": [
"# 1 method for generic function \"m\":\n",
"[1] m(x::Int64) in Main at In[55]:2"
]
},
"execution_count": 56,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"methods(m)"
]
},
{
"cell_type": "code",
"execution_count": 57,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"9"
]
},
"execution_count": 57,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Calling the function with an integer\n",
"m(3)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"A flotaing point value such as `m(3.)` will result in an error.\n",
"```\n",
"MethodError: no method matching m(::Float64)\n",
"Closest candidates are:\n",
" m(!Matched::Int64) at In[58]:2\n",
"\n",
"Stacktrace:\n",
" [1] top-level scope at In[62]:1\n",
" ```"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[Back to the top](#In-this-lesson)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Stabby functions and do blocks"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Stabby lambda functions as they are called, are quick-and-dirty functions. They are examples of anonymous functions, the latter referring to the fact that they don't have a name. The do block is also a form of anonymous function. Let's look at some examples."
]
},
{
"cell_type": "code",
"execution_count": 58,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"#5 (generic function with 1 method)"
]
},
"execution_count": 58,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# The Julia syntax uses the -> character combinations, hence stabby!\n",
"x -> 2x^2 + 3x - 2"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We can now us the `map()` function to apply the values in an array to this stabby function. Note that the stabby function cannot be called."
]
},
{
"cell_type": "code",
"execution_count": 59,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"5-element Array{Int64,1}:\n",
" 3\n",
" 12\n",
" 25\n",
" 42\n",
" 63"
]
},
"execution_count": 59,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"map(x -> 2x^2 + 3x - 2, [1, 2, 3, 4, 5])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"There is another way of achieving this using `do` blocks."
]
},
{
"cell_type": "code",
"execution_count": 60,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"5-element Array{Int64,1}:\n",
" 3\n",
" 12\n",
" 25\n",
" 42\n",
" 63"
]
},
"execution_count": 60,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Let's do something\n",
"map([1, 2, 3, 4, 5]) do x\n",
" 2x^2 + 3x - 2\n",
"end"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The `do` block can do some more!"
]
},
{
"cell_type": "code",
"execution_count": 61,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"5-element Array{Int64,1}:\n",
" 300\n",
" 600\n",
" 900\n",
" 2000\n",
" 3300"
]
},
"execution_count": 61,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"map([3, 6, 9, 10, 11]) do x\n",
" if mod(x, 3) == 0 # If the value is divisible by 3\n",
" 100x\n",
" elseif mod(x, 3) == 1 # If the remainder after dividing by 3 is 1\n",
" 200x\n",
" else\n",
" mod(x, 3) == 2 # If the remainder is 2\n",
" 300x\n",
" end\n",
"end"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[Back to the top](#In-this-lesson)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Using functions as arguments"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"As the title of this section implies, we can pass a function as an argument. That functional argument will actually call the function."
]
},
{
"cell_type": "code",
"execution_count": 62,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"luv (generic function with 1 method)"
]
},
"execution_count": 62,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# First function\n",
"function string_func(s)\n",
" str = s()\n",
" print(\"I love $str\", \"!\")\n",
"end\n",
"\n",
"# Second function\n",
"function luv()\n",
" return(\"Julia\")\n",
"end"
]
},
{
"cell_type": "code",
"execution_count": 63,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"I love Julia!"
]
}
],
"source": [
"string_func(luv)\n",
"# Calling the function string_func\n",
"# Passing a function as an argument, which then calls that function\n",
"# The called luv function returns the string Julia, which is now the argument of the originally called function"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[Back to the top](#In-this-lesson)"
]
},
{
"cell_type": "code",
"execution_count": 58,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"simple_addition (generic function with 2 methods)"
]
},
"execution_count": 58,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"function simple_addition(a, b = 1; c = 3)\n",
" return a + b + c\n",
"end"
]
},
{
"cell_type": "code",
"execution_count": 59,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"10"
]
},
"execution_count": 59,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"simple_addition(6)"
]
},
{
"cell_type": "code",
"execution_count": 60,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"8"
]
},
"execution_count": 60,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"simple_addition(3,2)"
]
},
{
"cell_type": "code",
"execution_count": 61,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"simple_addition (generic function with 3 methods)"
]
},
"execution_count": 61,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"function simple_addition(x::Int64, y::Int64)\n",
" return x + y\n",
"end"
]
},
{
"cell_type": "code",
"execution_count": 62,
"metadata": {},
"outputs": [
{
"ename": "MethodError",
"evalue": "MethodError: no method matching simple_addition(; x=5, y=5)\nClosest candidates are:\n simple_addition(!Matched::Int64, !Matched::Int64) at In[61]:2 got unsupported keyword arguments \"x\", \"y\"\n simple_addition(!Matched::Any) at In[58]:2 got unsupported keyword arguments \"x\", \"y\"\n simple_addition(!Matched::Any, !Matched::Any; c) at In[58]:2 got unsupported keyword arguments \"x\", \"y\"",
"output_type": "error",
"traceback": [
"MethodError: no method matching simple_addition(; x=5, y=5)\nClosest candidates are:\n simple_addition(!Matched::Int64, !Matched::Int64) at In[61]:2 got unsupported keyword arguments \"x\", \"y\"\n simple_addition(!Matched::Any) at In[58]:2 got unsupported keyword arguments \"x\", \"y\"\n simple_addition(!Matched::Any, !Matched::Any; c) at In[58]:2 got unsupported keyword arguments \"x\", \"y\"",
"",
"Stacktrace:",
" [1] top-level scope at In[62]:1"
]
}
],
"source": [
"simple_addition(x=5,y=5)"
]
},
{
"cell_type": "code",
"execution_count": 63,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"10"
]
},
"execution_count": 63,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"simple_addition(5,5)"
]
},
{
"cell_type": "code",
"execution_count": 64,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"13.0"
]
},
"execution_count": 64,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"simple_addition(5.,5.)"
]
},
{
"cell_type": "code",
"execution_count": 65,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"5-element Array{Int64,1}:\n",
" 3\n",
" 25\n",
" 63\n",
" 117\n",
" 187"
]
},
"execution_count": 65,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"map(x -> 2x^2 + 3x - 2, 1:2:9)"
]
},
{
"cell_type": "code",
"execution_count": 67,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"my_function (generic function with 2 methods)"
]
},
"execution_count": 67,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"function my_function(a::Float64, b::Float64, c::Float64 = 2 + 8im)\n",
" return a - (b * c)\n",
"end"
]
},
{
"cell_type": "code",
"execution_count": 68,
"metadata": {},
"outputs": [
{
"ename": "MethodError",
"evalue": "MethodError: no method matching my_function(::Float64, ::Float64, ::Int64)\nClosest candidates are:\n my_function(::Float64, ::Float64, !Matched::Float64) at In[67]:2\n my_function(::Float64, ::Float64) at In[67]:2",
"output_type": "error",
"traceback": [
"MethodError: no method matching my_function(::Float64, ::Float64, ::Int64)\nClosest candidates are:\n my_function(::Float64, ::Float64, !Matched::Float64) at In[67]:2\n my_function(::Float64, ::Float64) at In[67]:2",
"",
"Stacktrace:",
" [1] top-level scope at In[68]:1"
]
}
],
"source": [
"my_function(4.,3.5,8)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Julia 1.2.0",
"language": "julia",
"name": "julia-1.2"
},
"language_info": {
"file_extension": ".jl",
"mimetype": "application/julia",
"name": "julia",
"version": "1.2.0"
}
},
"nbformat": 4,
"nbformat_minor": 4
}