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