{ "cells": [ { "cell_type": "markdown", "metadata": { "collapsed": true }, "source": [ "# Functions\n", "\n", "This notebook shows how to define functions with one (or several) inputs and outputs. It also shows how to use to dot (.) syntax to apply a function to each element of an array." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Load Packages and Extra Functions" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "printyellow (generic function with 1 method)" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "using Printf\n", "\n", "include(\"jlFiles/printmat.jl\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Functions with One Output" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## The Basic Approach\n", "\n", "The basic approach to define a function with the name `fn` is\n", "```\n", "function fn(x,b)\n", " ...(some code)\n", " return y\n", "end\n", "``` \n", "\n", "Once you have defined a function, you can use it (call on it) by, for instance, `y1 = fn(2,1)`. This will generate a `y1` variable (not a `y` variable) in the workspace. Inside the function, `x` is then 2 and `b` is 1. Clearly, if `x1=2 and b1=1`, you get the same result by calling as `y1 = fn(x1,b1)`." ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "result from fn1(2,1): 0.310\n" ] } ], "source": [ "function fn1(x,b) #x and b are the inputs\n", " c = 0.5 #c is only \"seen\" inside the function\n", " y = b*(x-1.1)^2 - c\n", " return y #this is the output\n", "end\n", "\n", "y1 = fn1(2,1)\n", "printlnPs(\"result from fn1(2,1): \",y1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Default Values for the Inputs\n", "\n", "You can change the first line of the function to specify default values as \n", "```\n", "function fn(x,b=1)\n", "```\n", "\n", "In this case you can call on the function as `fn(x)` and the value of `b` will default to 1. (Clearly, inputs with default values must be towards the end of the list of inputs.)" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "result from fn2(2,1) and fn2(2): 0.310 0.310\n" ] } ], "source": [ "function fn2(x,b=1) #b=1 is the default in case we call as fn2(x)\n", " y = b*(x-1.1)^2 - 0.5\n", " return y\n", "end\n", "\n", "printlnPs(\"result from fn2(2,1) and fn2(2): \",fn2(2,1),fn2(2))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Elementwise Evaluation\n", "\n", "To apply the function to each element of arrays `x` and `b`, use the dot syntax (broadcasting): \n", "```\n", "y = fn.(x,b)\n", "```\n", "This calculates `fn(x[i],b[i])` for each pair `(x[i],b[i])`.\n", "\n", "Instead, with `fn.(x,2)`, you calculate `fn(x[i],2)` for each element `x[i]`." ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "result from fn2.(x1,2): \n", " -0.480\n", " -0.180\n", "\n", "\n", "result from fn2.(x1,b1): \n", " -0.480\n", " -0.180\n", "\n" ] } ], "source": [ "x1 = [1,1.5]\n", "b1 = [2,2]\n", "\n", "println(\"\\nresult from fn2.(x1,2): \")\n", "printmat(fn2.(x1,2))\n", "\n", "println(\"\\nresult from fn2.(x1,b1): \")\n", "printmat(fn2.(x1,b1))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Elementwise Evaluation over *Some* Inputs (extra)\n", "\n", "\n", "To apply the function to each element of the array `x`, but keep `b` fixed, use : \n", "```\n", "y = fn.(x,Ref(b))\n", "```\n", "\n", "For instance, `fn301.(x1,Ref(b1))` in the next cell will create the 2-element vector\n", "```\n", "1 + (10+20+30)\n", "1.5 + (10+20+30)\n", "```" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "result from fn301.(x1,Ref(b1)):\n", " 61.000\n", " 61.500\n", "\n" ] } ], "source": [ "function fn301(x,b)\n", " y = x + sum(b)\n", " return y\n", "end\n", "\n", "x1 = [1,1.5]\n", "b1 = [10,20,30]\n", "\n", "println(\"result from fn301.(x1,Ref(b1)):\")\n", "printmat(fn301.(x1,Ref(b1)))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Short Form (extra)\n", "\n", "We can also use short forms of a function as in the cell below. \n", "\n", "The first version (`fn3`) is just a single expression. It could span several lines. The second version (`fn3b`) is a sequence of expressions (a \"compound expression\") separated by semicolons (`;`). The last expression is the function output." ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "result from fn3(1.5,1) and fn3b(1.5,1): -0.340 -0.340\n" ] } ], "source": [ "fn3(x,b) = b*(x-1.1)^2 - 0.5 #short form of a function\n", "\n", "fn3b(x,b) = (c = 0.5;b*(x-1.1)^2 - c) #this works too. Notice the ;\n", "\n", "printlnPs(\"result from fn3(1.5,1) and fn3b(1.5,1): \",fn3(1.5,1),fn3b(1.5,1))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Explicit Names of the Inputs: Keyword Arguments\n", "\n", "You can also define functions that take *keyword arguments* like in \n", "```\n", "fn(x;b,c)\n", "``` \n", "Notice the semi-colon (`;`). You can also specify default values as `fn(x;b=1,c=0.5)`\n", "\n", "In this case, you *call* on the function by `fn(x,c=3,b=2)` or just `fn(x)` if you want to use the default values. This helps remembering/interpreting what the arguments represent. When calling on the function, you can pass the keyword arguments in any order and you can use comma (`,`) instead of semi-colon (`;`).\n", "\n", "(Extra) You can also use a `Dict()` to supply the keyword arguments as in\n", "```\n", "opt = Dict(:b=>1,:c=>0.5)\n", "fn(x;opt...)\n", "```\n", "Notice that the dictionary must use symbols and that you need to use `;` and `...` in the call. It is also possible to mix traditional keywords with a dictionary as in `opt = Dict(:c=>0.5); fn(x;b=1,opt...)`" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "result from fn4(1,c=3,b=2): -2.980\n" ] } ], "source": [ "function fn4(x;b=1,c=0.5)\n", " y = b*(x-1.1)^2 - c\n", " return y\n", "end\n", "\n", "printlnPs(\"result from fn4(1,c=3,b=2): \",fn4(1,c=3,b=2))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Variable Number of Inputs (extra)\n", "\n", "When *defining a function*, we can use the `x...` syntax (also called \"slurping\") on the last non-keyword input to capture a variable number of inputs. Later, when calling on this function, several inputs will be combined into a vector `x`.\n", "\n", "For instance, `fn5(-9,1,2,b=10)` in the next cell will create the 2-element vector\n", "```\n", "-9 + 1*10\n", "-9 + 2*10\n", "```" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "result from fn5(-9,1,2,b=10): \n", " 1.000\n", " 11.000\n", "\n" ] } ], "source": [ "function fn5(a,x...;b=1) #a is traditional input, x... captures several inputs; b=1 is a keyword input\n", " n = length(x)\n", " y = zeros(n)\n", " for i = 1:n\n", " y[i] = a + x[i]*b\n", " end\n", " return y\n", "end\n", "\n", "y = fn5(-9,1,2,b=10)\n", "println(\"result from fn5(-9,1,2,b=10): \")\n", "printmat(y)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Conversely, you can also use the `x...` syntax (also called \"splatting\") when *calling on a function* that requires several inputs.\n", "\n", "In the next cell, when running `fn5b([1,2]...)`, `x1` will be 1 and `x2` will be 2." ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1 and then also 2\n" ] } ], "source": [ "function fn5b(x1,x2)\n", " println(x1,\" and then also \",x2)\n", " return nothing\n", "end\n", "\n", "fn5b([1,2]...)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Functions with Several Outputs" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Several Outputs 1: Basic Approach\n", "\n", "A function can produce a \"tuple\" like `(y1,y2,y3)` as output.\n", "\n", "In case you only want the first two outputs, call as `(y1,y2,) = fn(x)`.\n", "\n", "Instead, if you only want the second and third outputs, call as `(_,y2,y3) = fn(x)`\n", "\n", "You can also extract the second output as `y2 = fn(x)[2]`\n", "\n", "If `Y1` is an already existing array, then `(Y1[3],y2,) = fn(x)` will change element 3 of `Y1` (and also assign a value to `y2`)." ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "The first 2 outputs from fn11(1,2): -0.480 2 \n", "\n", "The result from calling fn11(1,2)[2]: 2 \n", "\n", "Y1 array after calling fn11(1,2): 0.000 0.000 -0.480 0.000 0.000\n" ] } ], "source": [ "function fn11(x,b=1)\n", " y1 = b*(x-1.1)^2 - 0.5\n", " y2 = b*x\n", " y3 = 3\n", " return y1, y2, y3\n", "end\n", "\n", "(y1,y2,) = fn11(1,2)\n", "printlnPs(\"The first 2 outputs from fn11(1,2): \",y1,y2)\n", "\n", "y2 = fn11(1,2)[2] #grab the second output\n", "printlnPs(\"\\nThe result from calling fn11(1,2)[2]: \",y2)\n", "\n", "Y1 = zeros(5) #create an array\n", "(Y1[3],y2,) = fn11(1,2) #put result in existing array\n", "printlnPs(\"\\nY1 array after calling fn11(1,2): \",Y1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Several Outputs 2: Named Tuples and Dictionaries (extra)\n", "\n", "\n", "Instead of returning several values, it might be easier to combine them into either a \"named tuple\" or a dictionary and then export that.\n", "\n", "You could end the function with \n", "\n", "```\n", " y = (a=y1,b=y2,c=y3) #named tuple\n", " return y\n", "end\n", "```\n", "\n", "or \n", "\n", "```\n", " y = Dict(:a=>y1,:b=>y2,:c=>y3) #dictionary\n", " return y\n", "end\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Several Outputs 3: Elementwise Evaluation (extra)\n", "\n", "...can be tricky, because you get an array (same dimension as the input) of tuples instead of a tuple of arrays (which is probably what you want).\n", "\n", "One way around this is to reshuffle the output to get a tuple of arrays.\n", "\n", "Alternatively, you could loop over the function calls or write the function in such a way that it directly handles array inputs (without the dot). The latter is done in `fn14()`." ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "y, a 2-element vector of tuples (3 elements in each):\n", "(-0.48, 2.0, 3)\n", "(-0.18000000000000016, 3.0, 3)\n", "\n", "\n", "the vectors y1, y2 and y3:\n", " -0.480 2.000 3.000\n", " -0.180 3.000 3.000\n", "\n" ] } ], "source": [ "y = fn11.([1,1.5],2)\n", "println(\"y, a 2-element vector of tuples (3 elements in each):\")\n", "printmat(y)\n", "\n", "(y1,y2,y3) = ntuple(i->getindex.(y,i),3) #split up into 3 vectors\n", "\n", "println(\"\\nthe vectors y1, y2 and y3:\")\n", "printmat([y1 y2 y3])" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "result from fn14(x1,2): \n", " -0.480 2.000\n", " -0.180 3.000\n", "\n", " 3 \n", "\n" ] } ], "source": [ "function fn14(x,b=1) #x can be an array\n", " y1 = b*(x.-1.1).^2 .- 0.5\n", " y2 = b*x\n", " y3 = 3\n", " return y1, y2, y3\n", "end\n", "\n", "(y1,y2,y3) = fn14(x1,2) #function written to handle arrays\n", "println(\"result from fn14(x1,2): \")\n", "printmat([y1 y2])\n", "printmat(y3)" ] }, { "cell_type": "markdown", "metadata": { "collapsed": true }, "source": [ "# Documenting Your Function \n", "\n", "To use Julia's help function (`? FunctionName`), put the documentation in triple quotes, just above the function definition. (No empty lines between the last triple quote and the start of the function.) The cell below illustrates a simple case." ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "fn101" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "\"\"\"\n", " fn101(x,b=1)\n", "\n", "Calculate `b*(x-1.1)^2 - 0.5`.\n", "\n", "# Arguments\n", "- `x::Number`: an important number\n", "- `b::Number`: another number\n", "\n", "\"\"\"\n", "function fn101(x,b=1)\n", " y = b*(x-1.1)^2 - 0.5\n", " return y\n", "end" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "search: \u001b[0m\u001b[1mf\u001b[22m\u001b[0m\u001b[1mn\u001b[22m\u001b[0m\u001b[1m1\u001b[22m\u001b[0m\u001b[1m0\u001b[22m\u001b[0m\u001b[1m1\u001b[22m\n", "\n" ] }, { "data": { "text/latex": [ "\\begin{verbatim}\n", "fn101(x,b=1)\n", "\\end{verbatim}\n", "Calculate \\texttt{b*(x-1.1)\\^{}2 - 0.5}.\n", "\n", "\\section{Arguments}\n", "\\begin{itemize}\n", "\\item \\texttt{x::Number}: an important number\n", "\n", "\n", "\\item \\texttt{b::Number}: another number\n", "\n", "\\end{itemize}\n" ], "text/markdown": [ "```\n", "fn101(x,b=1)\n", "```\n", "\n", "Calculate `b*(x-1.1)^2 - 0.5`.\n", "\n", "# Arguments\n", "\n", " * `x::Number`: an important number\n", " * `b::Number`: another number\n" ], "text/plain": [ "\u001b[36m fn101(x,b=1)\u001b[39m\n", "\n", " Calculate \u001b[36mb*(x-1.1)^2 - 0.5\u001b[39m.\n", "\n", "\u001b[1m Arguments\u001b[22m\n", "\u001b[1m ≡≡≡≡≡≡≡≡≡≡≡\u001b[22m\n", "\n", " • \u001b[36mx::Number\u001b[39m: an important number\n", "\n", " • \u001b[36mb::Number\u001b[39m: another number" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "? fn101" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Sending a Function to a Function\n", "\n", "You can use a function name (for instance, `cos`) as an input to another function." ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "result from fn201(cos,3.145,10): 9.000\n", "\n", "result from fn201(z->mod(z,2),3.145,10): 11.145\n" ] } ], "source": [ "function fn201(f,a,b) #f is a function, could also write f::Function\n", " y = f(a) + b\n", " return y\n", "end\n", "\n", "#here f is the cos function\n", "printlnPs(\"result from fn201(cos,3.145,10): \",fn201(cos,3.145,10)) \n", "\n", "#here f is z->mod(z,2), an anonymous function\n", "printlnPs(\"\\nresult from fn201(z->mod(z,2),3.145,10):\",fn201(z->mod(z,2),3.145,10))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "@webio": { "lastCommId": null, "lastKernelId": null }, "anaconda-cloud": {}, "kernelspec": { "display_name": "Julia 1.6.1", "language": "julia", "name": "julia-1.6" }, "language_info": { "file_extension": ".jl", "mimetype": "application/julia", "name": "julia", "version": "1.6.1" } }, "nbformat": 4, "nbformat_minor": 1 }