{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Functions in Symata" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "collapsed": false }, "outputs": [], "source": [ "using Symata # load Symata and enter Symata mode" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There are (more or less) three ways to define functions in Symata.\n", "* via rules\n", "* via the host language (Julia)\n", "* via the pure function expression head `Function`" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Basics of defining and using functions\n", "\n", "This is how you define a function using rules" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/latex": [ "$$ 4 $$" ], "text/plain": [ "L\"$$ 4 $$\"" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "f(x_) := x^2\n", "\n", "f(2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note the trailing underscore in `x_`. This function is implemented via a *rule* for rewriting expressions.\n", "\n", "The argument to `f` can be any expression:" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/latex": [ "$$ \\left[ \\left( x + y \\right) ^{2},\\text{\"dog\"}^{2},4.0 \\right] $$" ], "text/plain": [ "L\"$$ \\left[ \\left( x + y \\right) ^{2},\\text{\\\"dog\\\"}^{2},4.0 \\right] $$\"" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "[f(x+y), f(\"dog\"), f(2.0)]" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "collapsed": true }, "outputs": [], "source": [ "ClearAll(f) # remove the definition for `f`" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This computes the `n`th Fibonacci number." ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/latex": [ "$$ 55 $$" ], "text/plain": [ "L\"$$ 55 $$\"" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "fib(1) := 1\n", "fib(2) := 1\n", "fib(n_) := fib(n-1) + fib(n-2)\n", "\n", "fib(10)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A compound statement can be written like this." ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/latex": [ "$$ 1 + y $$" ], "text/plain": [ "L\"$$ 1 + y $$\"" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "addone(x_) := (a = 1, x + a)\n", "\n", "addone(y)" ] }, { "cell_type": "markdown", "metadata": { "collapsed": true }, "source": [ "But, the variable `a` is not local to the function. The global value of a is set." ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/latex": [ "$$ 1 $$" ], "text/plain": [ "L\"$$ 1 $$\"" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To make variables local to a function, use `Module`" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "collapsed": true }, "outputs": [], "source": [ "g(x_) := Module([c,d],(c=1,d=3,a+d+x)) # local variables are lexically scoped." ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/latex": [ "$$ 4 + z $$" ], "text/plain": [ "L\"$$ 4 + z $$\"" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "g(z)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The global values of `c` and `d` are not affected." ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/latex": [ "$$ \\left[ c,d \\right] $$" ], "text/plain": [ "L\"$$ \\left[ c,d \\right] $$\"" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "[c,d]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The argument to the function `f` in `In(2)` can be any expression. You can also make multiple definitions that apply only when the argument satisfies some requirements. For example, the following makes a definition for `g` that only applies for floating point arguments greater than `5`." ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "collapsed": false }, "outputs": [], "source": [ "gt5(x_) := x > 5\n", "\n", "g(x_Float`gt5`) := 1" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/latex": [ "$$ 1 $$" ], "text/plain": [ "L\"$$ 1 $$\"" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "g(10.0) # The conditions in the new definition are satisfied by 10.0" ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/latex": [ "$$ \\left[ 8.0,14,4 + q + r \\right] $$" ], "text/plain": [ "L\"$$ \\left[ 8.0,14,4 + q + r \\right] $$\"" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "[g(4.0), g(10), g(q + r)] # The definition using `Module` applies here" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can do structural matching on the argument. This matches anything that has the form of a square." ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "collapsed": true }, "outputs": [], "source": [ "h(x_^2) := x" ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/latex": [ "$$ \\left[ r,h \\! \\left( r^{3} \\right) ,q + r,h \\! \\left( q^{2} + 2 \\ q \\ r + r^{2} \\right) ,4,2 \\right] $$" ], "text/plain": [ "L\"$$ \\left[ r,h \\! \\left( r^{3} \\right) ,q + r,h \\! \\left( q^{2} + 2 \\ q \\ r + r^{2} \\right) ,4,2 \\right] $$\"" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "[h(r^2), h(r^3), h((r+q)^2), h(Expand((r+q)^2)), 4 , 2]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Although `4` is the square of `2`, `2` is the square of `Sqrt(2)`, and `q^2 +2*q*r + r^2` is the square of `r+q`, none of these match. Only expressions of the form `Power(expr,2)` match. We see why `h(r^2)` matches by looking at the full form of `r^2`." ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "Power(r,2)" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "FullForm(r^2)" ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "collapsed": true }, "outputs": [], "source": [ "ClearAll(f,h,a)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Use two trailing underscores to match one or more elements" ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "collapsed": false }, "outputs": [], "source": [ "g(x_Symbol, p__Integer) := Apply(Plus, x^[p])" ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "collapsed": false, "scrolled": true }, "outputs": [ { "data": { "text/latex": [ "$$ y + y^{4} + y^{7} + y^{10} $$" ], "text/plain": [ "L\"$$ y + y^{4} + y^{7} + y^{10} $$\"" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "g(y,1,4,7,10)" ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "collapsed": true }, "outputs": [], "source": [ "Apply(ClearAll, UserSyms())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Set and SetDelayed\n", "\n", "Why did we use `:=` rather than `=` above ? One reason is that it is safer. The following example shows why." ] }, { "cell_type": "code", "execution_count": 21, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/latex": [ "$$ \\left[ a^{2},a^{2},9,a^{2} \\right] $$" ], "text/plain": [ "L\"$$ \\left[ a^{2},a^{2},9,a^{2} \\right] $$\"" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "f(x_) := x^2\n", "g(x_) = x^2\n", "x = 3\n", "h(x_) = x^2\n", "j(x_) := x^2\n", "\n", "[f(a), g(a), h(a), j(a)]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In the definition of `h`, `x` is not a dummy variable. If you are beginning with Symata it is enough to know that `:=` should be preferred and you can skip the following.\n", "\n", "What does `f(x_) := x^2` do ? When Symata evaluates this expression, it does not evaluate the right hand side, but rather holds it in its unevaluated form. When the rule is applied, say by evaluating `f(a+b)`, the value that matches `x_`, in this case `a+b`, is substituted for `x` in the right hand side and the result is evaluated with the result `(a+b)^2`. Note that the value of `x` at the time the definition is first evaluated has no affect on this result: The right hand side is never evaluated before subsituting for `x`.\n", "\n", "The definition `g(x_) = x^2` differs in that the right hand side is evaluated when the definition is made. After this, everything is the same as for `:=`. The value that matches `x_` is substituted in the right hand side and the result is evaluated. If `x` was unbound when the definition was made, the results are identical, because the substituion will be made before evaluating the right hand side again, so assignments to `x` in the mean time will have no effect. However if `x` *is* bound at the time of the definition, then the value of `x^2` at the time of defnition is stored and the match of `x_` is substituted into this expression. If `x` had a value of `3`, then `a+b` is substituted into `3`, which just results in `3`. \n", "\n", "These \"function\" definitions actually define `DownValues`. We can see that the definitions of `f`,`g`, and `j` are the same, no matter what the future values of `x`." ] }, { "cell_type": "code", "execution_count": 22, "metadata": { "collapsed": false, "scrolled": true }, "outputs": [ { "data": { "text/latex": [ "$$ \\left[ \\left[ \\text{HoldPattern} \\! \\left( f \\! \\left( x\\text{_} \\right) \\right) \\text{:>}x^{2} \\right] , \\left[ \\text{HoldPattern} \\! \\left( g \\! \\left( x\\text{_} \\right) \\right) \\text{:>}x^{2} \\right] , \\left[ \\text{HoldPattern} \\! \\left( h \\! \\left( x\\text{_} \\right) \\right) \\text{:>}9 \\right] , \\left[ \\text{HoldPattern} \\! \\left( j \\! \\left( x\\text{_} \\right) \\right) \\text{:>}x^{2} \\right] \\right] $$" ], "text/plain": [ "L\"$$ \\left[ \\left[ \\text{HoldPattern} \\! \\left( f \\! \\left( x\\text{_} \\right) \\right) \\text{:>}x^{2} \\right] , \\left[ \\text{HoldPattern} \\! \\left( g \\! \\left( x\\text{_} \\right) \\right) \\text{:>}x^{2} \\right] , \\left[ \\text{HoldPattern} \\! \\left( h \\! \\left( x\\text{_} \\right) \\right) \\text{:>}9 \\right] , \\left[ \\text{HoldPattern} \\! \\left( j \\! \\left( x\\text{_} \\right) \\right) \\text{:>}x^{2} \\right] \\right] $$\"" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Map(DownValues, [f,g,h,j])" ] }, { "cell_type": "code", "execution_count": 23, "metadata": { "collapsed": true }, "outputs": [], "source": [ "ClearAll(f,g,h,j,x) # clear the symbols used in this example" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Julia functions\n", "\n", "We mentioned above that these defintions define `Rule`s. You can also use compiled functions in Symata. One way is to code such a function directly in the host language, Julia." ] }, { "cell_type": "code", "execution_count": 24, "metadata": { "collapsed": true }, "outputs": [], "source": [ "1;" ] }, { "cell_type": "code", "execution_count": 25, "metadata": { "collapsed": false }, "outputs": [], "source": [ "ClearAll(g,h,a,b)\n", "# J(expr) interprets expr as Julia code\n", "f = J((x) -> x^2 ) ;" ] }, { "cell_type": "code", "execution_count": 26, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/latex": [ "$$ 9 $$" ], "text/plain": [ "L\"$$ 9 $$\"" ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "f(3)" ] }, { "cell_type": "code", "execution_count": 27, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/latex": [ "$$ \\text{\"dogdog\"} $$" ], "text/plain": [ "L\"$$ \\text{\\\"dogdog\\\"} $$\"" ] }, "execution_count": 27, "metadata": {}, "output_type": "execute_result" } ], "source": [ "f(\"dog\") # This calls the Julia function, so we get what \"dog\"^2 means in Julia" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that we did not use `:=` in the definition of `f`. That is, `f := :((x) -> x^2 )`. This would create a new anonymous function everytime `f` is called, which would be very slow." ] }, { "cell_type": "code", "execution_count": 28, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/latex": [ "$$ \\left( a + b \\right) ^{2} $$" ], "text/plain": [ "L\"$$ \\left( a + b \\right) ^{2} $$\"" ] }, "execution_count": 28, "metadata": {}, "output_type": "execute_result" } ], "source": [ "f(a+b)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The last example works because `^` has been \"overloaded\" in Julia to operate on Symata expressions. (Symata expressions are objects of type `Symata.Mxpr` in Julia.)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Pure (anonymous) functions" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Anonymous or pure functions are used like this:" ] }, { "cell_type": "code", "execution_count": 29, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/latex": [ "$$ a^{2} $$" ], "text/plain": [ "L\"$$ a^{2} $$\"" ] }, "execution_count": 29, "metadata": {}, "output_type": "execute_result" } ], "source": [ "Function(x, x^2)(a)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The infix operator `->` is equivalent to `Function`. Note that this is *not* a Julia function." ] }, { "cell_type": "code", "execution_count": 30, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/latex": [ "$$ a^{2} $$" ], "text/plain": [ "L\"$$ a^{2} $$\"" ] }, "execution_count": 30, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(x -> x^2)(a)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A function of two variables is defined like this:" ] }, { "cell_type": "code", "execution_count": 31, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/latex": [ "$$ 25 $$" ], "text/plain": [ "L\"$$ 25 $$\"" ] }, "execution_count": 31, "metadata": {}, "output_type": "execute_result" } ], "source": [ "([x,y] -> x^2 + y^2)(3,4)" ] }, { "cell_type": "markdown", "metadata": { "collapsed": true }, "source": [ "### Version and date" ] }, { "cell_type": "code", "execution_count": 32, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Symata version 0.4.1-dev.3\n", "Julia version 0.7.0-beta2.1\n", "Python version 2.7.14+\n", "SymPy version 1.0\n" ] } ], "source": [ "VersionInfo()" ] }, { "cell_type": "code", "execution_count": 33, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "2018-07-20T13:18:33.604" ] }, "execution_count": 33, "metadata": {}, "output_type": "execute_result" } ], "source": [ "InputForm(Now())" ] } ], "metadata": { "kernelspec": { "display_name": "Julia 0.7.0-beta2", "language": "julia", "name": "julia-0.7" }, "language_info": { "file_extension": ".jl", "mimetype": "application/julia", "name": "julia", "version": "0.7.0" } }, "nbformat": 4, "nbformat_minor": 1 }