├── live_versions ├── README.md ├── INFO.md ├── 3. Case study of metaprogramming.ipynb └── 2. Metaprogramming.ipynb ├── Dockerfile ├── README.md ├── LICENSE.md ├── 4. Developing a package.ipynb ├── 3. Case study of metaprogramming.ipynb └── 2. Metaprogramming.ipynb /live_versions/README.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /live_versions/INFO.md: -------------------------------------------------------------------------------- 1 | These are the live versions of the notebooks from the talk at JuliaCon 2016. 2 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM andrewosh/binder-base 2 | 3 | # MAINTAINER Andrew Osheroff 4 | 5 | USER root 6 | 7 | # Add Julia dependencies 8 | RUN apt-get update && apt-get install -y wget && apt-get clean 9 | 10 | USER main 11 | 12 | RUN wget https://julialang.s3.amazonaws.com/bin/linux/x64/0.4/julia-0.4.6-linux-x86_64.tar.gz 13 | RUN mkdir $HOME/julia 14 | RUN tar xvf julia-0.4.6-linux-x86_64.tar.gz -C $HOME/julia --strip-components=1 15 | ENV PATH $PATH:$HOME/julia/bin 16 | 17 | # Install Julia kernel 18 | RUN julia -e 'Pkg.add("IJulia")' 19 | RUN julia -e 'Pkg.add("PyPlot")' 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Invitation to Intermediate Julia 2 | 3 | Material for a 3-hour workshop **Invitation to Intermediate Julia**, presented at JuliaCon 2016. 4 | The video is available [here](https://www.youtube.com/watch?v=rAxzR7lMGDM). 5 | 6 | This follows on from my 4-hour workshop [**Invitation to Julia**](https://github.com/dpsanders/invitation_to_julia) at JuliaCon 2015. 7 | 8 | 9 | ### Financial support 10 | 11 | Financial support is acknowledged from DGAPA-UNAM (Mexico) PAPIME grant PE-107114, DGAPA-UNAM PAPIIT grant IN-117214, and from a CONACYT-Mexico sabbatical fellowship. The author thanks Alan Edelman and the Julia group for hospitality during his sabbatical visit. 12 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The Invitation to Intermediate Julia material is licensed under following licenses: 2 | 3 | - the **code** portions under the MIT "Expat" License; 4 | 5 | - **everything else** under the CC-by-SA 4.0 License: 6 | 7 | 8 | ## MIT "Expat" License for the code portions: 9 | 10 | > Copyright (c) 2016: David Sanders. 11 | > 12 | > Permission is hereby granted, free of charge, to any person obtaining 13 | > a copy of this software and associated documentation files (the 14 | > "Software"), to deal in the Software without restriction, including 15 | > without limitation the rights to use, copy, modify, merge, publish, 16 | > distribute, sublicense, and/or sell copies of the Software, and to 17 | > permit persons to whom the Software is furnished to do so, subject to 18 | > the following conditions: 19 | > 20 | > The above copyright notice and this permission notice shall be 21 | > included in all copies or substantial portions of the Software. 22 | > 23 | > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 24 | > EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 25 | > MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 26 | > IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 27 | > CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 28 | > TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 29 | > SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 30 | 31 | ## CC by SA 4.0 for the non-code portions: 32 | Creative Commons License
This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License. 33 | -------------------------------------------------------------------------------- /4. Developing a package.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Developing a package " 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "Once you have some experience with Julia, one of the best ways of learning more is to contribute to a pre-existing package. You can also [write tests for Julia itself](https://github.com/JuliaLang/julia/issues/11885). You may also wish to develop a new package for functionality that is not yet available in the Julia ecosystem." 15 | ] 16 | }, 17 | { 18 | "cell_type": "markdown", 19 | "metadata": {}, 20 | "source": [ 21 | "## Create a package " 22 | ] 23 | }, 24 | { 25 | "cell_type": "markdown", 26 | "metadata": {}, 27 | "source": [ 28 | "Julia's package manager simplifies some of the trickier aspects of setting up packages. To create a new, empty package, do:" 29 | ] 30 | }, 31 | { 32 | "cell_type": "code", 33 | "execution_count": null, 34 | "metadata": { 35 | "collapsed": false 36 | }, 37 | "outputs": [], 38 | "source": [ 39 | "Pkg.generate(\"JuliaCon\", \"MIT\")" 40 | ] 41 | }, 42 | { 43 | "cell_type": "markdown", 44 | "metadata": {}, 45 | "source": [ 46 | "replacing `JuliaCon` by the name of the package you would like to generate.\n", 47 | "\n", 48 | "This creates a new directory with the same name inside `~/.julia/v0.4` with the same name, with the default MIT license and the standard Julia package structure:" 49 | ] 50 | }, 51 | { 52 | "cell_type": "code", 53 | "execution_count": null, 54 | "metadata": { 55 | "collapsed": false 56 | }, 57 | "outputs": [], 58 | "source": [ 59 | "; ls ~/.julia/v0.4/JuliaCon # works on Linux and OSX" 60 | ] 61 | }, 62 | { 63 | "cell_type": "markdown", 64 | "metadata": {}, 65 | "source": [ 66 | "Packages in Julia are `git` repositories. Now (or yesterday) is a good time to learn `git`, e.g. using \n", 67 | "the [Software Carpentry lessons](http://swcarpentry.github.io/git-novice/)." 68 | ] 69 | }, 70 | { 71 | "cell_type": "markdown", 72 | "metadata": {}, 73 | "source": [ 74 | "Inside the `src` subdirectory is a single Julia file with the same name as your package:" 75 | ] 76 | }, 77 | { 78 | "cell_type": "code", 79 | "execution_count": null, 80 | "metadata": { 81 | "collapsed": false 82 | }, 83 | "outputs": [], 84 | "source": [ 85 | "; ls ~/.julia/v0.4/JuliaCon/src" 86 | ] 87 | }, 88 | { 89 | "cell_type": "code", 90 | "execution_count": null, 91 | "metadata": { 92 | "collapsed": false 93 | }, 94 | "outputs": [], 95 | "source": [ 96 | "; cat ~/.julia/v0.4/JuliaCon/src/JuliaCon.jl" 97 | ] 98 | }, 99 | { 100 | "cell_type": "markdown", 101 | "metadata": {}, 102 | "source": [ 103 | "This is a Julia **module**, which can be thought of as a separate workspace with separate names. You make available only those functions that are relevant for the user of the package using `export`." 104 | ] 105 | }, 106 | { 107 | "cell_type": "markdown", 108 | "metadata": {}, 109 | "source": [ 110 | "## Develop your package " 111 | ] 112 | }, 113 | { 114 | "cell_type": "markdown", 115 | "metadata": {}, 116 | "source": [ 117 | "The next step is to fill up your package with code in the `src` directory.\n", 118 | "\n", 119 | "It is standard to separate the code into different files that you `include` in the module:" 120 | ] 121 | }, 122 | { 123 | "cell_type": "code", 124 | "execution_count": null, 125 | "metadata": { 126 | "collapsed": false 127 | }, 128 | "outputs": [], 129 | "source": [ 130 | "module JuliaCon\n", 131 | "\n", 132 | "include(\"my_stuff.jl\")\n", 133 | "include(\"my_other_stuff.jl\")\n", 134 | "\n", 135 | "end # module" 136 | ] 137 | }, 138 | { 139 | "cell_type": "markdown", 140 | "metadata": {}, 141 | "source": [ 142 | "## Write tests " 143 | ] 144 | }, 145 | { 146 | "cell_type": "markdown", 147 | "metadata": {}, 148 | "source": [ 149 | "All code requires **tests**. You can use either `Base.Test` (now preferred) or the [`FactCheck.jl`](https://github.com/JuliaLang/FactCheck.jl) package." 150 | ] 151 | }, 152 | { 153 | "cell_type": "code", 154 | "execution_count": null, 155 | "metadata": { 156 | "collapsed": true 157 | }, 158 | "outputs": [], 159 | "source": [ 160 | "using FactCheck" 161 | ] 162 | }, 163 | { 164 | "cell_type": "code", 165 | "execution_count": null, 166 | "metadata": { 167 | "collapsed": false 168 | }, 169 | "outputs": [], 170 | "source": [ 171 | "facts(\"Testing arithmetic\") do\n", 172 | " @fact 3+3 ==> 6\n", 173 | " \n", 174 | " x = 17\n", 175 | " \n", 176 | " @fact isa(x/3, Float64) => true\n", 177 | "end" 178 | ] 179 | }, 180 | { 181 | "cell_type": "markdown", 182 | "metadata": {}, 183 | "source": [ 184 | "This code goes in `runtests.jl` in the `test` subdirectory. Again, you can `include` several files in `runtests.jl`." 185 | ] 186 | }, 187 | { 188 | "cell_type": "markdown", 189 | "metadata": {}, 190 | "source": [ 191 | "You can test your package with" 192 | ] 193 | }, 194 | { 195 | "cell_type": "code", 196 | "execution_count": null, 197 | "metadata": { 198 | "collapsed": false 199 | }, 200 | "outputs": [], 201 | "source": [ 202 | "Pkg.test(\"JuliaCon\")" 203 | ] 204 | }, 205 | { 206 | "cell_type": "markdown", 207 | "metadata": {}, 208 | "source": [ 209 | "## Document your package " 210 | ] 211 | }, 212 | { 213 | "cell_type": "markdown", 214 | "metadata": {}, 215 | "source": [ 216 | "You must document your package if you would like to have >1 user. The current solution for documentation is https://github.com/JuliaDocs/Documenter.jl." 217 | ] 218 | }, 219 | { 220 | "cell_type": "markdown", 221 | "metadata": {}, 222 | "source": [ 223 | "## Publishing your package " 224 | ] 225 | }, 226 | { 227 | "cell_type": "markdown", 228 | "metadata": {}, 229 | "source": [ 230 | "Once your package is ready to publish, you register it locally:" 231 | ] 232 | }, 233 | { 234 | "cell_type": "code", 235 | "execution_count": null, 236 | "metadata": { 237 | "collapsed": false 238 | }, 239 | "outputs": [], 240 | "source": [ 241 | "Pkg.register(\"JuliaCon\")" 242 | ] 243 | }, 244 | { 245 | "cell_type": "markdown", 246 | "metadata": {}, 247 | "source": [ 248 | "and tag a version:" 249 | ] 250 | }, 251 | { 252 | "cell_type": "code", 253 | "execution_count": null, 254 | "metadata": { 255 | "collapsed": false 256 | }, 257 | "outputs": [], 258 | "source": [ 259 | "Pkg.tag(\"JuliaCon\")" 260 | ] 261 | }, 262 | { 263 | "cell_type": "markdown", 264 | "metadata": {}, 265 | "source": [ 266 | "This creates a tag in `git` which always refers to this specific version of the code (`v0.0.1` by default). \n", 267 | "Now publish it:" 268 | ] 269 | }, 270 | { 271 | "cell_type": "code", 272 | "execution_count": null, 273 | "metadata": { 274 | "collapsed": true 275 | }, 276 | "outputs": [], 277 | "source": [ 278 | "Pkg.publish(\"JuliaCon\")" 279 | ] 280 | }, 281 | { 282 | "cell_type": "markdown", 283 | "metadata": {}, 284 | "source": [ 285 | "This sends the information on your package to `METADATA`, the central repository for Julia packages. Your proposed package will be looked over, and will be accepted if it meets certain minimum standards." 286 | ] 287 | }, 288 | { 289 | "cell_type": "code", 290 | "execution_count": null, 291 | "metadata": { 292 | "collapsed": true 293 | }, 294 | "outputs": [], 295 | "source": [ 296 | "Pkg.add(\"JuliaCon\")" 297 | ] 298 | }, 299 | { 300 | "cell_type": "markdown", 301 | "metadata": {}, 302 | "source": [ 303 | "If not, or while you are getting it ready, you can just publish the fact that it exists on your blog, on Twitter, or on the Julia users list, and people can do" 304 | ] 305 | }, 306 | { 307 | "cell_type": "code", 308 | "execution_count": null, 309 | "metadata": { 310 | "collapsed": true 311 | }, 312 | "outputs": [], 313 | "source": [ 314 | "Pkg.clone(\"https://github.com/dpsanders/BilliardModels.jl\")" 315 | ] 316 | }, 317 | { 318 | "cell_type": "markdown", 319 | "metadata": {}, 320 | "source": [ 321 | "straight from the URL of your public `git` repository on a server." 322 | ] 323 | }, 324 | { 325 | "cell_type": "markdown", 326 | "metadata": {}, 327 | "source": [ 328 | "## On the importance of **tests**" 329 | ] 330 | }, 331 | { 332 | "cell_type": "markdown", 333 | "metadata": { 334 | "collapsed": true 335 | }, 336 | "source": [ 337 | "Having a good test suite is crucial to having a healthy software package, since any mistake or change in functionality is rapidly caught." 338 | ] 339 | } 340 | ], 341 | "metadata": { 342 | "kernelspec": { 343 | "display_name": "Julia 0.4.6", 344 | "language": "julia", 345 | "name": "julia-0.4" 346 | }, 347 | "language_info": { 348 | "file_extension": ".jl", 349 | "mimetype": "application/julia", 350 | "name": "julia", 351 | "version": "0.4.6" 352 | }, 353 | "widgets": { 354 | "state": {}, 355 | "version": "1.1.2" 356 | } 357 | }, 358 | "nbformat": 4, 359 | "nbformat_minor": 0 360 | } 361 | -------------------------------------------------------------------------------- /3. Case study of metaprogramming.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Case study of metaprogramming: interval constraint propagation " 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "Perhaps the best way to learn metaprogramming is through an example. \n", 15 | "Let's look at **interval constraint propagation**, as implemented in the author's\n", 16 | "[`IntervalConstraintProgramming.jl`](https://github.com/dpsanders/IntervalConstraintProgramming.jl) package.\n", 17 | "\n", 18 | "This is less complicated than it sounds. The idea is to find a way of taking a declarative **constraint**, like `x^2 + y^2 <= 1`, representing a set `S`, and **turning it into actual code** that operates on interval variables `x` and `y` representing a box `B` in the plane, and *contracts* it (squashes it down) to a smaller subbox `B'` that still contains the part of `B` that is inside `S`.\n", 19 | "\n", 20 | "The method takes the AST (Abstract Syntax Tree) of the expression, and unfolds it in two directions: first \"forward\", from the leaves of the tree towards the root, and then backwards, incorporating the constraints. We will present a simplified version of the code at https://github.com/dpsanders/IntervalConstraintProgramming.jl/blob/master/src/contractor.jl" 21 | ] 22 | }, 23 | { 24 | "cell_type": "markdown", 25 | "metadata": {}, 26 | "source": [ 27 | "Let's take the example of the expression" 28 | ] 29 | }, 30 | { 31 | "cell_type": "code", 32 | "execution_count": 1, 33 | "metadata": { 34 | "collapsed": false 35 | }, 36 | "outputs": [ 37 | { 38 | "data": { 39 | "text/plain": [ 40 | ":(x ^ 2 + y ^ 2)" 41 | ] 42 | }, 43 | "execution_count": 1, 44 | "metadata": {}, 45 | "output_type": "execute_result" 46 | } 47 | ], 48 | "source": [ 49 | "ex = :(x^2 + y^2)" 50 | ] 51 | }, 52 | { 53 | "cell_type": "markdown", 54 | "metadata": {}, 55 | "source": [ 56 | "We would like to introduce new variables at each node in the tree, from the \"bottom\" (i.e. the leaves) up, giving the result\n", 57 | "\n", 58 | " z1 = x^2\n", 59 | " z2 = y^2\n", 60 | " z3 = z1 + z2\n", 61 | " \n", 62 | "These variables will be used later in the backwards pass to pass back information about the constraint that `z3` must be in the interval `[0,1]`." 63 | ] 64 | }, 65 | { 66 | "cell_type": "markdown", 67 | "metadata": {}, 68 | "source": [ 69 | "When we define an expression, Julia parses it into its AST:" 70 | ] 71 | }, 72 | { 73 | "cell_type": "code", 74 | "execution_count": 2, 75 | "metadata": { 76 | "collapsed": false 77 | }, 78 | "outputs": [ 79 | { 80 | "name": "stdout", 81 | "output_type": "stream", 82 | "text": [ 83 | "(:call, :+, (:call, :^, :x, 2), (:call, :^, :y, 2))" 84 | ] 85 | } 86 | ], 87 | "source": [ 88 | "Meta.show_sexpr(ex)" 89 | ] 90 | }, 91 | { 92 | "cell_type": "markdown", 93 | "metadata": {}, 94 | "source": [ 95 | "## Forward pass" 96 | ] 97 | }, 98 | { 99 | "cell_type": "code", 100 | "execution_count": 3, 101 | "metadata": { 102 | "collapsed": false 103 | }, 104 | "outputs": [], 105 | "source": [ 106 | "workspace()" 107 | ] 108 | }, 109 | { 110 | "cell_type": "markdown", 111 | "metadata": {}, 112 | "source": [ 113 | "We first write a function to generate new unique symbols. (We could use `gensym()` from `Base`, but the result is unreadable for humans):" 114 | ] 115 | }, 116 | { 117 | "cell_type": "code", 118 | "execution_count": 5, 119 | "metadata": { 120 | "collapsed": false 121 | }, 122 | "outputs": [ 123 | { 124 | "name": "stderr", 125 | "output_type": "stream", 126 | "text": [ 127 | "WARNING: redefining constant _variable_number_\n" 128 | ] 129 | }, 130 | { 131 | "data": { 132 | "text/plain": [ 133 | "makesymbol (generic function with 1 method)" 134 | ] 135 | }, 136 | "execution_count": 5, 137 | "metadata": {}, 138 | "output_type": "execute_result" 139 | } 140 | ], 141 | "source": [ 142 | "const _variable_number_ = [0]\n", 143 | "\n", 144 | "makesymbol() = symbol(\"_z_\", _variable_number_[1]+=1)" 145 | ] 146 | }, 147 | { 148 | "cell_type": "code", 149 | "execution_count": 6, 150 | "metadata": { 151 | "collapsed": false 152 | }, 153 | "outputs": [ 154 | { 155 | "data": { 156 | "text/plain": [ 157 | ":_z_1" 158 | ] 159 | }, 160 | "execution_count": 6, 161 | "metadata": {}, 162 | "output_type": "execute_result" 163 | } 164 | ], 165 | "source": [ 166 | "makesymbol()" 167 | ] 168 | }, 169 | { 170 | "cell_type": "code", 171 | "execution_count": 7, 172 | "metadata": { 173 | "collapsed": false 174 | }, 175 | "outputs": [ 176 | { 177 | "data": { 178 | "text/plain": [ 179 | ":_z_2" 180 | ] 181 | }, 182 | "execution_count": 7, 183 | "metadata": {}, 184 | "output_type": "execute_result" 185 | } 186 | ], 187 | "source": [ 188 | "makesymbol()" 189 | ] 190 | }, 191 | { 192 | "cell_type": "markdown", 193 | "metadata": {}, 194 | "source": [ 195 | "We will make a recursive function that takes a sub-expression and returns two things: (i) the new variable at the top of that subexpression; and (ii) the code that has been generated so far." 196 | ] 197 | }, 198 | { 199 | "cell_type": "code", 200 | "execution_count": 8, 201 | "metadata": { 202 | "collapsed": false 203 | }, 204 | "outputs": [ 205 | { 206 | "data": { 207 | "text/plain": [ 208 | "unfold (generic function with 2 methods)" 209 | ] 210 | }, 211 | "execution_count": 8, 212 | "metadata": {}, 213 | "output_type": "execute_result" 214 | } 215 | ], 216 | "source": [ 217 | "unfold(ex) = (ex, quote end) # catch-all for symbols and constants\n", 218 | "\n", 219 | "\n", 220 | "function unfold(ex::Expr)\n", 221 | "\n", 222 | " ex.head != :call && throw(ArgumentError(\"Unknown expression type: $ex\"))\n", 223 | " \n", 224 | " new_code = quote end # empty expression\n", 225 | "\n", 226 | " op = ex.args[1]\n", 227 | " \n", 228 | " # do child nodes *first*:\n", 229 | " var1, code1 = unfold(ex.args[2])\n", 230 | " var2, code2 = unfold(ex.args[3])\n", 231 | " \n", 232 | " new_var = makesymbol()\n", 233 | " new_line = :($new_var = $op($var1, $var2)) \n", 234 | " \n", 235 | " append!(new_code.args, code1.args)\n", 236 | " append!(new_code.args, code2.args)\n", 237 | " push!(new_code.args, new_line)\n", 238 | " \n", 239 | " return new_var, new_code\n", 240 | "\n", 241 | "end\n" 242 | ] 243 | }, 244 | { 245 | "cell_type": "code", 246 | "execution_count": null, 247 | "metadata": { 248 | "collapsed": true 249 | }, 250 | "outputs": [], 251 | "source": [] 252 | }, 253 | { 254 | "cell_type": "markdown", 255 | "metadata": {}, 256 | "source": [ 257 | "## Backward pass" 258 | ] 259 | }, 260 | { 261 | "cell_type": "markdown", 262 | "metadata": {}, 263 | "source": [ 264 | "The top-level variable must now be constrained:" 265 | ] 266 | }, 267 | { 268 | "cell_type": "code", 269 | "execution_count": 9, 270 | "metadata": { 271 | "collapsed": false 272 | }, 273 | "outputs": [ 274 | { 275 | "data": { 276 | "text/plain": [ 277 | "(:_z_5,quote \n", 278 | " _z_3 = x ^ 2\n", 279 | " _z_4 = y ^ 2\n", 280 | " _z_5 = _z_3 + _z_4\n", 281 | "end)" 282 | ] 283 | }, 284 | "execution_count": 9, 285 | "metadata": {}, 286 | "output_type": "execute_result" 287 | } 288 | ], 289 | "source": [ 290 | "ex = :(x^2 + y^2)\n", 291 | "var, code = unfold(ex)" 292 | ] 293 | }, 294 | { 295 | "cell_type": "code", 296 | "execution_count": 11, 297 | "metadata": { 298 | "collapsed": false 299 | }, 300 | "outputs": [ 301 | { 302 | "data": { 303 | "text/plain": [ 304 | "intersect (generic function with 15 methods)" 305 | ] 306 | }, 307 | "execution_count": 11, 308 | "metadata": {}, 309 | "output_type": "execute_result" 310 | } 311 | ], 312 | "source": [ 313 | "∩" 314 | ] 315 | }, 316 | { 317 | "cell_type": "code", 318 | "execution_count": 12, 319 | "metadata": { 320 | "collapsed": false 321 | }, 322 | "outputs": [ 323 | { 324 | "data": { 325 | "text/plain": [ 326 | "5-element Array{Any,1}:\n", 327 | " :(_z_3 = x ^ 2) \n", 328 | " :(_z_4 = y ^ 2) \n", 329 | " :(_z_5 = _z_3 + _z_4) \n", 330 | " :(_z_5 = _z_5 ∩ Interval(0,1))\n", 331 | " :(_z_5 = _z_5 ∩ Interval(0,1))" 332 | ] 333 | }, 334 | "execution_count": 12, 335 | "metadata": {}, 336 | "output_type": "execute_result" 337 | } 338 | ], 339 | "source": [ 340 | "interval = :(Interval(0,1))\n", 341 | "\n", 342 | "constraint_code = :($var = $var ∩ $interval)\n", 343 | "push!(code.args, constraint_code)" 344 | ] 345 | }, 346 | { 347 | "cell_type": "markdown", 348 | "metadata": {}, 349 | "source": [ 350 | "Now we wish to go backwards through the generated code, and use reverse-mode functions to propagate the constraints.\n", 351 | "For example, if `z5 = z3 + z4`, then `z3 = z5 - z4`, so we can *constrain* `z3` accordingly by\n", 352 | "\n", 353 | " z3 = z3 ∩ (z5 - z4)\n", 354 | " \n", 355 | "Here, ∩ is an operation defined on intervals in the [`ValidatedNumerics.jl package`](https://github.com/dpsanders/ValidatedNumerics.jl)" 356 | ] 357 | }, 358 | { 359 | "cell_type": "markdown", 360 | "metadata": {}, 361 | "source": [ 362 | "We will assume that there are so-called \"reverse mode\" functions defined that do these operations; see https://github.com/dpsanders/IntervalConstraintProgramming.jl/blob/master/src/reverse_mode.jl\n", 363 | "\n", 364 | "We will then replace `z5 = z3 + z4` by `(z5,z3,z4) = reverse_add(z5, z3, z4)` with the following function" 365 | ] 366 | }, 367 | { 368 | "cell_type": "markdown", 369 | "metadata": {}, 370 | "source": [ 371 | "To find out how to do this, we do" 372 | ] 373 | }, 374 | { 375 | "cell_type": "code", 376 | "execution_count": null, 377 | "metadata": { 378 | "collapsed": false 379 | }, 380 | "outputs": [], 381 | "source": [ 382 | "ex = :(z = x + y)\n", 383 | "dump(ex)" 384 | ] 385 | }, 386 | { 387 | "cell_type": "markdown", 388 | "metadata": {}, 389 | "source": [ 390 | "This tells us which elements to use:" 391 | ] 392 | }, 393 | { 394 | "cell_type": "code", 395 | "execution_count": null, 396 | "metadata": { 397 | "collapsed": false 398 | }, 399 | "outputs": [], 400 | "source": [ 401 | "var1 = ex.args[1]\n", 402 | "op = ex.args[2].args[1]\n", 403 | "var2, var3 = ex.args[2].args[2:3]" 404 | ] 405 | }, 406 | { 407 | "cell_type": "code", 408 | "execution_count": 15, 409 | "metadata": { 410 | "collapsed": false 411 | }, 412 | "outputs": [ 413 | { 414 | "data": { 415 | "text/plain": [ 416 | "Dict{Symbol,Symbol} with 2 entries:\n", 417 | " :+ => :reverse_add\n", 418 | " :- => :reverse_sub" 419 | ] 420 | }, 421 | "execution_count": 15, 422 | "metadata": {}, 423 | "output_type": "execute_result" 424 | } 425 | ], 426 | "source": [ 427 | "const reverse_ops = Dict(:+ => :reverse_add,\n", 428 | " :- => :reverse_sub)" 429 | ] 430 | }, 431 | { 432 | "cell_type": "code", 433 | "execution_count": 16, 434 | "metadata": { 435 | "collapsed": false 436 | }, 437 | "outputs": [ 438 | { 439 | "data": { 440 | "text/plain": [ 441 | "make_reverse (generic function with 1 method)" 442 | ] 443 | }, 444 | "execution_count": 16, 445 | "metadata": {}, 446 | "output_type": "execute_result" 447 | } 448 | ], 449 | "source": [ 450 | "function make_reverse(ex)\n", 451 | " # a = b op c\n", 452 | " \n", 453 | " var1 = ex.args[1]\n", 454 | " op = ex.args[2].args[1]\n", 455 | " var2, var3 = ex.args[2].args[2:3]\n", 456 | " \n", 457 | " reverse_op = reverse_ops[op]\n", 458 | " \n", 459 | " return :( ($var1, $var2, $var3) = $reverse_op($var1, $var2, $var3) )\n", 460 | " \n", 461 | "end" 462 | ] 463 | }, 464 | { 465 | "cell_type": "code", 466 | "execution_count": 18, 467 | "metadata": { 468 | "collapsed": false 469 | }, 470 | "outputs": [ 471 | { 472 | "data": { 473 | "text/plain": [ 474 | ":(z = x + y)" 475 | ] 476 | }, 477 | "execution_count": 18, 478 | "metadata": {}, 479 | "output_type": "execute_result" 480 | } 481 | ], 482 | "source": [ 483 | "ex = :(z = x+y)" 484 | ] 485 | }, 486 | { 487 | "cell_type": "code", 488 | "execution_count": 19, 489 | "metadata": { 490 | "collapsed": false 491 | }, 492 | "outputs": [ 493 | { 494 | "data": { 495 | "text/plain": [ 496 | ":((z,x,y) = reverse_add(z,x,y))" 497 | ] 498 | }, 499 | "execution_count": 19, 500 | "metadata": {}, 501 | "output_type": "execute_result" 502 | } 503 | ], 504 | "source": [ 505 | "make_reverse(ex)" 506 | ] 507 | }, 508 | { 509 | "cell_type": "markdown", 510 | "metadata": {}, 511 | "source": [ 512 | "## MacroTools.jl \n" 513 | ] 514 | }, 515 | { 516 | "cell_type": "markdown", 517 | "metadata": {}, 518 | "source": [ 519 | "The extraction of the variables was pretty ugly. There is a solution to this: the `MacroTools.jl` package." 520 | ] 521 | }, 522 | { 523 | "cell_type": "code", 524 | "execution_count": 21, 525 | "metadata": { 526 | "collapsed": true 527 | }, 528 | "outputs": [], 529 | "source": [ 530 | "using MacroTools" 531 | ] 532 | }, 533 | { 534 | "cell_type": "code", 535 | "execution_count": 22, 536 | "metadata": { 537 | "collapsed": false 538 | }, 539 | "outputs": [ 540 | { 541 | "data": { 542 | "text/plain": [ 543 | ":(z = x + y)" 544 | ] 545 | }, 546 | "execution_count": 22, 547 | "metadata": {}, 548 | "output_type": "execute_result" 549 | } 550 | ], 551 | "source": [ 552 | "ex" 553 | ] 554 | }, 555 | { 556 | "cell_type": "code", 557 | "execution_count": 23, 558 | "metadata": { 559 | "collapsed": false 560 | }, 561 | "outputs": [ 562 | { 563 | "data": { 564 | "text/plain": [ 565 | "(:+,:z,:x,:y)" 566 | ] 567 | }, 568 | "execution_count": 23, 569 | "metadata": {}, 570 | "output_type": "execute_result" 571 | } 572 | ], 573 | "source": [ 574 | "(op, var1, var2, var3) = @match ex begin\n", 575 | " (var1_ = op_(var2_, var3_)) => (op, var1, var2, var3)\n", 576 | "end" 577 | ] 578 | }, 579 | { 580 | "cell_type": "markdown", 581 | "metadata": {}, 582 | "source": [ 583 | "**Exercise**: Finish writing the backwards pass." 584 | ] 585 | } 586 | ], 587 | "metadata": { 588 | "kernelspec": { 589 | "display_name": "Julia 0.4.6", 590 | "language": "julia", 591 | "name": "julia-0.4" 592 | }, 593 | "language_info": { 594 | "file_extension": ".jl", 595 | "mimetype": "application/julia", 596 | "name": "julia", 597 | "version": "0.4.6" 598 | }, 599 | "widgets": { 600 | "state": {}, 601 | "version": "1.1.2" 602 | } 603 | }, 604 | "nbformat": 4, 605 | "nbformat_minor": 0 606 | } 607 | -------------------------------------------------------------------------------- /live_versions/3. Case study of metaprogramming.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Case study of metaprogramming: interval constraint propagation " 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "Perhaps the best way to learn metaprogramming is through an example. \n", 15 | "Let's look at **interval constraint propagation**, as implemented in the author's\n", 16 | "[`IntervalConstraintProgramming.jl`](https://github.com/dpsanders/IntervalConstraintProgramming.jl) package.\n", 17 | "\n", 18 | "This is less complicated than it sounds. The idea is to find a way of taking a declarative **constraint**, like `x^2 + y^2 <= 1`, representing a set `S`, and **turning it into actual code** that operates on interval variables `x` and `y` representing a box `B` in the plane, and *contracts* it (squashes it down) to a smaller subbox `B'` that still contains the part of `B` that is inside `S`.\n", 19 | "\n", 20 | "The method takes the AST (Abstract Syntax Tree) of the expression, and unfolds it in two directions: first \"forward\", from the leaves of the tree towards the root, and then backwards, incorporating the constraints. We will present a simplified version of the code at https://github.com/dpsanders/IntervalConstraintProgramming.jl/blob/master/src/contractor.jl" 21 | ] 22 | }, 23 | { 24 | "cell_type": "markdown", 25 | "metadata": {}, 26 | "source": [ 27 | "Let's take the example of the expression" 28 | ] 29 | }, 30 | { 31 | "cell_type": "code", 32 | "execution_count": 1, 33 | "metadata": { 34 | "collapsed": false 35 | }, 36 | "outputs": [ 37 | { 38 | "data": { 39 | "text/plain": [ 40 | ":(x ^ 2 + y ^ 2)" 41 | ] 42 | }, 43 | "execution_count": 1, 44 | "metadata": {}, 45 | "output_type": "execute_result" 46 | } 47 | ], 48 | "source": [ 49 | "ex = :(x^2 + y^2)" 50 | ] 51 | }, 52 | { 53 | "cell_type": "markdown", 54 | "metadata": {}, 55 | "source": [ 56 | "We would like to introduce new variables at each node in the tree, from the \"bottom\" (i.e. the leaves) up, giving the result\n", 57 | "\n", 58 | " z1 = x^2\n", 59 | " z2 = y^2\n", 60 | " z3 = z1 + z2\n", 61 | " \n", 62 | "These variables will be used later in the backwards pass to pass back information about the constraint that `z3` must be in the interval `[0,1]`." 63 | ] 64 | }, 65 | { 66 | "cell_type": "markdown", 67 | "metadata": {}, 68 | "source": [ 69 | "When we define an expression, Julia parses it into its AST:" 70 | ] 71 | }, 72 | { 73 | "cell_type": "code", 74 | "execution_count": 2, 75 | "metadata": { 76 | "collapsed": false 77 | }, 78 | "outputs": [ 79 | { 80 | "name": "stdout", 81 | "output_type": "stream", 82 | "text": [ 83 | "(:call, :+, (:call, :^, :x, 2), (:call, :^, :y, 2))" 84 | ] 85 | } 86 | ], 87 | "source": [ 88 | "Meta.show_sexpr(ex)" 89 | ] 90 | }, 91 | { 92 | "cell_type": "markdown", 93 | "metadata": {}, 94 | "source": [ 95 | "## Forward pass" 96 | ] 97 | }, 98 | { 99 | "cell_type": "code", 100 | "execution_count": 3, 101 | "metadata": { 102 | "collapsed": false 103 | }, 104 | "outputs": [], 105 | "source": [ 106 | "workspace()" 107 | ] 108 | }, 109 | { 110 | "cell_type": "markdown", 111 | "metadata": {}, 112 | "source": [ 113 | "We first write a function to generate new unique symbols. (We could use `gensym()` from `Base`, but the result is unreadable for humans):" 114 | ] 115 | }, 116 | { 117 | "cell_type": "code", 118 | "execution_count": 5, 119 | "metadata": { 120 | "collapsed": false 121 | }, 122 | "outputs": [ 123 | { 124 | "name": "stderr", 125 | "output_type": "stream", 126 | "text": [ 127 | "WARNING: redefining constant _variable_number_\n" 128 | ] 129 | }, 130 | { 131 | "data": { 132 | "text/plain": [ 133 | "makesymbol (generic function with 1 method)" 134 | ] 135 | }, 136 | "execution_count": 5, 137 | "metadata": {}, 138 | "output_type": "execute_result" 139 | } 140 | ], 141 | "source": [ 142 | "const _variable_number_ = [0]\n", 143 | "\n", 144 | "makesymbol() = symbol(\"_z_\", _variable_number_[1]+=1)" 145 | ] 146 | }, 147 | { 148 | "cell_type": "code", 149 | "execution_count": 6, 150 | "metadata": { 151 | "collapsed": false 152 | }, 153 | "outputs": [ 154 | { 155 | "data": { 156 | "text/plain": [ 157 | ":_z_1" 158 | ] 159 | }, 160 | "execution_count": 6, 161 | "metadata": {}, 162 | "output_type": "execute_result" 163 | } 164 | ], 165 | "source": [ 166 | "makesymbol()" 167 | ] 168 | }, 169 | { 170 | "cell_type": "code", 171 | "execution_count": 7, 172 | "metadata": { 173 | "collapsed": false 174 | }, 175 | "outputs": [ 176 | { 177 | "data": { 178 | "text/plain": [ 179 | ":_z_2" 180 | ] 181 | }, 182 | "execution_count": 7, 183 | "metadata": {}, 184 | "output_type": "execute_result" 185 | } 186 | ], 187 | "source": [ 188 | "makesymbol()" 189 | ] 190 | }, 191 | { 192 | "cell_type": "markdown", 193 | "metadata": {}, 194 | "source": [ 195 | "We will make a recursive function that takes a sub-expression and returns two things: (i) the new variable at the top of that subexpression; and (ii) the code that has been generated so far." 196 | ] 197 | }, 198 | { 199 | "cell_type": "code", 200 | "execution_count": 8, 201 | "metadata": { 202 | "collapsed": false 203 | }, 204 | "outputs": [ 205 | { 206 | "data": { 207 | "text/plain": [ 208 | "unfold (generic function with 2 methods)" 209 | ] 210 | }, 211 | "execution_count": 8, 212 | "metadata": {}, 213 | "output_type": "execute_result" 214 | } 215 | ], 216 | "source": [ 217 | "unfold(ex) = (ex, quote end) # catch-all for symbols and constants\n", 218 | "\n", 219 | "\n", 220 | "function unfold(ex::Expr)\n", 221 | "\n", 222 | " ex.head != :call && throw(ArgumentError(\"Unknown expression type: $ex\"))\n", 223 | " \n", 224 | " new_code = quote end # empty expression\n", 225 | "\n", 226 | " op = ex.args[1]\n", 227 | " \n", 228 | " # do child nodes *first*:\n", 229 | " var1, code1 = unfold(ex.args[2])\n", 230 | " var2, code2 = unfold(ex.args[3])\n", 231 | " \n", 232 | " new_var = makesymbol()\n", 233 | " new_line = :($new_var = $op($var1, $var2)) \n", 234 | " \n", 235 | " append!(new_code.args, code1.args)\n", 236 | " append!(new_code.args, code2.args)\n", 237 | " push!(new_code.args, new_line)\n", 238 | " \n", 239 | " return new_var, new_code\n", 240 | "\n", 241 | "end\n" 242 | ] 243 | }, 244 | { 245 | "cell_type": "code", 246 | "execution_count": null, 247 | "metadata": { 248 | "collapsed": true 249 | }, 250 | "outputs": [], 251 | "source": [] 252 | }, 253 | { 254 | "cell_type": "markdown", 255 | "metadata": {}, 256 | "source": [ 257 | "## Backward pass" 258 | ] 259 | }, 260 | { 261 | "cell_type": "markdown", 262 | "metadata": {}, 263 | "source": [ 264 | "The top-level variable must now be constrained:" 265 | ] 266 | }, 267 | { 268 | "cell_type": "code", 269 | "execution_count": 9, 270 | "metadata": { 271 | "collapsed": false 272 | }, 273 | "outputs": [ 274 | { 275 | "data": { 276 | "text/plain": [ 277 | "(:_z_5,quote \n", 278 | " _z_3 = x ^ 2\n", 279 | " _z_4 = y ^ 2\n", 280 | " _z_5 = _z_3 + _z_4\n", 281 | "end)" 282 | ] 283 | }, 284 | "execution_count": 9, 285 | "metadata": {}, 286 | "output_type": "execute_result" 287 | } 288 | ], 289 | "source": [ 290 | "ex = :(x^2 + y^2)\n", 291 | "var, code = unfold(ex)" 292 | ] 293 | }, 294 | { 295 | "cell_type": "code", 296 | "execution_count": 11, 297 | "metadata": { 298 | "collapsed": false 299 | }, 300 | "outputs": [ 301 | { 302 | "data": { 303 | "text/plain": [ 304 | "intersect (generic function with 15 methods)" 305 | ] 306 | }, 307 | "execution_count": 11, 308 | "metadata": {}, 309 | "output_type": "execute_result" 310 | } 311 | ], 312 | "source": [ 313 | "∩" 314 | ] 315 | }, 316 | { 317 | "cell_type": "code", 318 | "execution_count": 12, 319 | "metadata": { 320 | "collapsed": false 321 | }, 322 | "outputs": [ 323 | { 324 | "data": { 325 | "text/plain": [ 326 | "5-element Array{Any,1}:\n", 327 | " :(_z_3 = x ^ 2) \n", 328 | " :(_z_4 = y ^ 2) \n", 329 | " :(_z_5 = _z_3 + _z_4) \n", 330 | " :(_z_5 = _z_5 ∩ Interval(0,1))\n", 331 | " :(_z_5 = _z_5 ∩ Interval(0,1))" 332 | ] 333 | }, 334 | "execution_count": 12, 335 | "metadata": {}, 336 | "output_type": "execute_result" 337 | } 338 | ], 339 | "source": [ 340 | "interval = :(Interval(0,1))\n", 341 | "\n", 342 | "constraint_code = :($var = $var ∩ $interval)\n", 343 | "push!(code.args, constraint_code)" 344 | ] 345 | }, 346 | { 347 | "cell_type": "markdown", 348 | "metadata": {}, 349 | "source": [ 350 | "Now we wish to go backwards through the generated code, and use reverse-mode functions to propagate the constraints.\n", 351 | "For example, if `z5 = z3 + z4`, then `z3 = z5 - z4`, so we can *constrain* `z3` accordingly by\n", 352 | "\n", 353 | " z3 = z3 ∩ (z5 - z4)\n", 354 | " \n", 355 | "Here, ∩ is an operation defined on intervals in the [`ValidatedNumerics.jl package`](https://github.com/dpsanders/ValidatedNumerics.jl)" 356 | ] 357 | }, 358 | { 359 | "cell_type": "markdown", 360 | "metadata": {}, 361 | "source": [ 362 | "We will assume that there are so-called \"reverse mode\" functions defined that do these operations; see https://github.com/dpsanders/IntervalConstraintProgramming.jl/blob/master/src/reverse_mode.jl\n", 363 | "\n", 364 | "We will then replace `z5 = z3 + z4` by `(z5,z3,z4) = reverse_add(z5, z3, z4)` with the following function" 365 | ] 366 | }, 367 | { 368 | "cell_type": "markdown", 369 | "metadata": {}, 370 | "source": [ 371 | "To find out how to do this, we do" 372 | ] 373 | }, 374 | { 375 | "cell_type": "code", 376 | "execution_count": null, 377 | "metadata": { 378 | "collapsed": false 379 | }, 380 | "outputs": [], 381 | "source": [ 382 | "ex = :(z = x + y)\n", 383 | "dump(ex)" 384 | ] 385 | }, 386 | { 387 | "cell_type": "markdown", 388 | "metadata": {}, 389 | "source": [ 390 | "This tells us which elements to use:" 391 | ] 392 | }, 393 | { 394 | "cell_type": "code", 395 | "execution_count": null, 396 | "metadata": { 397 | "collapsed": false 398 | }, 399 | "outputs": [], 400 | "source": [ 401 | "var1 = ex.args[1]\n", 402 | "op = ex.args[2].args[1]\n", 403 | "var2, var3 = ex.args[2].args[2:3]" 404 | ] 405 | }, 406 | { 407 | "cell_type": "code", 408 | "execution_count": 15, 409 | "metadata": { 410 | "collapsed": false 411 | }, 412 | "outputs": [ 413 | { 414 | "data": { 415 | "text/plain": [ 416 | "Dict{Symbol,Symbol} with 2 entries:\n", 417 | " :+ => :reverse_add\n", 418 | " :- => :reverse_sub" 419 | ] 420 | }, 421 | "execution_count": 15, 422 | "metadata": {}, 423 | "output_type": "execute_result" 424 | } 425 | ], 426 | "source": [ 427 | "const reverse_ops = Dict(:+ => :reverse_add,\n", 428 | " :- => :reverse_sub)" 429 | ] 430 | }, 431 | { 432 | "cell_type": "code", 433 | "execution_count": 16, 434 | "metadata": { 435 | "collapsed": false 436 | }, 437 | "outputs": [ 438 | { 439 | "data": { 440 | "text/plain": [ 441 | "make_reverse (generic function with 1 method)" 442 | ] 443 | }, 444 | "execution_count": 16, 445 | "metadata": {}, 446 | "output_type": "execute_result" 447 | } 448 | ], 449 | "source": [ 450 | "function make_reverse(ex)\n", 451 | " # a = b op c\n", 452 | " \n", 453 | " var1 = ex.args[1]\n", 454 | " op = ex.args[2].args[1]\n", 455 | " var2, var3 = ex.args[2].args[2:3]\n", 456 | " \n", 457 | " reverse_op = reverse_ops[op]\n", 458 | " \n", 459 | " return :( ($var1, $var2, $var3) = $reverse_op($var1, $var2, $var3) )\n", 460 | " \n", 461 | "end" 462 | ] 463 | }, 464 | { 465 | "cell_type": "code", 466 | "execution_count": 18, 467 | "metadata": { 468 | "collapsed": false 469 | }, 470 | "outputs": [ 471 | { 472 | "data": { 473 | "text/plain": [ 474 | ":(z = x + y)" 475 | ] 476 | }, 477 | "execution_count": 18, 478 | "metadata": {}, 479 | "output_type": "execute_result" 480 | } 481 | ], 482 | "source": [ 483 | "ex = :(z = x+y)" 484 | ] 485 | }, 486 | { 487 | "cell_type": "code", 488 | "execution_count": 19, 489 | "metadata": { 490 | "collapsed": false 491 | }, 492 | "outputs": [ 493 | { 494 | "data": { 495 | "text/plain": [ 496 | ":((z,x,y) = reverse_add(z,x,y))" 497 | ] 498 | }, 499 | "execution_count": 19, 500 | "metadata": {}, 501 | "output_type": "execute_result" 502 | } 503 | ], 504 | "source": [ 505 | "make_reverse(ex)" 506 | ] 507 | }, 508 | { 509 | "cell_type": "markdown", 510 | "metadata": {}, 511 | "source": [ 512 | "## MacroTools.jl \n" 513 | ] 514 | }, 515 | { 516 | "cell_type": "markdown", 517 | "metadata": {}, 518 | "source": [ 519 | "The extraction of the variables was pretty ugly. There is a solution to this: the `MacroTools.jl` package." 520 | ] 521 | }, 522 | { 523 | "cell_type": "code", 524 | "execution_count": 21, 525 | "metadata": { 526 | "collapsed": true 527 | }, 528 | "outputs": [], 529 | "source": [ 530 | "using MacroTools" 531 | ] 532 | }, 533 | { 534 | "cell_type": "code", 535 | "execution_count": 22, 536 | "metadata": { 537 | "collapsed": false 538 | }, 539 | "outputs": [ 540 | { 541 | "data": { 542 | "text/plain": [ 543 | ":(z = x + y)" 544 | ] 545 | }, 546 | "execution_count": 22, 547 | "metadata": {}, 548 | "output_type": "execute_result" 549 | } 550 | ], 551 | "source": [ 552 | "ex" 553 | ] 554 | }, 555 | { 556 | "cell_type": "code", 557 | "execution_count": 23, 558 | "metadata": { 559 | "collapsed": false 560 | }, 561 | "outputs": [ 562 | { 563 | "data": { 564 | "text/plain": [ 565 | "(:+,:z,:x,:y)" 566 | ] 567 | }, 568 | "execution_count": 23, 569 | "metadata": {}, 570 | "output_type": "execute_result" 571 | } 572 | ], 573 | "source": [ 574 | "(op, var1, var2, var3) = @match ex begin\n", 575 | " (var1_ = op_(var2_, var3_)) => (op, var1, var2, var3)\n", 576 | "end" 577 | ] 578 | }, 579 | { 580 | "cell_type": "markdown", 581 | "metadata": {}, 582 | "source": [ 583 | "**Exercise**: Finish writing the backwards pass." 584 | ] 585 | } 586 | ], 587 | "metadata": { 588 | "kernelspec": { 589 | "display_name": "Julia 0.4.6", 590 | "language": "julia", 591 | "name": "julia-0.4" 592 | }, 593 | "language_info": { 594 | "file_extension": ".jl", 595 | "mimetype": "application/julia", 596 | "name": "julia", 597 | "version": "0.4.6" 598 | }, 599 | "widgets": { 600 | "state": {}, 601 | "version": "1.1.2" 602 | } 603 | }, 604 | "nbformat": 4, 605 | "nbformat_minor": 0 606 | } 607 | -------------------------------------------------------------------------------- /2. Metaprogramming.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Introduction to metaprogramming: \"Code that creates code\" " 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "Julia has strong **metaprogramming** capabilities. What does this mean?\n", 15 | "\n", 16 | "> **meta**: something on a higher level\n", 17 | "\n", 18 | "**metaprogramming** = \"higher-level programming\"\n", 19 | "\n", 20 | "i.e. writing code (a program) to manipulate not data, but code (that itself manipulates data)\n" 21 | ] 22 | }, 23 | { 24 | "cell_type": "markdown", 25 | "metadata": {}, 26 | "source": [ 27 | "## Motivating example\n", 28 | "\n", 29 | "Metaprogramming has many different uses, several of which we will explore. It is often a way to add, or change, the syntax of Julia, to write a \"domain-specific language\" (DSL), that converts a simple syntax (but that is not standard Julia syntax) into true Julia code.\n", 30 | "\n", 31 | "As a motivating example, we might like to be able to write" 32 | ] 33 | }, 34 | { 35 | "cell_type": "code", 36 | "execution_count": 2, 37 | "metadata": { 38 | "collapsed": false 39 | }, 40 | "outputs": [ 41 | { 42 | "ename": "LoadError", 43 | "evalue": "LoadError: UndefVarError: ∑_ not defined\nwhile loading In[2], in expression starting on line 1", 44 | "output_type": "error", 45 | "traceback": [ 46 | "LoadError: UndefVarError: ∑_ not defined\nwhile loading In[2], in expression starting on line 1", 47 | "" 48 | ] 49 | } 50 | ], 51 | "source": [ 52 | "∑_{i ≠ j}, 1/(λ_i - λ_j) # NOT JULIA SYNTAX!" 53 | ] 54 | }, 55 | { 56 | "cell_type": "markdown", 57 | "metadata": {}, 58 | "source": [ 59 | "and have it *automatically converted* into something like" 60 | ] 61 | }, 62 | { 63 | "cell_type": "code", 64 | "execution_count": null, 65 | "metadata": { 66 | "collapsed": false 67 | }, 68 | "outputs": [], 69 | "source": [ 70 | "total = zero(λ[1])\n", 71 | "\n", 72 | "for i in 1:N\n", 73 | " j == i && continue\n", 74 | " \n", 75 | " total += 1 / (λ[i] - λ[j])\n", 76 | "end\n", 77 | "\n", 78 | "total" 79 | ] 80 | }, 81 | { 82 | "cell_type": "markdown", 83 | "metadata": {}, 84 | "source": [ 85 | "## A simpler example " 86 | ] 87 | }, 88 | { 89 | "cell_type": "markdown", 90 | "metadata": {}, 91 | "source": [ 92 | "This is too hard [**exercise**: write this functionality and make it into a package!], let's start out with a simpler example: Wilkinson-type polynomials.\n", 93 | "The [Wilkinson polynomial](https://en.wikipedia.org/wiki/Wilkinson's_polynomial) is\n", 94 | "\n", 95 | "$$p_{20}(x) := (x-1) \\cdot (x-2) \\cdot \\cdots \\cdot (x-20) = \\prod_{i=1}^{20} (x-i).$$\n", 96 | "\n", 97 | "[Polynomials like this are interesting, since the eigenvalues of the associated \"companion matrix\", which are used to find the roots of the polynomial, are very sensitive to perturbations of the coefficients of the polynomial.]\n", 98 | "\n", 99 | "Suppose we wish to define this polynomial in Julia. The simple way would be to write it out explicitly:" 100 | ] 101 | }, 102 | { 103 | "cell_type": "code", 104 | "execution_count": 3, 105 | "metadata": { 106 | "collapsed": false 107 | }, 108 | "outputs": [ 109 | { 110 | "data": { 111 | "text/plain": [ 112 | "p_5 (generic function with 1 method)" 113 | ] 114 | }, 115 | "execution_count": 3, 116 | "metadata": {}, 117 | "output_type": "execute_result" 118 | } 119 | ], 120 | "source": [ 121 | "p_5(x) = (x-1) * (x-2) * (x-3) * (x-4) * (x-5)" 122 | ] 123 | }, 124 | { 125 | "cell_type": "markdown", 126 | "metadata": {}, 127 | "source": [ 128 | "$p_{10}$ is already a pain to type by hand, $p_{20}$ more so, and $p_{100}$ is basically impossible. But this is just a case of repetition, and computers are designed for that. One possible definition uses a `for` loop:" 129 | ] 130 | }, 131 | { 132 | "cell_type": "code", 133 | "execution_count": 4, 134 | "metadata": { 135 | "collapsed": false 136 | }, 137 | "outputs": [ 138 | { 139 | "data": { 140 | "text/plain": [ 141 | "wilkinson (generic function with 1 method)" 142 | ] 143 | }, 144 | "execution_count": 4, 145 | "metadata": {}, 146 | "output_type": "execute_result" 147 | } 148 | ], 149 | "source": [ 150 | "function wilkinson(n, x)\n", 151 | " result = x - 1\n", 152 | " \n", 153 | " for i in 2:n\n", 154 | " result *= (x - i)\n", 155 | " end\n", 156 | " \n", 157 | " result\n", 158 | "end" 159 | ] 160 | }, 161 | { 162 | "cell_type": "markdown", 163 | "metadata": {}, 164 | "source": [ 165 | "We can use an anonymous function to define the function $p_n$:" 166 | ] 167 | }, 168 | { 169 | "cell_type": "code", 170 | "execution_count": 5, 171 | "metadata": { 172 | "collapsed": false 173 | }, 174 | "outputs": [ 175 | { 176 | "data": { 177 | "text/plain": [ 178 | "p (generic function with 1 method)" 179 | ] 180 | }, 181 | "execution_count": 5, 182 | "metadata": {}, 183 | "output_type": "execute_result" 184 | } 185 | ], 186 | "source": [ 187 | "p(n) = x -> wilkinson(n, x)" 188 | ] 189 | }, 190 | { 191 | "cell_type": "code", 192 | "execution_count": 6, 193 | "metadata": { 194 | "collapsed": false 195 | }, 196 | "outputs": [ 197 | { 198 | "data": { 199 | "text/plain": [ 200 | "-0.20790000000000022" 201 | ] 202 | }, 203 | "execution_count": 6, 204 | "metadata": {}, 205 | "output_type": "execute_result" 206 | } 207 | ], 208 | "source": [ 209 | "p(4)(3.1)" 210 | ] 211 | }, 212 | { 213 | "cell_type": "markdown", 214 | "metadata": {}, 215 | "source": [ 216 | "[In Julia 0.4, anonymous functions have a performance penalty, although they no longer do in 0.5.]\n", 217 | "\n", 218 | "It seems, though, that it should be possible to use the original definition of the function to write the equivalent of" 219 | ] 220 | }, 221 | { 222 | "cell_type": "code", 223 | "execution_count": null, 224 | "metadata": { 225 | "collapsed": false 226 | }, 227 | "outputs": [], 228 | "source": [ 229 | "p_5(x) = (x-1)*(x-2)*(x-3)*(x-4)*(x-5)" 230 | ] 231 | }, 232 | { 233 | "cell_type": "markdown", 234 | "metadata": {}, 235 | "source": [ 236 | "for $n=100$.\n", 237 | "In other languages, this would often be accomplished by manipulating strings that represent the \"surface syntax\", i.e. the string of characters that you would actually type, and then evaluate this string. However, in Julia\n", 238 | "**we never use strings for this**, since Julia has a way to **refer to Julia code objects within Julia**." 239 | ] 240 | }, 241 | { 242 | "cell_type": "markdown", 243 | "metadata": {}, 244 | "source": [ 245 | "## Expressions " 246 | ] 247 | }, 248 | { 249 | "cell_type": "markdown", 250 | "metadata": { 251 | "collapsed": false 252 | }, 253 | "source": [ 254 | "Let's take the simplest polynomial, that we write as `(x-1) * (x-2)`. We can view this as a piece of Julia code, called an **expression**, and can tell Julia this as follows:" 255 | ] 256 | }, 257 | { 258 | "cell_type": "code", 259 | "execution_count": 7, 260 | "metadata": { 261 | "collapsed": false 262 | }, 263 | "outputs": [ 264 | { 265 | "data": { 266 | "text/plain": [ 267 | "quote # In[7], line 2:\n", 268 | " (x - 1) * (x - 2)\n", 269 | "end" 270 | ] 271 | }, 272 | "execution_count": 7, 273 | "metadata": {}, 274 | "output_type": "execute_result" 275 | } 276 | ], 277 | "source": [ 278 | "ex = quote \n", 279 | " (x - 1) * (x - 2)\n", 280 | " end" 281 | ] 282 | }, 283 | { 284 | "cell_type": "markdown", 285 | "metadata": {}, 286 | "source": [ 287 | "For small pieces of code like this, an alternative syntax uses the `:( ... )` operator:" 288 | ] 289 | }, 290 | { 291 | "cell_type": "code", 292 | "execution_count": 8, 293 | "metadata": { 294 | "collapsed": false 295 | }, 296 | "outputs": [ 297 | { 298 | "data": { 299 | "text/plain": [ 300 | ":((x - 1) * (x - 2))" 301 | ] 302 | }, 303 | "execution_count": 8, 304 | "metadata": {}, 305 | "output_type": "execute_result" 306 | } 307 | ], 308 | "source": [ 309 | "ex = :( (x-1) * (x-2) )" 310 | ] 311 | }, 312 | { 313 | "cell_type": "markdown", 314 | "metadata": {}, 315 | "source": [ 316 | "What does Julia think this is?" 317 | ] 318 | }, 319 | { 320 | "cell_type": "code", 321 | "execution_count": 9, 322 | "metadata": { 323 | "collapsed": false 324 | }, 325 | "outputs": [ 326 | { 327 | "data": { 328 | "text/plain": [ 329 | "Expr" 330 | ] 331 | }, 332 | "execution_count": 9, 333 | "metadata": {}, 334 | "output_type": "execute_result" 335 | } 336 | ], 337 | "source": [ 338 | "typeof(ex)" 339 | ] 340 | }, 341 | { 342 | "cell_type": "markdown", 343 | "metadata": {}, 344 | "source": [ 345 | "We see that Julia has a type `Expr` that represents an **expression object**, that we can think of as a \"Julia code snippet\"." 346 | ] 347 | }, 348 | { 349 | "cell_type": "markdown", 350 | "metadata": {}, 351 | "source": [ 352 | "We would like to be able to execute this code; we can do so with `eval` (\"evaluate\"):" 353 | ] 354 | }, 355 | { 356 | "cell_type": "code", 357 | "execution_count": 10, 358 | "metadata": { 359 | "collapsed": false 360 | }, 361 | "outputs": [ 362 | { 363 | "data": { 364 | "text/plain": [ 365 | ":((x - 1) * (x - 2))" 366 | ] 367 | }, 368 | "execution_count": 10, 369 | "metadata": {}, 370 | "output_type": "execute_result" 371 | } 372 | ], 373 | "source": [ 374 | "ex" 375 | ] 376 | }, 377 | { 378 | "cell_type": "code", 379 | "execution_count": 11, 380 | "metadata": { 381 | "collapsed": false 382 | }, 383 | "outputs": [ 384 | { 385 | "ename": "LoadError", 386 | "evalue": "LoadError: UndefVarError: x not defined\nwhile loading In[11], in expression starting on line 1", 387 | "output_type": "error", 388 | "traceback": [ 389 | "LoadError: UndefVarError: x not defined\nwhile loading In[11], in expression starting on line 1", 390 | "" 391 | ] 392 | } 393 | ], 394 | "source": [ 395 | "eval(ex)" 396 | ] 397 | }, 398 | { 399 | "cell_type": "markdown", 400 | "metadata": {}, 401 | "source": [ 402 | "We see that `x` is not defined, since Julia is trying to do the same as if we had written the following straight at the prompt" 403 | ] 404 | }, 405 | { 406 | "cell_type": "code", 407 | "execution_count": 12, 408 | "metadata": { 409 | "collapsed": false 410 | }, 411 | "outputs": [ 412 | { 413 | "ename": "LoadError", 414 | "evalue": "LoadError: UndefVarError: x not defined\nwhile loading In[12], in expression starting on line 1", 415 | "output_type": "error", 416 | "traceback": [ 417 | "LoadError: UndefVarError: x not defined\nwhile loading In[12], in expression starting on line 1", 418 | "" 419 | ] 420 | } 421 | ], 422 | "source": [ 423 | "(x-1) * (x-2)" 424 | ] 425 | }, 426 | { 427 | "cell_type": "markdown", 428 | "metadata": {}, 429 | "source": [ 430 | "If we give `x` a value first, then it works:" 431 | ] 432 | }, 433 | { 434 | "cell_type": "code", 435 | "execution_count": 13, 436 | "metadata": { 437 | "collapsed": false 438 | }, 439 | "outputs": [ 440 | { 441 | "data": { 442 | "text/plain": [ 443 | "3.75" 444 | ] 445 | }, 446 | "execution_count": 13, 447 | "metadata": {}, 448 | "output_type": "execute_result" 449 | } 450 | ], 451 | "source": [ 452 | "x = 3.5\n", 453 | "(x-1) * (x-2)" 454 | ] 455 | }, 456 | { 457 | "cell_type": "markdown", 458 | "metadata": {}, 459 | "source": [ 460 | "And so hence does the `eval`:" 461 | ] 462 | }, 463 | { 464 | "cell_type": "code", 465 | "execution_count": 14, 466 | "metadata": { 467 | "collapsed": false 468 | }, 469 | "outputs": [ 470 | { 471 | "data": { 472 | "text/plain": [ 473 | "3.75" 474 | ] 475 | }, 476 | "execution_count": 14, 477 | "metadata": {}, 478 | "output_type": "execute_result" 479 | } 480 | ], 481 | "source": [ 482 | "eval(ex)" 483 | ] 484 | }, 485 | { 486 | "cell_type": "markdown", 487 | "metadata": {}, 488 | "source": [ 489 | "For example, we can try to write a simple formula evaluator:" 490 | ] 491 | }, 492 | { 493 | "cell_type": "code", 494 | "execution_count": 20, 495 | "metadata": { 496 | "collapsed": false 497 | }, 498 | "outputs": [ 499 | { 500 | "data": { 501 | "text/plain": [ 502 | "\"3x^2 + 4x - 3sin(x)\"" 503 | ] 504 | }, 505 | "execution_count": 20, 506 | "metadata": {}, 507 | "output_type": "execute_result" 508 | } 509 | ], 510 | "source": [ 511 | "formula = \"3x^2 + 4x - 3sin(x)\"" 512 | ] 513 | }, 514 | { 515 | "cell_type": "code", 516 | "execution_count": 16, 517 | "metadata": { 518 | "collapsed": false 519 | }, 520 | "outputs": [ 521 | { 522 | "data": { 523 | "text/plain": [ 524 | "\"3x\"" 525 | ] 526 | }, 527 | "execution_count": 16, 528 | "metadata": {}, 529 | "output_type": "execute_result" 530 | } 531 | ], 532 | "source": [ 533 | "eval(formula)" 534 | ] 535 | }, 536 | { 537 | "cell_type": "markdown", 538 | "metadata": {}, 539 | "source": [ 540 | "This does not do what we expect, since we have a string, not a Julia expression:" 541 | ] 542 | }, 543 | { 544 | "cell_type": "code", 545 | "execution_count": 17, 546 | "metadata": { 547 | "collapsed": false 548 | }, 549 | "outputs": [ 550 | { 551 | "data": { 552 | "text/plain": [ 553 | "ASCIIString" 554 | ] 555 | }, 556 | "execution_count": 17, 557 | "metadata": {}, 558 | "output_type": "execute_result" 559 | } 560 | ], 561 | "source": [ 562 | "typeof(formula)" 563 | ] 564 | }, 565 | { 566 | "cell_type": "markdown", 567 | "metadata": {}, 568 | "source": [ 569 | "To convert this string into an expression, we use `parse`:" 570 | ] 571 | }, 572 | { 573 | "cell_type": "code", 574 | "execution_count": 18, 575 | "metadata": { 576 | "collapsed": false 577 | }, 578 | "outputs": [ 579 | { 580 | "data": { 581 | "text/plain": [ 582 | ":(3x)" 583 | ] 584 | }, 585 | "execution_count": 18, 586 | "metadata": {}, 587 | "output_type": "execute_result" 588 | } 589 | ], 590 | "source": [ 591 | "formula2 = parse(formula)" 592 | ] 593 | }, 594 | { 595 | "cell_type": "code", 596 | "execution_count": 19, 597 | "metadata": { 598 | "collapsed": false 599 | }, 600 | "outputs": [ 601 | { 602 | "data": { 603 | "text/plain": [ 604 | "10.5" 605 | ] 606 | }, 607 | "execution_count": 19, 608 | "metadata": {}, 609 | "output_type": "execute_result" 610 | } 611 | ], 612 | "source": [ 613 | "eval(formula2)" 614 | ] 615 | }, 616 | { 617 | "cell_type": "code", 618 | "execution_count": 21, 619 | "metadata": { 620 | "collapsed": false 621 | }, 622 | "outputs": [ 623 | { 624 | "data": { 625 | "text/plain": [ 626 | ":((3 * x ^ 2 + 4x) - 3 * sin(x))" 627 | ] 628 | }, 629 | "execution_count": 21, 630 | "metadata": {}, 631 | "output_type": "execute_result" 632 | } 633 | ], 634 | "source": [ 635 | "ex = parse(\"3x^2 + 4x - 3sin(x)\")" 636 | ] 637 | }, 638 | { 639 | "cell_type": "code", 640 | "execution_count": 22, 641 | "metadata": { 642 | "collapsed": false 643 | }, 644 | "outputs": [ 645 | { 646 | "data": { 647 | "text/plain": [ 648 | "51.80234968306886" 649 | ] 650 | }, 651 | "execution_count": 22, 652 | "metadata": {}, 653 | "output_type": "execute_result" 654 | } 655 | ], 656 | "source": [ 657 | "eval(ex)" 658 | ] 659 | }, 660 | { 661 | "cell_type": "code", 662 | "execution_count": null, 663 | "metadata": { 664 | "collapsed": true 665 | }, 666 | "outputs": [], 667 | "source": [ 668 | "f(x) = 3x^2 + 4x - 3sin(x)" 669 | ] 670 | }, 671 | { 672 | "cell_type": "markdown", 673 | "metadata": {}, 674 | "source": [ 675 | "Note that the really hard work, that of parsing the expression, i.e. converting it into an internal representation in terms of a tree, has already been done for us by Julia. It is thus, happily, usually not necessary for us to write our own parser; we just leverage Julia's own! This is one of many ways in which Julia minimises the work that we need to do, compared to other languages." 676 | ] 677 | }, 678 | { 679 | "cell_type": "markdown", 680 | "metadata": {}, 681 | "source": [ 682 | "## Structure of `Expr`essions " 683 | ] 684 | }, 685 | { 686 | "cell_type": "markdown", 687 | "metadata": {}, 688 | "source": [ 689 | "An expression object of type `Expr` has an internal structure that corresponds to the **abstract syntax tree** (AST) of the expression, i.e. a tree, whose nodes are operations, and whose children are subtrees.\n", 690 | "We can see this in two ways, using `dump`:" 691 | ] 692 | }, 693 | { 694 | "cell_type": "code", 695 | "execution_count": 23, 696 | "metadata": { 697 | "collapsed": false 698 | }, 699 | "outputs": [ 700 | { 701 | "name": "stdout", 702 | "output_type": "stream", 703 | "text": [ 704 | "Expr \n", 705 | " head: Symbol call\n", 706 | " args: Array(Any,(3,))\n", 707 | " 1: Symbol -\n", 708 | " 2: Expr \n", 709 | " head: Symbol call\n", 710 | " args: Array(Any,(3,))\n", 711 | " 1: Symbol +\n", 712 | " 2: Expr \n", 713 | " head: Symbol call\n", 714 | " args: Array(Any,(3,))\n", 715 | " typ: Any\n", 716 | " 3: Expr \n", 717 | " head: Symbol call\n", 718 | " args: Array(Any,(3,))\n", 719 | " typ: Any\n", 720 | " typ: Any\n", 721 | " 3: Expr \n", 722 | " head: Symbol call\n", 723 | " args: Array(Any,(3,))\n", 724 | " 1: Symbol *\n", 725 | " 2: Int64 3\n", 726 | " 3: Expr \n", 727 | " head: Symbol call\n", 728 | " args: Array(Any,(2,))\n", 729 | " typ: Any\n", 730 | " typ: Any\n", 731 | " typ: Any\n" 732 | ] 733 | } 734 | ], 735 | "source": [ 736 | "dump(ex)" 737 | ] 738 | }, 739 | { 740 | "cell_type": "markdown", 741 | "metadata": {}, 742 | "source": [ 743 | "which shows everything [up to a pre-determined depth] in detail, or " 744 | ] 745 | }, 746 | { 747 | "cell_type": "code", 748 | "execution_count": 24, 749 | "metadata": { 750 | "collapsed": false 751 | }, 752 | "outputs": [ 753 | { 754 | "name": "stdout", 755 | "output_type": "stream", 756 | "text": [ 757 | "(:call, :-, (:call, :+, (:call, :*, 3, (:call, :^, :x, 2)), (:call, :*, 4, :x)), (:call, :*, 3, (:call, :sin, :x)))" 758 | ] 759 | } 760 | ], 761 | "source": [ 762 | "Meta.show_sexpr(ex)" 763 | ] 764 | }, 765 | { 766 | "cell_type": "code", 767 | "execution_count": 25, 768 | "metadata": { 769 | "collapsed": false 770 | }, 771 | "outputs": [ 772 | { 773 | "data": { 774 | "text/plain": [ 775 | ":((x - 1) * (x - 2))" 776 | ] 777 | }, 778 | "execution_count": 25, 779 | "metadata": {}, 780 | "output_type": "execute_result" 781 | } 782 | ], 783 | "source": [ 784 | "ex = :((x-1) * (x-2))" 785 | ] 786 | }, 787 | { 788 | "cell_type": "code", 789 | "execution_count": 26, 790 | "metadata": { 791 | "collapsed": false 792 | }, 793 | "outputs": [ 794 | { 795 | "name": "stdout", 796 | "output_type": "stream", 797 | "text": [ 798 | "Expr \n", 799 | " head: Symbol call\n", 800 | " args: Array(Any,(3,))\n", 801 | " 1: Symbol *\n", 802 | " 2: Expr \n", 803 | " head: Symbol call\n", 804 | " args: Array(Any,(3,))\n", 805 | " 1: Symbol -\n", 806 | " 2: Symbol x\n", 807 | " 3: Int64 1\n", 808 | " typ: Any\n", 809 | " 3: Expr \n", 810 | " head: Symbol call\n", 811 | " args: Array(Any,(3,))\n", 812 | " 1: Symbol -\n", 813 | " 2: Symbol x\n", 814 | " 3: Int64 2\n", 815 | " typ: Any\n", 816 | " typ: Any\n" 817 | ] 818 | } 819 | ], 820 | "source": [ 821 | "dump(ex)" 822 | ] 823 | }, 824 | { 825 | "cell_type": "markdown", 826 | "metadata": {}, 827 | "source": [ 828 | "which gives a compact version (similar to an [S-expression](https://en.wikipedia.org/wiki/S-expression) in Lisp, whence the name of the function, which is in the `Meta` submodule in `Base`). They both give representations of the syntax tree describing the hierarchical structure of the expression.\n", 829 | "\n", 830 | "The point is that since an `Expr`ession is a **standard Julia objects**, we can **use standard Julia commands to manipulate it**! \n", 831 | "\n", 832 | "As usual, doing `ex.` gives a list of the fields in the type `Expr`, or we can use `fieldnames(ex)`:" 833 | ] 834 | }, 835 | { 836 | "cell_type": "code", 837 | "execution_count": 27, 838 | "metadata": { 839 | "collapsed": false 840 | }, 841 | "outputs": [ 842 | { 843 | "data": { 844 | "text/plain": [ 845 | "3-element Array{Symbol,1}:\n", 846 | " :head\n", 847 | " :args\n", 848 | " :typ " 849 | ] 850 | }, 851 | "execution_count": 27, 852 | "metadata": {}, 853 | "output_type": "execute_result" 854 | } 855 | ], 856 | "source": [ 857 | "fieldnames(ex)" 858 | ] 859 | }, 860 | { 861 | "cell_type": "markdown", 862 | "metadata": {}, 863 | "source": [ 864 | "and then examine the fields:" 865 | ] 866 | }, 867 | { 868 | "cell_type": "code", 869 | "execution_count": 28, 870 | "metadata": { 871 | "collapsed": false 872 | }, 873 | "outputs": [ 874 | { 875 | "data": { 876 | "text/plain": [ 877 | ":call" 878 | ] 879 | }, 880 | "execution_count": 28, 881 | "metadata": {}, 882 | "output_type": "execute_result" 883 | } 884 | ], 885 | "source": [ 886 | "ex.head" 887 | ] 888 | }, 889 | { 890 | "cell_type": "markdown", 891 | "metadata": {}, 892 | "source": [ 893 | "This tells us that the expression represents a function call, whose arguments are the following:" 894 | ] 895 | }, 896 | { 897 | "cell_type": "code", 898 | "execution_count": 29, 899 | "metadata": { 900 | "collapsed": false 901 | }, 902 | "outputs": [ 903 | { 904 | "data": { 905 | "text/plain": [ 906 | "3-element Array{Any,1}:\n", 907 | " :* \n", 908 | " :(x - 1)\n", 909 | " :(x - 2)" 910 | ] 911 | }, 912 | "execution_count": 29, 913 | "metadata": {}, 914 | "output_type": "execute_result" 915 | } 916 | ], 917 | "source": [ 918 | "ex.args" 919 | ] 920 | }, 921 | { 922 | "cell_type": "markdown", 923 | "metadata": {}, 924 | "source": [ 925 | "[`ex.typ` is not useful for the user.]" 926 | ] 927 | }, 928 | { 929 | "cell_type": "markdown", 930 | "metadata": {}, 931 | "source": [ 932 | "The first element of the array `ex.args` is the function to be called:" 933 | ] 934 | }, 935 | { 936 | "cell_type": "code", 937 | "execution_count": 30, 938 | "metadata": { 939 | "collapsed": false 940 | }, 941 | "outputs": [ 942 | { 943 | "data": { 944 | "text/plain": [ 945 | ":*" 946 | ] 947 | }, 948 | "execution_count": 30, 949 | "metadata": {}, 950 | "output_type": "execute_result" 951 | } 952 | ], 953 | "source": [ 954 | "ex.args[1]" 955 | ] 956 | }, 957 | { 958 | "cell_type": "markdown", 959 | "metadata": {}, 960 | "source": [ 961 | "and the second element is" 962 | ] 963 | }, 964 | { 965 | "cell_type": "code", 966 | "execution_count": 31, 967 | "metadata": { 968 | "collapsed": false 969 | }, 970 | "outputs": [ 971 | { 972 | "data": { 973 | "text/plain": [ 974 | ":(x - 1)" 975 | ] 976 | }, 977 | "execution_count": 31, 978 | "metadata": {}, 979 | "output_type": "execute_result" 980 | } 981 | ], 982 | "source": [ 983 | "ex.args[2]" 984 | ] 985 | }, 986 | { 987 | "cell_type": "markdown", 988 | "metadata": {}, 989 | "source": [ 990 | "that is, it is itself an `Expr`ession:" 991 | ] 992 | }, 993 | { 994 | "cell_type": "code", 995 | "execution_count": 32, 996 | "metadata": { 997 | "collapsed": false 998 | }, 999 | "outputs": [ 1000 | { 1001 | "data": { 1002 | "text/plain": [ 1003 | "Expr" 1004 | ] 1005 | }, 1006 | "execution_count": 32, 1007 | "metadata": {}, 1008 | "output_type": "execute_result" 1009 | } 1010 | ], 1011 | "source": [ 1012 | "typeof(ans)" 1013 | ] 1014 | }, 1015 | { 1016 | "cell_type": "markdown", 1017 | "metadata": {}, 1018 | "source": [ 1019 | "Thus the structure of `Expr`essions is fundamentally recursive, and so lends itself naturally to recursive algorithms." 1020 | ] 1021 | }, 1022 | { 1023 | "cell_type": "markdown", 1024 | "metadata": {}, 1025 | "source": [ 1026 | "We can dive, in turn, into the structure of each element:" 1027 | ] 1028 | }, 1029 | { 1030 | "cell_type": "code", 1031 | "execution_count": 36, 1032 | "metadata": { 1033 | "collapsed": false 1034 | }, 1035 | "outputs": [ 1036 | { 1037 | "data": { 1038 | "text/plain": [ 1039 | ":(=)" 1040 | ] 1041 | }, 1042 | "execution_count": 36, 1043 | "metadata": {}, 1044 | "output_type": "execute_result" 1045 | } 1046 | ], 1047 | "source": [ 1048 | "ex2 = :(a = b + c)\n", 1049 | "ex2.head" 1050 | ] 1051 | }, 1052 | { 1053 | "cell_type": "code", 1054 | "execution_count": 34, 1055 | "metadata": { 1056 | "collapsed": false 1057 | }, 1058 | "outputs": [ 1059 | { 1060 | "data": { 1061 | "text/plain": [ 1062 | ":call" 1063 | ] 1064 | }, 1065 | "execution_count": 34, 1066 | "metadata": {}, 1067 | "output_type": "execute_result" 1068 | } 1069 | ], 1070 | "source": [ 1071 | "ex.head" 1072 | ] 1073 | }, 1074 | { 1075 | "cell_type": "code", 1076 | "execution_count": 37, 1077 | "metadata": { 1078 | "collapsed": false 1079 | }, 1080 | "outputs": [ 1081 | { 1082 | "data": { 1083 | "text/plain": [ 1084 | ":(x - 1)" 1085 | ] 1086 | }, 1087 | "execution_count": 37, 1088 | "metadata": {}, 1089 | "output_type": "execute_result" 1090 | } 1091 | ], 1092 | "source": [ 1093 | "ex.args[2]" 1094 | ] 1095 | }, 1096 | { 1097 | "cell_type": "code", 1098 | "execution_count": 39, 1099 | "metadata": { 1100 | "collapsed": false 1101 | }, 1102 | "outputs": [ 1103 | { 1104 | "data": { 1105 | "text/plain": [ 1106 | ":-" 1107 | ] 1108 | }, 1109 | "execution_count": 39, 1110 | "metadata": {}, 1111 | "output_type": "execute_result" 1112 | } 1113 | ], 1114 | "source": [ 1115 | "ex.args[2].args[1]" 1116 | ] 1117 | }, 1118 | { 1119 | "cell_type": "code", 1120 | "execution_count": 40, 1121 | "metadata": { 1122 | "collapsed": false 1123 | }, 1124 | "outputs": [ 1125 | { 1126 | "data": { 1127 | "text/plain": [ 1128 | ":x" 1129 | ] 1130 | }, 1131 | "execution_count": 40, 1132 | "metadata": {}, 1133 | "output_type": "execute_result" 1134 | } 1135 | ], 1136 | "source": [ 1137 | "ex.args[2].args[2]" 1138 | ] 1139 | }, 1140 | { 1141 | "cell_type": "markdown", 1142 | "metadata": {}, 1143 | "source": [ 1144 | "What is this `:x`?" 1145 | ] 1146 | }, 1147 | { 1148 | "cell_type": "code", 1149 | "execution_count": 41, 1150 | "metadata": { 1151 | "collapsed": false 1152 | }, 1153 | "outputs": [ 1154 | { 1155 | "data": { 1156 | "text/plain": [ 1157 | "Symbol" 1158 | ] 1159 | }, 1160 | "execution_count": 41, 1161 | "metadata": {}, 1162 | "output_type": "execute_result" 1163 | } 1164 | ], 1165 | "source": [ 1166 | "typeof(ex.args[2].args[2])" 1167 | ] 1168 | }, 1169 | { 1170 | "cell_type": "markdown", 1171 | "metadata": {}, 1172 | "source": [ 1173 | "We see that it has a special `Symbol` type, which represents the atoms (smallest parts) of an expression." 1174 | ] 1175 | }, 1176 | { 1177 | "cell_type": "markdown", 1178 | "metadata": {}, 1179 | "source": [ 1180 | "## Modifying expressions " 1181 | ] 1182 | }, 1183 | { 1184 | "cell_type": "markdown", 1185 | "metadata": {}, 1186 | "source": [ 1187 | "Now, what happens if we **modify** this? Let's make a copy of the expression first so that we can compare the two." 1188 | ] 1189 | }, 1190 | { 1191 | "cell_type": "code", 1192 | "execution_count": 50, 1193 | "metadata": { 1194 | "collapsed": false 1195 | }, 1196 | "outputs": [ 1197 | { 1198 | "data": { 1199 | "text/plain": [ 1200 | ":((x - 1) * (x - 2))" 1201 | ] 1202 | }, 1203 | "execution_count": 50, 1204 | "metadata": {}, 1205 | "output_type": "execute_result" 1206 | } 1207 | ], 1208 | "source": [ 1209 | "ex = :((x - 1) * (x - 2))" 1210 | ] 1211 | }, 1212 | { 1213 | "cell_type": "code", 1214 | "execution_count": 51, 1215 | "metadata": { 1216 | "collapsed": false 1217 | }, 1218 | "outputs": [ 1219 | { 1220 | "data": { 1221 | "text/plain": [ 1222 | ":((x - 1) * (x - 2))" 1223 | ] 1224 | }, 1225 | "execution_count": 51, 1226 | "metadata": {}, 1227 | "output_type": "execute_result" 1228 | } 1229 | ], 1230 | "source": [ 1231 | "ex2 = copy(ex)" 1232 | ] 1233 | }, 1234 | { 1235 | "cell_type": "code", 1236 | "execution_count": 52, 1237 | "metadata": { 1238 | "collapsed": false 1239 | }, 1240 | "outputs": [ 1241 | { 1242 | "data": { 1243 | "text/plain": [ 1244 | ":z" 1245 | ] 1246 | }, 1247 | "execution_count": 52, 1248 | "metadata": {}, 1249 | "output_type": "execute_result" 1250 | } 1251 | ], 1252 | "source": [ 1253 | "ex2.args[2] = :z" 1254 | ] 1255 | }, 1256 | { 1257 | "cell_type": "code", 1258 | "execution_count": 53, 1259 | "metadata": { 1260 | "collapsed": false 1261 | }, 1262 | "outputs": [ 1263 | { 1264 | "data": { 1265 | "text/plain": [ 1266 | ":(z * (x - 2))" 1267 | ] 1268 | }, 1269 | "execution_count": 53, 1270 | "metadata": {}, 1271 | "output_type": "execute_result" 1272 | } 1273 | ], 1274 | "source": [ 1275 | "ex2" 1276 | ] 1277 | }, 1278 | { 1279 | "cell_type": "code", 1280 | "execution_count": 54, 1281 | "metadata": { 1282 | "collapsed": false 1283 | }, 1284 | "outputs": [ 1285 | { 1286 | "data": { 1287 | "text/plain": [ 1288 | ":((x - 1) * (x - 2))" 1289 | ] 1290 | }, 1291 | "execution_count": 54, 1292 | "metadata": {}, 1293 | "output_type": "execute_result" 1294 | } 1295 | ], 1296 | "source": [ 1297 | "ex" 1298 | ] 1299 | }, 1300 | { 1301 | "cell_type": "code", 1302 | "execution_count": 43, 1303 | "metadata": { 1304 | "collapsed": false 1305 | }, 1306 | "outputs": [ 1307 | { 1308 | "data": { 1309 | "text/plain": [ 1310 | ":x" 1311 | ] 1312 | }, 1313 | "execution_count": 43, 1314 | "metadata": {}, 1315 | "output_type": "execute_result" 1316 | } 1317 | ], 1318 | "source": [ 1319 | "ex2.args[2].args[2]" 1320 | ] 1321 | }, 1322 | { 1323 | "cell_type": "code", 1324 | "execution_count": 44, 1325 | "metadata": { 1326 | "collapsed": false 1327 | }, 1328 | "outputs": [ 1329 | { 1330 | "data": { 1331 | "text/plain": [ 1332 | ":z" 1333 | ] 1334 | }, 1335 | "execution_count": 44, 1336 | "metadata": {}, 1337 | "output_type": "execute_result" 1338 | } 1339 | ], 1340 | "source": [ 1341 | "ex2.args[2].args[2] = :z" 1342 | ] 1343 | }, 1344 | { 1345 | "cell_type": "markdown", 1346 | "metadata": {}, 1347 | "source": [ 1348 | "We have changed something inside the object `ex2`, so let's look at it:" 1349 | ] 1350 | }, 1351 | { 1352 | "cell_type": "code", 1353 | "execution_count": 45, 1354 | "metadata": { 1355 | "collapsed": false 1356 | }, 1357 | "outputs": [ 1358 | { 1359 | "data": { 1360 | "text/plain": [ 1361 | ":((z - 1) * (x - 2))" 1362 | ] 1363 | }, 1364 | "execution_count": 45, 1365 | "metadata": {}, 1366 | "output_type": "execute_result" 1367 | } 1368 | ], 1369 | "source": [ 1370 | "ex2" 1371 | ] 1372 | }, 1373 | { 1374 | "cell_type": "code", 1375 | "execution_count": null, 1376 | "metadata": { 1377 | "collapsed": false 1378 | }, 1379 | "outputs": [], 1380 | "source": [ 1381 | "ex" 1382 | ] 1383 | }, 1384 | { 1385 | "cell_type": "markdown", 1386 | "metadata": {}, 1387 | "source": [ 1388 | "The original expression has, indeed, changed! That is, we have taken a piece of Julia code, and used Julia itself to manipulate it into a different piece of Julia code. This is one of the simplest examples of metaprogramming." 1389 | ] 1390 | }, 1391 | { 1392 | "cell_type": "markdown", 1393 | "metadata": {}, 1394 | "source": [ 1395 | "Now we can define `x` and `z` and evaluate the expressions:" 1396 | ] 1397 | }, 1398 | { 1399 | "cell_type": "code", 1400 | "execution_count": 55, 1401 | "metadata": { 1402 | "collapsed": false 1403 | }, 1404 | "outputs": [ 1405 | { 1406 | "data": { 1407 | "text/plain": [ 1408 | "4.5" 1409 | ] 1410 | }, 1411 | "execution_count": 55, 1412 | "metadata": {}, 1413 | "output_type": "execute_result" 1414 | } 1415 | ], 1416 | "source": [ 1417 | "x = 3.5; z = 4.5" 1418 | ] 1419 | }, 1420 | { 1421 | "cell_type": "code", 1422 | "execution_count": 56, 1423 | "metadata": { 1424 | "collapsed": false 1425 | }, 1426 | "outputs": [ 1427 | { 1428 | "data": { 1429 | "text/plain": [ 1430 | "(3.75,6.75)" 1431 | ] 1432 | }, 1433 | "execution_count": 56, 1434 | "metadata": {}, 1435 | "output_type": "execute_result" 1436 | } 1437 | ], 1438 | "source": [ 1439 | "eval(ex), eval(ex2)" 1440 | ] 1441 | }, 1442 | { 1443 | "cell_type": "markdown", 1444 | "metadata": {}, 1445 | "source": [ 1446 | "## Walking a syntax tree " 1447 | ] 1448 | }, 1449 | { 1450 | "cell_type": "markdown", 1451 | "metadata": {}, 1452 | "source": [ 1453 | "What if we wanted to replace *all* of the `x`s in a given expression. The problem is that they may be buried arbitrarily deeply in the nested syntax tree. So we have to traverse the tree, in order to visit each piece of it and check if it is an `:x`." 1454 | ] 1455 | }, 1456 | { 1457 | "cell_type": "markdown", 1458 | "metadata": {}, 1459 | "source": [ 1460 | "**Exercise**: Write a function that takes an expression object and replaces **all** of the `:x`s by `:z`s." 1461 | ] 1462 | }, 1463 | { 1464 | "cell_type": "code", 1465 | "execution_count": 59, 1466 | "metadata": { 1467 | "collapsed": false 1468 | }, 1469 | "outputs": [ 1470 | { 1471 | "data": { 1472 | "text/plain": [ 1473 | "3-element Array{Tuple{Int64,Int64},1}:\n", 1474 | " (1,4)\n", 1475 | " (2,5)\n", 1476 | " (3,6)" 1477 | ] 1478 | }, 1479 | "execution_count": 59, 1480 | "metadata": {}, 1481 | "output_type": "execute_result" 1482 | } 1483 | ], 1484 | "source": [ 1485 | "v = [4, 5, 6]\n", 1486 | "collect(enumerate(v))" 1487 | ] 1488 | }, 1489 | { 1490 | "cell_type": "code", 1491 | "execution_count": 66, 1492 | "metadata": { 1493 | "collapsed": false 1494 | }, 1495 | "outputs": [ 1496 | { 1497 | "data": { 1498 | "text/plain": [ 1499 | "traverse! (generic function with 1 method)" 1500 | ] 1501 | }, 1502 | "execution_count": 66, 1503 | "metadata": {}, 1504 | "output_type": "execute_result" 1505 | } 1506 | ], 1507 | "source": [ 1508 | "function traverse!(ex::Expr)\n", 1509 | " for i in 1:length(ex.args) # enumerate(ex.args)\n", 1510 | " \n", 1511 | " if ex.args[i] == :x\n", 1512 | " #ex.args[i] = :z # we cannot use arg here, since it is a copy, not a reference\n", 1513 | " ex.args[i] = :z\n", 1514 | " end\n", 1515 | " \n", 1516 | " if isa(ex.args[i], Expr)\n", 1517 | " traverse!(ex.args[i])\n", 1518 | " end\n", 1519 | " end\n", 1520 | "end" 1521 | ] 1522 | }, 1523 | { 1524 | "cell_type": "code", 1525 | "execution_count": 67, 1526 | "metadata": { 1527 | "collapsed": false 1528 | }, 1529 | "outputs": [ 1530 | { 1531 | "data": { 1532 | "text/plain": [ 1533 | ":((x - 1) * (x - 2))" 1534 | ] 1535 | }, 1536 | "execution_count": 67, 1537 | "metadata": {}, 1538 | "output_type": "execute_result" 1539 | } 1540 | ], 1541 | "source": [ 1542 | "ex = :((x-1)*(x-2))" 1543 | ] 1544 | }, 1545 | { 1546 | "cell_type": "code", 1547 | "execution_count": 68, 1548 | "metadata": { 1549 | "collapsed": false 1550 | }, 1551 | "outputs": [ 1552 | { 1553 | "data": { 1554 | "text/plain": [ 1555 | ":((z - 1) * (z - 2))" 1556 | ] 1557 | }, 1558 | "execution_count": 68, 1559 | "metadata": {}, 1560 | "output_type": "execute_result" 1561 | } 1562 | ], 1563 | "source": [ 1564 | "traverse!(ex)\n", 1565 | "ex" 1566 | ] 1567 | }, 1568 | { 1569 | "cell_type": "markdown", 1570 | "metadata": {}, 1571 | "source": [ 1572 | "Of course, we can now make this potentially more useful by generalising the function, allowing us to replace one sub-expression by another:" 1573 | ] 1574 | }, 1575 | { 1576 | "cell_type": "code", 1577 | "execution_count": 72, 1578 | "metadata": { 1579 | "collapsed": false 1580 | }, 1581 | "outputs": [ 1582 | { 1583 | "data": { 1584 | "text/plain": [ 1585 | "traverse! (generic function with 2 methods)" 1586 | ] 1587 | }, 1588 | "execution_count": 72, 1589 | "metadata": {}, 1590 | "output_type": "execute_result" 1591 | } 1592 | ], 1593 | "source": [ 1594 | "function traverse!(ex::Expr, find, replace)\n", 1595 | " for (i, arg) in enumerate(ex.args)\n", 1596 | " \n", 1597 | " if arg == find\n", 1598 | " ex.args[i] = replace # we cannot use arg here, since it is a copy, not a reference\n", 1599 | " end\n", 1600 | " \n", 1601 | " if isa(arg, Expr)\n", 1602 | " traverse!(arg, find, replace) # recursive\n", 1603 | " end\n", 1604 | " end\n", 1605 | " \n", 1606 | " ex\n", 1607 | "end" 1608 | ] 1609 | }, 1610 | { 1611 | "cell_type": "code", 1612 | "execution_count": 73, 1613 | "metadata": { 1614 | "collapsed": false 1615 | }, 1616 | "outputs": [ 1617 | { 1618 | "data": { 1619 | "text/plain": [ 1620 | ":((x - 10) * (x - 2))" 1621 | ] 1622 | }, 1623 | "execution_count": 73, 1624 | "metadata": {}, 1625 | "output_type": "execute_result" 1626 | } 1627 | ], 1628 | "source": [ 1629 | "traverse!( :( (x-1) * (x-2) ), :(x-1), :(x-10) )" 1630 | ] 1631 | }, 1632 | { 1633 | "cell_type": "code", 1634 | "execution_count": 74, 1635 | "metadata": { 1636 | "collapsed": false 1637 | }, 1638 | "outputs": [ 1639 | { 1640 | "data": { 1641 | "text/plain": [ 1642 | ":((z - 1) * (z - 2))" 1643 | ] 1644 | }, 1645 | "execution_count": 74, 1646 | "metadata": {}, 1647 | "output_type": "execute_result" 1648 | } 1649 | ], 1650 | "source": [ 1651 | "traverse!( :( (x-1) * (x-2) ), :(x), :(z) )" 1652 | ] 1653 | }, 1654 | { 1655 | "cell_type": "markdown", 1656 | "metadata": {}, 1657 | "source": [ 1658 | "This is, of course, not general enough - for example, we cannot replace something of the form `:(x - a)` with `:(x - 2a)` with the current version; this would require more sophisticated pattern matching capabilities - but it demonstrates the basic idea." 1659 | ] 1660 | }, 1661 | { 1662 | "cell_type": "markdown", 1663 | "metadata": {}, 1664 | "source": [ 1665 | "#### Exercise\n", 1666 | "Julia by default uses standard 64-bit (or 32-bit) integers, which leads to surprising behaviour, e.g." 1667 | ] 1668 | }, 1669 | { 1670 | "cell_type": "code", 1671 | "execution_count": 76, 1672 | "metadata": { 1673 | "collapsed": false 1674 | }, 1675 | "outputs": [ 1676 | { 1677 | "data": { 1678 | "text/plain": [ 1679 | "-9223372036854775808" 1680 | ] 1681 | }, 1682 | "execution_count": 76, 1683 | "metadata": {}, 1684 | "output_type": "execute_result" 1685 | } 1686 | ], 1687 | "source": [ 1688 | "2^32 * 2^31" 1689 | ] 1690 | }, 1691 | { 1692 | "cell_type": "markdown", 1693 | "metadata": {}, 1694 | "source": [ 1695 | "No warning is given that there was an overflow in this calculation. \n", 1696 | "\n", 1697 | "However, in `Base` there are *checked* operations, such as `checked_mul`, which do throw an exception on overflow:" 1698 | ] 1699 | }, 1700 | { 1701 | "cell_type": "code", 1702 | "execution_count": 77, 1703 | "metadata": { 1704 | "collapsed": false 1705 | }, 1706 | "outputs": [ 1707 | { 1708 | "ename": "LoadError", 1709 | "evalue": "LoadError: OverflowError()\nwhile loading In[77], in expression starting on line 1", 1710 | "output_type": "error", 1711 | "traceback": [ 1712 | "LoadError: OverflowError()\nwhile loading In[77], in expression starting on line 1", 1713 | "", 1714 | " in checked_mul at int.jl:514" 1715 | ] 1716 | } 1717 | ], 1718 | "source": [ 1719 | "Base.checked_mul(2^60, 2^60)" 1720 | ] 1721 | }, 1722 | { 1723 | "cell_type": "markdown", 1724 | "metadata": {}, 1725 | "source": [ 1726 | "#### Exercise\n", 1727 | "Write a function `checked` that replaces standard functions (`-`, `+`, `*`, `/`) in an expression by their corresponding checked counterparts." 1728 | ] 1729 | }, 1730 | { 1731 | "cell_type": "markdown", 1732 | "metadata": {}, 1733 | "source": [ 1734 | "## Code generation" 1735 | ] 1736 | }, 1737 | { 1738 | "cell_type": "markdown", 1739 | "metadata": {}, 1740 | "source": [ 1741 | "Let's return to the original question: how to create a long polynomial expression. \n", 1742 | "So far, we have not seen how to *add* code, only *change* code that is already there. \n", 1743 | "\n", 1744 | "A natural idea is to build the code up step by step; this is known as **code generation**." 1745 | ] 1746 | }, 1747 | { 1748 | "cell_type": "markdown", 1749 | "metadata": {}, 1750 | "source": [ 1751 | "Let's start again from the first term:" 1752 | ] 1753 | }, 1754 | { 1755 | "cell_type": "code", 1756 | "execution_count": 79, 1757 | "metadata": { 1758 | "collapsed": false 1759 | }, 1760 | "outputs": [ 1761 | { 1762 | "data": { 1763 | "text/plain": [ 1764 | ":(x - 1)" 1765 | ] 1766 | }, 1767 | "execution_count": 79, 1768 | "metadata": {}, 1769 | "output_type": "execute_result" 1770 | } 1771 | ], 1772 | "source": [ 1773 | "ex = :(x-1)" 1774 | ] 1775 | }, 1776 | { 1777 | "cell_type": "code", 1778 | "execution_count": 80, 1779 | "metadata": { 1780 | "collapsed": false 1781 | }, 1782 | "outputs": [ 1783 | { 1784 | "data": { 1785 | "text/plain": [ 1786 | "Expr" 1787 | ] 1788 | }, 1789 | "execution_count": 80, 1790 | "metadata": {}, 1791 | "output_type": "execute_result" 1792 | } 1793 | ], 1794 | "source": [ 1795 | "typeof(ex)" 1796 | ] 1797 | }, 1798 | { 1799 | "cell_type": "markdown", 1800 | "metadata": {}, 1801 | "source": [ 1802 | "We would like to take what we have and create code that is \"what we have multiplied by `:(x-2)`\". Let's try:" 1803 | ] 1804 | }, 1805 | { 1806 | "cell_type": "code", 1807 | "execution_count": 81, 1808 | "metadata": { 1809 | "collapsed": false 1810 | }, 1811 | "outputs": [ 1812 | { 1813 | "data": { 1814 | "text/plain": [ 1815 | ":(ex * (x - 2))" 1816 | ] 1817 | }, 1818 | "execution_count": 81, 1819 | "metadata": {}, 1820 | "output_type": "execute_result" 1821 | } 1822 | ], 1823 | "source": [ 1824 | "ex_new = :(ex * (x-2))" 1825 | ] 1826 | }, 1827 | { 1828 | "cell_type": "markdown", 1829 | "metadata": {}, 1830 | "source": [ 1831 | "This doesn't work, since `ex` is treated as a symbol, whereas we need the *value* contained in the *variable* called `ex`. This is obtained using the `$` operator, a procedure called **interpolation**. (Compare this to string interpolation.)" 1832 | ] 1833 | }, 1834 | { 1835 | "cell_type": "code", 1836 | "execution_count": 82, 1837 | "metadata": { 1838 | "collapsed": false 1839 | }, 1840 | "outputs": [ 1841 | { 1842 | "data": { 1843 | "text/plain": [ 1844 | "\"Hello David\"" 1845 | ] 1846 | }, 1847 | "execution_count": 82, 1848 | "metadata": {}, 1849 | "output_type": "execute_result" 1850 | } 1851 | ], 1852 | "source": [ 1853 | "name = \"David\"\n", 1854 | "s = \"Hello $name\"" 1855 | ] 1856 | }, 1857 | { 1858 | "cell_type": "code", 1859 | "execution_count": 83, 1860 | "metadata": { 1861 | "collapsed": false 1862 | }, 1863 | "outputs": [ 1864 | { 1865 | "data": { 1866 | "text/plain": [ 1867 | ":((x - 1) * (x - 2))" 1868 | ] 1869 | }, 1870 | "execution_count": 83, 1871 | "metadata": {}, 1872 | "output_type": "execute_result" 1873 | } 1874 | ], 1875 | "source": [ 1876 | "ex = :( $ex * (x-2) )" 1877 | ] 1878 | }, 1879 | { 1880 | "cell_type": "markdown", 1881 | "metadata": { 1882 | "collapsed": false 1883 | }, 1884 | "source": [ 1885 | "Now we can continue:" 1886 | ] 1887 | }, 1888 | { 1889 | "cell_type": "code", 1890 | "execution_count": 84, 1891 | "metadata": { 1892 | "collapsed": false 1893 | }, 1894 | "outputs": [ 1895 | { 1896 | "data": { 1897 | "text/plain": [ 1898 | ":(((x - 1) * (x - 2)) * (x - 3))" 1899 | ] 1900 | }, 1901 | "execution_count": 84, 1902 | "metadata": {}, 1903 | "output_type": "execute_result" 1904 | } 1905 | ], 1906 | "source": [ 1907 | "ex = :( $ex * (x-3) ) " 1908 | ] 1909 | }, 1910 | { 1911 | "cell_type": "markdown", 1912 | "metadata": {}, 1913 | "source": [ 1914 | "Finally we see how to construct our loop:" 1915 | ] 1916 | }, 1917 | { 1918 | "cell_type": "code", 1919 | "execution_count": 89, 1920 | "metadata": { 1921 | "collapsed": true 1922 | }, 1923 | "outputs": [], 1924 | "source": [ 1925 | "n = 10\n", 1926 | "ex = :(x-1)\n", 1927 | "\n", 1928 | "for i in 2:n\n", 1929 | " ex = :( $ex * (x - i) )\n", 1930 | "end" 1931 | ] 1932 | }, 1933 | { 1934 | "cell_type": "code", 1935 | "execution_count": 88, 1936 | "metadata": { 1937 | "collapsed": false 1938 | }, 1939 | "outputs": [ 1940 | { 1941 | "data": { 1942 | "text/plain": [ 1943 | ":((((((((((x - 1) * (x - 2)) * (x - 3)) * (x - 4)) * (x - 5)) * (x - 6)) * (x - 7)) * (x - 8)) * (x - 9)) * (x - 10))" 1944 | ] 1945 | }, 1946 | "execution_count": 88, 1947 | "metadata": {}, 1948 | "output_type": "execute_result" 1949 | } 1950 | ], 1951 | "source": [ 1952 | "ex" 1953 | ] 1954 | }, 1955 | { 1956 | "cell_type": "markdown", 1957 | "metadata": {}, 1958 | "source": [ 1959 | "This did not work, since once again we did not want \"the code '`i`'\", but rather the value of the variable `i`. So:" 1960 | ] 1961 | }, 1962 | { 1963 | "cell_type": "code", 1964 | "execution_count": 97, 1965 | "metadata": { 1966 | "collapsed": true 1967 | }, 1968 | "outputs": [], 1969 | "source": [ 1970 | "n = 10\n", 1971 | "\n", 1972 | "ex = :(x-1)\n", 1973 | "\n", 1974 | "for i in 2:n\n", 1975 | " ex = :( $ex * (x - $i) )\n", 1976 | "end" 1977 | ] 1978 | }, 1979 | { 1980 | "cell_type": "code", 1981 | "execution_count": 98, 1982 | "metadata": { 1983 | "collapsed": false 1984 | }, 1985 | "outputs": [ 1986 | { 1987 | "data": { 1988 | "text/plain": [ 1989 | ":((((((((((x - 1) * (x - 2)) * (x - 3)) * (x - 4)) * (x - 5)) * (x - 6)) * (x - 7)) * (x - 8)) * (x - 9)) * (x - 10))" 1990 | ] 1991 | }, 1992 | "execution_count": 98, 1993 | "metadata": {}, 1994 | "output_type": "execute_result" 1995 | } 1996 | ], 1997 | "source": [ 1998 | "ex" 1999 | ] 2000 | }, 2001 | { 2002 | "cell_type": "markdown", 2003 | "metadata": {}, 2004 | "source": [ 2005 | "This is almost what we would write by hand, except for the large number of parentheses." 2006 | ] 2007 | }, 2008 | { 2009 | "cell_type": "markdown", 2010 | "metadata": {}, 2011 | "source": [ 2012 | "Now we need to produce the name of the function:" 2013 | ] 2014 | }, 2015 | { 2016 | "cell_type": "code", 2017 | "execution_count": 100, 2018 | "metadata": { 2019 | "collapsed": false 2020 | }, 2021 | "outputs": [ 2022 | { 2023 | "data": { 2024 | "text/plain": [ 2025 | "\"p_10\"" 2026 | ] 2027 | }, 2028 | "execution_count": 100, 2029 | "metadata": {}, 2030 | "output_type": "execute_result" 2031 | } 2032 | ], 2033 | "source": [ 2034 | "string(\"p_\", n)" 2035 | ] 2036 | }, 2037 | { 2038 | "cell_type": "code", 2039 | "execution_count": 99, 2040 | "metadata": { 2041 | "collapsed": false 2042 | }, 2043 | "outputs": [ 2044 | { 2045 | "data": { 2046 | "text/plain": [ 2047 | ":p_10" 2048 | ] 2049 | }, 2050 | "execution_count": 99, 2051 | "metadata": {}, 2052 | "output_type": "execute_result" 2053 | } 2054 | ], 2055 | "source": [ 2056 | "name = symbol(\"p_\", n) # use `Symbol` instead of `symbol` in Julia v0.5" 2057 | ] 2058 | }, 2059 | { 2060 | "cell_type": "markdown", 2061 | "metadata": {}, 2062 | "source": [ 2063 | "The code is then" 2064 | ] 2065 | }, 2066 | { 2067 | "cell_type": "code", 2068 | "execution_count": 101, 2069 | "metadata": { 2070 | "collapsed": false 2071 | }, 2072 | "outputs": [ 2073 | { 2074 | "data": { 2075 | "text/plain": [ 2076 | ":(p_10(x) = begin # In[101], line 1:\n", 2077 | " (((((((((x - 1) * (x - 2)) * (x - 3)) * (x - 4)) * (x - 5)) * (x - 6)) * (x - 7)) * (x - 8)) * (x - 9)) * (x - 10)\n", 2078 | " end)" 2079 | ] 2080 | }, 2081 | "execution_count": 101, 2082 | "metadata": {}, 2083 | "output_type": "execute_result" 2084 | } 2085 | ], 2086 | "source": [ 2087 | "code = :( $name(x) = $ex )" 2088 | ] 2089 | }, 2090 | { 2091 | "cell_type": "markdown", 2092 | "metadata": {}, 2093 | "source": [ 2094 | "We can wrap this in a function:" 2095 | ] 2096 | }, 2097 | { 2098 | "cell_type": "code", 2099 | "execution_count": 103, 2100 | "metadata": { 2101 | "collapsed": false 2102 | }, 2103 | "outputs": [ 2104 | { 2105 | "data": { 2106 | "text/plain": [ 2107 | "make_wilkinson (generic function with 1 method)" 2108 | ] 2109 | }, 2110 | "execution_count": 103, 2111 | "metadata": {}, 2112 | "output_type": "execute_result" 2113 | } 2114 | ], 2115 | "source": [ 2116 | "function make_wilkinson(n)\n", 2117 | " \n", 2118 | " ex = :(x-1)\n", 2119 | "\n", 2120 | " for i in 2:n\n", 2121 | " ex = :( $ex * (x - $i) )\n", 2122 | " end\n", 2123 | " \n", 2124 | " name = symbol(\"p_\", n) \n", 2125 | " code = :( $name(x) = $ex )\n", 2126 | " \n", 2127 | " eval(code)\n", 2128 | "end" 2129 | ] 2130 | }, 2131 | { 2132 | "cell_type": "markdown", 2133 | "metadata": {}, 2134 | "source": [ 2135 | "Finally we evaluate this:" 2136 | ] 2137 | }, 2138 | { 2139 | "cell_type": "code", 2140 | "execution_count": 104, 2141 | "metadata": { 2142 | "collapsed": false 2143 | }, 2144 | "outputs": [ 2145 | { 2146 | "data": { 2147 | "text/plain": [ 2148 | "p_10 (generic function with 1 method)" 2149 | ] 2150 | }, 2151 | "execution_count": 104, 2152 | "metadata": {}, 2153 | "output_type": "execute_result" 2154 | } 2155 | ], 2156 | "source": [ 2157 | "make_wilkinson(10)" 2158 | ] 2159 | }, 2160 | { 2161 | "cell_type": "code", 2162 | "execution_count": 105, 2163 | "metadata": { 2164 | "collapsed": false 2165 | }, 2166 | "outputs": [ 2167 | { 2168 | "data": { 2169 | "text/plain": [ 2170 | "p_100 (generic function with 1 method)" 2171 | ] 2172 | }, 2173 | "execution_count": 105, 2174 | "metadata": {}, 2175 | "output_type": "execute_result" 2176 | } 2177 | ], 2178 | "source": [ 2179 | "make_wilkinson(100)" 2180 | ] 2181 | }, 2182 | { 2183 | "cell_type": "markdown", 2184 | "metadata": {}, 2185 | "source": [ 2186 | "This creates a function with the name `p_10` that does what we would write by hand." 2187 | ] 2188 | }, 2189 | { 2190 | "cell_type": "markdown", 2191 | "metadata": {}, 2192 | "source": [ 2193 | "Let's compare the two options by evaluating the function on a grid of values:" 2194 | ] 2195 | }, 2196 | { 2197 | "cell_type": "code", 2198 | "execution_count": 106, 2199 | "metadata": { 2200 | "collapsed": false 2201 | }, 2202 | "outputs": [ 2203 | { 2204 | "data": { 2205 | "text/plain": [ 2206 | "f2 (generic function with 1 method)" 2207 | ] 2208 | }, 2209 | "execution_count": 106, 2210 | "metadata": {}, 2211 | "output_type": "execute_result" 2212 | } 2213 | ], 2214 | "source": [ 2215 | "function f1(range)\n", 2216 | " total = 0.0\n", 2217 | " for x in range\n", 2218 | " total += p_100(x)\n", 2219 | " end\n", 2220 | " return total\n", 2221 | "end\n", 2222 | "\n", 2223 | "function f2(range)\n", 2224 | " total = 0.0\n", 2225 | " for x in range\n", 2226 | " total += wilkinson(100, x)\n", 2227 | " end\n", 2228 | " return total\n", 2229 | "end" 2230 | ] 2231 | }, 2232 | { 2233 | "cell_type": "code", 2234 | "execution_count": null, 2235 | "metadata": { 2236 | "collapsed": true 2237 | }, 2238 | "outputs": [], 2239 | "source": [] 2240 | }, 2241 | { 2242 | "cell_type": "code", 2243 | "execution_count": 112, 2244 | "metadata": { 2245 | "collapsed": false 2246 | }, 2247 | "outputs": [ 2248 | { 2249 | "data": { 2250 | "text/plain": [ 2251 | "1.2080035401369522" 2252 | ] 2253 | }, 2254 | "execution_count": 112, 2255 | "metadata": {}, 2256 | "output_type": "execute_result" 2257 | } 2258 | ], 2259 | "source": [ 2260 | "range = -10:0.000001:10\n", 2261 | "t1 = @elapsed f1(range);\n", 2262 | "t2 = @elapsed f2(range);\n", 2263 | "t2 / t1" 2264 | ] 2265 | }, 2266 | { 2267 | "cell_type": "markdown", 2268 | "metadata": {}, 2269 | "source": [ 2270 | "We see that the generated code with the unrolled loop is 10% faster than the naive loop with 100 terms." 2271 | ] 2272 | }, 2273 | { 2274 | "cell_type": "markdown", 2275 | "metadata": {}, 2276 | "source": [ 2277 | "### Starting from empty" 2278 | ] 2279 | }, 2280 | { 2281 | "cell_type": "markdown", 2282 | "metadata": {}, 2283 | "source": [ 2284 | "In some cases, it is useful to start from something empty and add statements to it.\n", 2285 | "To do this, we can do, for example," 2286 | ] 2287 | }, 2288 | { 2289 | "cell_type": "code", 2290 | "execution_count": 113, 2291 | "metadata": { 2292 | "collapsed": false 2293 | }, 2294 | "outputs": [ 2295 | { 2296 | "data": { 2297 | "text/plain": [ 2298 | "quote \n", 2299 | "end" 2300 | ] 2301 | }, 2302 | "execution_count": 113, 2303 | "metadata": {}, 2304 | "output_type": "execute_result" 2305 | } 2306 | ], 2307 | "source": [ 2308 | "code = quote end # empty" 2309 | ] 2310 | }, 2311 | { 2312 | "cell_type": "code", 2313 | "execution_count": 114, 2314 | "metadata": { 2315 | "collapsed": false 2316 | }, 2317 | "outputs": [ 2318 | { 2319 | "name": "stdout", 2320 | "output_type": "stream", 2321 | "text": [ 2322 | "Expr \n", 2323 | " head: Symbol block\n", 2324 | " args: Array(Any,(0,))\n", 2325 | " typ: Any\n" 2326 | ] 2327 | } 2328 | ], 2329 | "source": [ 2330 | "dump(code)" 2331 | ] 2332 | }, 2333 | { 2334 | "cell_type": "markdown", 2335 | "metadata": {}, 2336 | "source": [ 2337 | "[Note that in Julia 0.5, this is not \"empty\", since it has a `:line` line-number node.]\n", 2338 | "\n", 2339 | "We can add statements using the standard Julia `push!`:" 2340 | ] 2341 | }, 2342 | { 2343 | "cell_type": "code", 2344 | "execution_count": 115, 2345 | "metadata": { 2346 | "collapsed": false 2347 | }, 2348 | "outputs": [ 2349 | { 2350 | "data": { 2351 | "text/plain": [ 2352 | "quote \n", 2353 | " a = 1\n", 2354 | " b = a + 1\n", 2355 | "end" 2356 | ] 2357 | }, 2358 | "execution_count": 115, 2359 | "metadata": {}, 2360 | "output_type": "execute_result" 2361 | } 2362 | ], 2363 | "source": [ 2364 | "Base.push!(code.args, :(a = 1))\n", 2365 | "Base.push!(code.args, :(b = a + 1))\n", 2366 | "code" 2367 | ] 2368 | }, 2369 | { 2370 | "cell_type": "code", 2371 | "execution_count": 116, 2372 | "metadata": { 2373 | "collapsed": false 2374 | }, 2375 | "outputs": [ 2376 | { 2377 | "data": { 2378 | "text/plain": [ 2379 | "2" 2380 | ] 2381 | }, 2382 | "execution_count": 116, 2383 | "metadata": {}, 2384 | "output_type": "execute_result" 2385 | } 2386 | ], 2387 | "source": [ 2388 | "eval(code)" 2389 | ] 2390 | }, 2391 | { 2392 | "cell_type": "code", 2393 | "execution_count": 117, 2394 | "metadata": { 2395 | "collapsed": false 2396 | }, 2397 | "outputs": [ 2398 | { 2399 | "data": { 2400 | "text/plain": [ 2401 | "1" 2402 | ] 2403 | }, 2404 | "execution_count": 117, 2405 | "metadata": {}, 2406 | "output_type": "execute_result" 2407 | } 2408 | ], 2409 | "source": [ 2410 | "a" 2411 | ] 2412 | }, 2413 | { 2414 | "cell_type": "code", 2415 | "execution_count": 118, 2416 | "metadata": { 2417 | "collapsed": false 2418 | }, 2419 | "outputs": [ 2420 | { 2421 | "data": { 2422 | "text/plain": [ 2423 | "2" 2424 | ] 2425 | }, 2426 | "execution_count": 118, 2427 | "metadata": {}, 2428 | "output_type": "execute_result" 2429 | } 2430 | ], 2431 | "source": [ 2432 | "b" 2433 | ] 2434 | }, 2435 | { 2436 | "cell_type": "code", 2437 | "execution_count": 119, 2438 | "metadata": { 2439 | "collapsed": false 2440 | }, 2441 | "outputs": [ 2442 | { 2443 | "name": "stdout", 2444 | "output_type": "stream", 2445 | "text": [ 2446 | "Expr \n", 2447 | " head: Symbol call\n", 2448 | " args: Array(Any,(4,))\n", 2449 | " 1: Symbol *\n", 2450 | " 2: Symbol a\n", 2451 | " 3: Symbol b\n", 2452 | " 4: Symbol c\n", 2453 | " typ: Any\n" 2454 | ] 2455 | } 2456 | ], 2457 | "source": [ 2458 | "dump(:(a * b * c))" 2459 | ] 2460 | }, 2461 | { 2462 | "cell_type": "markdown", 2463 | "metadata": {}, 2464 | "source": [ 2465 | "**Exercise**: Build up the Wilkinson example using this technique." 2466 | ] 2467 | }, 2468 | { 2469 | "cell_type": "markdown", 2470 | "metadata": {}, 2471 | "source": [ 2472 | "## Repetitive code" 2473 | ] 2474 | }, 2475 | { 2476 | "cell_type": "markdown", 2477 | "metadata": {}, 2478 | "source": [ 2479 | "Code generation is used frequently in Julia code when repetitive code is required. For example, let's return to the idea of wrapping a type:" 2480 | ] 2481 | }, 2482 | { 2483 | "cell_type": "code", 2484 | "execution_count": 121, 2485 | "metadata": { 2486 | "collapsed": false 2487 | }, 2488 | "outputs": [], 2489 | "source": [ 2490 | "type OurFloat\n", 2491 | " x::Float64\n", 2492 | "end" 2493 | ] 2494 | }, 2495 | { 2496 | "cell_type": "markdown", 2497 | "metadata": {}, 2498 | "source": [ 2499 | "We can generate objects of this type:" 2500 | ] 2501 | }, 2502 | { 2503 | "cell_type": "code", 2504 | "execution_count": 123, 2505 | "metadata": { 2506 | "collapsed": false 2507 | }, 2508 | "outputs": [ 2509 | { 2510 | "data": { 2511 | "text/plain": [ 2512 | "OurFloat(4.0)" 2513 | ] 2514 | }, 2515 | "execution_count": 123, 2516 | "metadata": {}, 2517 | "output_type": "execute_result" 2518 | } 2519 | ], 2520 | "source": [ 2521 | "a = OurFloat(3)\n", 2522 | "b = OurFloat(4)" 2523 | ] 2524 | }, 2525 | { 2526 | "cell_type": "markdown", 2527 | "metadata": {}, 2528 | "source": [ 2529 | "But arithmetic operations are not defined:" 2530 | ] 2531 | }, 2532 | { 2533 | "cell_type": "code", 2534 | "execution_count": 124, 2535 | "metadata": { 2536 | "collapsed": false 2537 | }, 2538 | "outputs": [ 2539 | { 2540 | "ename": "LoadError", 2541 | "evalue": "LoadError: MethodError: `+` has no method matching +(::OurFloat, ::OurFloat)\nClosest candidates are:\n +(::Any, ::Any, !Matched::Any, !Matched::Any...)\nwhile loading In[124], in expression starting on line 1", 2542 | "output_type": "error", 2543 | "traceback": [ 2544 | "LoadError: MethodError: `+` has no method matching +(::OurFloat, ::OurFloat)\nClosest candidates are:\n +(::Any, ::Any, !Matched::Any, !Matched::Any...)\nwhile loading In[124], in expression starting on line 1", 2545 | "" 2546 | ] 2547 | } 2548 | ], 2549 | "source": [ 2550 | "a + b" 2551 | ] 2552 | }, 2553 | { 2554 | "cell_type": "markdown", 2555 | "metadata": {}, 2556 | "source": [ 2557 | "We can define them in the natural way:" 2558 | ] 2559 | }, 2560 | { 2561 | "cell_type": "code", 2562 | "execution_count": 125, 2563 | "metadata": { 2564 | "collapsed": false 2565 | }, 2566 | "outputs": [ 2567 | { 2568 | "data": { 2569 | "text/plain": [ 2570 | "- (generic function with 205 methods)" 2571 | ] 2572 | }, 2573 | "execution_count": 125, 2574 | "metadata": {}, 2575 | "output_type": "execute_result" 2576 | } 2577 | ], 2578 | "source": [ 2579 | "import Base: +, -, *, /\n", 2580 | "+(a::OurFloat, b::OurFloat) = a.x + b.x\n", 2581 | "-(a::OurFloat, b::OurFloat) = a.x - b.x" 2582 | ] 2583 | }, 2584 | { 2585 | "cell_type": "markdown", 2586 | "metadata": {}, 2587 | "source": [ 2588 | "But this will quickly get dull, and we could easily make a mistake. \n", 2589 | "As usual, whenever we are repeating something more than twice, we should try to automate it.\n", 2590 | "\n", 2591 | "We have some code of the form\n", 2592 | "\n", 2593 | " *op*(a::OurFloat, b::OurFloat) = a.x *op* b.x\n", 2594 | " \n", 2595 | "where `*op*` is supposed to represent the operator. Julia allows us to do this almost literally; we just need to substitute in the *value* of the *variable* `op`! This is an **exercise**" 2596 | ] 2597 | }, 2598 | { 2599 | "cell_type": "code", 2600 | "execution_count": 127, 2601 | "metadata": { 2602 | "collapsed": false 2603 | }, 2604 | "outputs": [ 2605 | { 2606 | "name": "stdout", 2607 | "output_type": "stream", 2608 | "text": [ 2609 | "op = :+\n", 2610 | "code = :(a::OurFloat + b::OurFloat = begin # In[127], line 3:\n", 2611 | " a.x + b.x\n", 2612 | " end)\n", 2613 | "op = :-\n", 2614 | "code = :(a::OurFloat - b::OurFloat = begin # In[127], line 3:\n", 2615 | " a.x - b.x\n", 2616 | " end)\n", 2617 | "op = :*\n", 2618 | "code = :(a::OurFloat * b::OurFloat = begin # In[127], line 3:\n", 2619 | " a.x * b.x\n", 2620 | " end)\n", 2621 | "op = :/\n", 2622 | "code = :(a::OurFloat / b::OurFloat = begin # In[127], line 3:\n", 2623 | " a.x / b.x\n", 2624 | " end)\n" 2625 | ] 2626 | } 2627 | ], 2628 | "source": [ 2629 | "for op in (:+, :-, :*, :/)\n", 2630 | " @show op\n", 2631 | " code = :( $(op)(a::OurFloat, b::OurFloat) = $(op)(a.x, b.x) )\n", 2632 | " @show code\n", 2633 | "end" 2634 | ] 2635 | }, 2636 | { 2637 | "cell_type": "markdown", 2638 | "metadata": {}, 2639 | "source": [ 2640 | "Finally we need to evaluate the code. The combination of `eval` and `:(...)` that we used above can be abbreviated to `@eval`:" 2641 | ] 2642 | }, 2643 | { 2644 | "cell_type": "code", 2645 | "execution_count": 128, 2646 | "metadata": { 2647 | "collapsed": false 2648 | }, 2649 | "outputs": [], 2650 | "source": [ 2651 | "for op in (:+, :-, :*, :/)\n", 2652 | " @eval $(op)(a::OurFloat, b::OurFloat) = $(op)(a.x, b.x) \n", 2653 | "end" 2654 | ] 2655 | }, 2656 | { 2657 | "cell_type": "code", 2658 | "execution_count": 129, 2659 | "metadata": { 2660 | "collapsed": false 2661 | }, 2662 | "outputs": [ 2663 | { 2664 | "data": { 2665 | "text/plain": [ 2666 | "12.0" 2667 | ] 2668 | }, 2669 | "execution_count": 129, 2670 | "metadata": {}, 2671 | "output_type": "execute_result" 2672 | } 2673 | ], 2674 | "source": [ 2675 | "a*b" 2676 | ] 2677 | }, 2678 | { 2679 | "cell_type": "code", 2680 | "execution_count": 130, 2681 | "metadata": { 2682 | "collapsed": false 2683 | }, 2684 | "outputs": [ 2685 | { 2686 | "data": { 2687 | "text/html": [ 2688 | "*(a::OurFloat, b::OurFloat) at In[128]:2" 2689 | ], 2690 | "text/plain": [ 2691 | "*(a::OurFloat, b::OurFloat) at In[128]:2" 2692 | ] 2693 | }, 2694 | "execution_count": 130, 2695 | "metadata": {}, 2696 | "output_type": "execute_result" 2697 | } 2698 | ], 2699 | "source": [ 2700 | "@which a*b" 2701 | ] 2702 | }, 2703 | { 2704 | "cell_type": "code", 2705 | "execution_count": 131, 2706 | "metadata": { 2707 | "collapsed": false 2708 | }, 2709 | "outputs": [ 2710 | { 2711 | "data": { 2712 | "text/html": [ 2713 | "sin(x::Float64) at math.jl:137" 2714 | ], 2715 | "text/plain": [ 2716 | "sin(x::Float64) at math.jl:137" 2717 | ] 2718 | }, 2719 | "execution_count": 131, 2720 | "metadata": {}, 2721 | "output_type": "execute_result" 2722 | } 2723 | ], 2724 | "source": [ 2725 | "@which sin(3.5)" 2726 | ] 2727 | }, 2728 | { 2729 | "cell_type": "markdown", 2730 | "metadata": {}, 2731 | "source": [ 2732 | "## Macros" 2733 | ] 2734 | }, 2735 | { 2736 | "cell_type": "markdown", 2737 | "metadata": {}, 2738 | "source": [ 2739 | "It is very common in Julia to use things that look like functions but whose names start with `@`, e.g. `@time`, `@which`, etc. These are not functions in the standard sense, but rather are a kind of \"super-function\", called **macros**. \n", 2740 | "\n", 2741 | "A macro takes a **piece of Julia code** (`Expr`ession object) as its argument, manipulates that code to turn it into a new one, and **returns a new piece of code** as its output. The effect of a macro call is to insert that new piece of code in place of the old code, which is consequently compiled by the Julia compiler. \n", 2742 | "\n", 2743 | "Note that the user *does not need to explicitly pass an `Expr`ession object, since this is done automatically*." 2744 | ] 2745 | }, 2746 | { 2747 | "cell_type": "markdown", 2748 | "metadata": {}, 2749 | "source": [ 2750 | "To see this, let's define a simple macro:" 2751 | ] 2752 | }, 2753 | { 2754 | "cell_type": "code", 2755 | "execution_count": 132, 2756 | "metadata": { 2757 | "collapsed": false 2758 | }, 2759 | "outputs": [], 2760 | "source": [ 2761 | "macro simple(expr)\n", 2762 | " @show expr\n", 2763 | " nothing # return nothing for the moment\n", 2764 | "end" 2765 | ] 2766 | }, 2767 | { 2768 | "cell_type": "code", 2769 | "execution_count": 137, 2770 | "metadata": { 2771 | "collapsed": false 2772 | }, 2773 | "outputs": [ 2774 | { 2775 | "ename": "LoadError", 2776 | "evalue": "LoadError: UndefVarError: ≲ not defined\nwhile loading In[137], in expression starting on line 3", 2777 | "output_type": "error", 2778 | "traceback": [ 2779 | "LoadError: UndefVarError: ≲ not defined\nwhile loading In[137], in expression starting on line 3", 2780 | "" 2781 | ] 2782 | } 2783 | ], 2784 | "source": [ 2785 | "x = 3\n", 2786 | "y = 4\n", 2787 | "x ≲ y" 2788 | ] 2789 | }, 2790 | { 2791 | "cell_type": "code", 2792 | "execution_count": 138, 2793 | "metadata": { 2794 | "collapsed": false 2795 | }, 2796 | "outputs": [ 2797 | { 2798 | "name": "stdout", 2799 | "output_type": "stream", 2800 | "text": [ 2801 | "expr = :((3 * x ^ 2 + 4x) - 2)\n" 2802 | ] 2803 | } 2804 | ], 2805 | "source": [ 2806 | "@simple 3x^2 + 4x - 2" 2807 | ] 2808 | }, 2809 | { 2810 | "cell_type": "markdown", 2811 | "metadata": {}, 2812 | "source": [ 2813 | "We see that the Julia code that follows the macro call is passed to the macro *already having been parsed into an `Expr` object*." 2814 | ] 2815 | }, 2816 | { 2817 | "cell_type": "markdown", 2818 | "metadata": {}, 2819 | "source": [ 2820 | "Suppose we redefine the macro as" 2821 | ] 2822 | }, 2823 | { 2824 | "cell_type": "code", 2825 | "execution_count": 139, 2826 | "metadata": { 2827 | "collapsed": false 2828 | }, 2829 | "outputs": [], 2830 | "source": [ 2831 | "macro simple(expr)\n", 2832 | " @show expr\n", 2833 | " expr # returns expr\n", 2834 | "end" 2835 | ] 2836 | }, 2837 | { 2838 | "cell_type": "markdown", 2839 | "metadata": {}, 2840 | "source": [ 2841 | "Then we get" 2842 | ] 2843 | }, 2844 | { 2845 | "cell_type": "code", 2846 | "execution_count": 140, 2847 | "metadata": { 2848 | "collapsed": false 2849 | }, 2850 | "outputs": [ 2851 | { 2852 | "name": "stdout", 2853 | "output_type": "stream", 2854 | "text": [ 2855 | "expr = :(w + z)\n" 2856 | ] 2857 | }, 2858 | { 2859 | "ename": "LoadError", 2860 | "evalue": "LoadError: UndefVarError: w not defined\nwhile loading In[140], in expression starting on line 1", 2861 | "output_type": "error", 2862 | "traceback": [ 2863 | "LoadError: UndefVarError: w not defined\nwhile loading In[140], in expression starting on line 1", 2864 | "" 2865 | ] 2866 | } 2867 | ], 2868 | "source": [ 2869 | "@simple w + z" 2870 | ] 2871 | }, 2872 | { 2873 | "cell_type": "markdown", 2874 | "metadata": {}, 2875 | "source": [ 2876 | "What is happening here is that the macro returns the expression `:(x+y)`, and this is then evaluated using `eval`.\n", 2877 | "The result is that Julia tries to calculate the value of the expression `x+y`, but the variable `x` is not defined. Let's define `y` and `z`, but not `x`:" 2878 | ] 2879 | }, 2880 | { 2881 | "cell_type": "code", 2882 | "execution_count": null, 2883 | "metadata": { 2884 | "collapsed": false 2885 | }, 2886 | "outputs": [], 2887 | "source": [ 2888 | "y = 3; z = 4" 2889 | ] 2890 | }, 2891 | { 2892 | "cell_type": "code", 2893 | "execution_count": null, 2894 | "metadata": { 2895 | "collapsed": false 2896 | }, 2897 | "outputs": [], 2898 | "source": [ 2899 | "x" 2900 | ] 2901 | }, 2902 | { 2903 | "cell_type": "code", 2904 | "execution_count": null, 2905 | "metadata": { 2906 | "collapsed": false 2907 | }, 2908 | "outputs": [], 2909 | "source": [ 2910 | "@simple x+y" 2911 | ] 2912 | }, 2913 | { 2914 | "cell_type": "markdown", 2915 | "metadata": {}, 2916 | "source": [ 2917 | "Now let's define a new macro `@replace` that uses our previous `replace` function:" 2918 | ] 2919 | }, 2920 | { 2921 | "cell_type": "code", 2922 | "execution_count": 146, 2923 | "metadata": { 2924 | "collapsed": false 2925 | }, 2926 | "outputs": [], 2927 | "source": [ 2928 | "macro traverse(expr)\n", 2929 | " traverse!(expr)\n", 2930 | " @show expr\n", 2931 | " #Meta.quot(expr)\n", 2932 | " expr\n", 2933 | "end" 2934 | ] 2935 | }, 2936 | { 2937 | "cell_type": "code", 2938 | "execution_count": 147, 2939 | "metadata": { 2940 | "collapsed": false 2941 | }, 2942 | "outputs": [ 2943 | { 2944 | "name": "stdout", 2945 | "output_type": "stream", 2946 | "text": [ 2947 | "expr = :(z + y)\n" 2948 | ] 2949 | }, 2950 | { 2951 | "data": { 2952 | "text/plain": [ 2953 | "8.5" 2954 | ] 2955 | }, 2956 | "execution_count": 147, 2957 | "metadata": {}, 2958 | "output_type": "execute_result" 2959 | } 2960 | ], 2961 | "source": [ 2962 | "@traverse x + y" 2963 | ] 2964 | }, 2965 | { 2966 | "cell_type": "markdown", 2967 | "metadata": {}, 2968 | "source": [ 2969 | "## `macroexpand`" 2970 | ] 2971 | }, 2972 | { 2973 | "cell_type": "markdown", 2974 | "metadata": {}, 2975 | "source": [ 2976 | "We can discover what a macro does using the `macroexpand` function which takes a code expression:" 2977 | ] 2978 | }, 2979 | { 2980 | "cell_type": "code", 2981 | "execution_count": 149, 2982 | "metadata": { 2983 | "collapsed": false 2984 | }, 2985 | "outputs": [ 2986 | { 2987 | "name": "stdout", 2988 | "output_type": "stream", 2989 | "text": [ 2990 | " 0.000003 seconds (5 allocations: 176 bytes)\n" 2991 | ] 2992 | }, 2993 | { 2994 | "data": { 2995 | "text/plain": [ 2996 | "-0.5440211108893698" 2997 | ] 2998 | }, 2999 | "execution_count": 149, 3000 | "metadata": {}, 3001 | "output_type": "execute_result" 3002 | } 3003 | ], 3004 | "source": [ 3005 | "@time sin(10)" 3006 | ] 3007 | }, 3008 | { 3009 | "cell_type": "code", 3010 | "execution_count": 150, 3011 | "metadata": { 3012 | "collapsed": false 3013 | }, 3014 | "outputs": [ 3015 | { 3016 | "data": { 3017 | "text/plain": [ 3018 | ":(sin(10))" 3019 | ] 3020 | }, 3021 | "execution_count": 150, 3022 | "metadata": {}, 3023 | "output_type": "execute_result" 3024 | } 3025 | ], 3026 | "source": [ 3027 | "ex = :(sin(10))" 3028 | ] 3029 | }, 3030 | { 3031 | "cell_type": "code", 3032 | "execution_count": 151, 3033 | "metadata": { 3034 | "collapsed": false 3035 | }, 3036 | "outputs": [ 3037 | { 3038 | "data": { 3039 | "text/plain": [ 3040 | "quote # util.jl, line 153:\n", 3041 | " local #101#stats = Base.gc_num() # util.jl, line 154:\n", 3042 | " local #103#elapsedtime = Base.time_ns() # util.jl, line 155:\n", 3043 | " local #102#val = sin(10) # util.jl, line 156:\n", 3044 | " #103#elapsedtime = Base.-(Base.time_ns(),#103#elapsedtime) # util.jl, line 157:\n", 3045 | " local #104#diff = Base.GC_Diff(Base.gc_num(),#101#stats) # util.jl, line 158:\n", 3046 | " Base.time_print(#103#elapsedtime,#104#diff.allocd,#104#diff.total_time,Base.gc_alloc_count(#104#diff)) # util.jl, line 160:\n", 3047 | " #102#val\n", 3048 | "end" 3049 | ] 3050 | }, 3051 | "execution_count": 151, 3052 | "metadata": {}, 3053 | "output_type": "execute_result" 3054 | } 3055 | ], 3056 | "source": [ 3057 | "macroexpand(:(@time sin(10)))" 3058 | ] 3059 | }, 3060 | { 3061 | "cell_type": "markdown", 3062 | "metadata": {}, 3063 | "source": [ 3064 | "As an example of the use of a macro, let's look at the `@interval` macro from the [`ValidatedNumerics.jl` package](https://github.com/dpsanders/ValidatedNumerics.jl):" 3065 | ] 3066 | }, 3067 | { 3068 | "cell_type": "code", 3069 | "execution_count": null, 3070 | "metadata": { 3071 | "collapsed": false 3072 | }, 3073 | "outputs": [], 3074 | "source": [ 3075 | "# Pkg.add(\"ValidatedNumerics\")" 3076 | ] 3077 | }, 3078 | { 3079 | "cell_type": "code", 3080 | "execution_count": 153, 3081 | "metadata": { 3082 | "collapsed": false 3083 | }, 3084 | "outputs": [], 3085 | "source": [ 3086 | "using ValidatedNumerics" 3087 | ] 3088 | }, 3089 | { 3090 | "cell_type": "code", 3091 | "execution_count": 154, 3092 | "metadata": { 3093 | "collapsed": false 3094 | }, 3095 | "outputs": [ 3096 | { 3097 | "data": { 3098 | "text/plain": [ 3099 | "[0.0999999, 0.100001]" 3100 | ] 3101 | }, 3102 | "execution_count": 154, 3103 | "metadata": {}, 3104 | "output_type": "execute_result" 3105 | } 3106 | ], 3107 | "source": [ 3108 | "@interval(0.1)" 3109 | ] 3110 | }, 3111 | { 3112 | "cell_type": "markdown", 3113 | "metadata": {}, 3114 | "source": [ 3115 | "Floating-point calculations are not precise. Interval arithmetic is a way to provide a guaranteed *enclosure* of a result, i.e. an interval that contains the true result. " 3116 | ] 3117 | }, 3118 | { 3119 | "cell_type": "code", 3120 | "execution_count": 155, 3121 | "metadata": { 3122 | "collapsed": false 3123 | }, 3124 | "outputs": [ 3125 | { 3126 | "data": { 3127 | "text/plain": [ 3128 | ":(ValidatedNumerics.convert(Interval{parameters.precision_type},0.1))" 3129 | ] 3130 | }, 3131 | "execution_count": 155, 3132 | "metadata": {}, 3133 | "output_type": "execute_result" 3134 | } 3135 | ], 3136 | "source": [ 3137 | "macroexpand(:(@interval(0.1)))" 3138 | ] 3139 | }, 3140 | { 3141 | "cell_type": "code", 3142 | "execution_count": 156, 3143 | "metadata": { 3144 | "collapsed": false 3145 | }, 3146 | "outputs": [ 3147 | { 3148 | "data": { 3149 | "text/plain": [ 3150 | "[1.07989, 1.0799]" 3151 | ] 3152 | }, 3153 | "execution_count": 156, 3154 | "metadata": {}, 3155 | "output_type": "execute_result" 3156 | } 3157 | ], 3158 | "source": [ 3159 | "@interval sin(0.1) + cos(0.2)" 3160 | ] 3161 | }, 3162 | { 3163 | "cell_type": "code", 3164 | "execution_count": 158, 3165 | "metadata": { 3166 | "collapsed": false 3167 | }, 3168 | "outputs": [ 3169 | { 3170 | "data": { 3171 | "text/plain": [ 3172 | ":(ValidatedNumerics.+(sin(ValidatedNumerics.convert(Interval{parameters.precision_type},0.1)),cos(ValidatedNumerics.convert(Interval{parameters.precision_type},0.2))))" 3173 | ] 3174 | }, 3175 | "execution_count": 158, 3176 | "metadata": {}, 3177 | "output_type": "execute_result" 3178 | } 3179 | ], 3180 | "source": [ 3181 | "hello = macroexpand(:(@interval sin(0.1) + cos(0.2)))" 3182 | ] 3183 | }, 3184 | { 3185 | "cell_type": "markdown", 3186 | "metadata": {}, 3187 | "source": [ 3188 | "This has the structure `sin(interval(0.1)) + cos(interval(0.2))` (although the details are a bit more complicated).\n", 3189 | "\n", 3190 | "So basically the `@interval` macro does something very similar to our `replace!` macro: it walks the tree of the expression; finds parts of it that are of a certain type (numeric literals); and wraps those in the `make_interval` function" 3191 | ] 3192 | }, 3193 | { 3194 | "cell_type": "markdown", 3195 | "metadata": {}, 3196 | "source": [ 3197 | "## Exercise " 3198 | ] 3199 | }, 3200 | { 3201 | "cell_type": "markdown", 3202 | "metadata": {}, 3203 | "source": [ 3204 | "Define a `@checked` macro that uses the `check_arithmetic` function from a previous exercise to wrap convert a piece of code that uses arithmetic operations into the corresponding code that uses the checked versions instead." 3205 | ] 3206 | }, 3207 | { 3208 | "cell_type": "markdown", 3209 | "metadata": {}, 3210 | "source": [ 3211 | "## Macro hygiene" 3212 | ] 3213 | }, 3214 | { 3215 | "cell_type": "markdown", 3216 | "metadata": {}, 3217 | "source": [ 3218 | "An important, but slippery topic with macros is so-called \"hygiene\". This refers to where variables are considered to be defined: inside the macro itself, or where the code that the macro creates is evaluated. We unfortunately do not have time to give this topic a detailed discussion. \n", 3219 | "\n", 3220 | "Basically, we need to allow variables to \"escape\" from the environment of the macro into the surrounding code, when the code returned by a macro refers to these external variables, rather than variables internal to the macro. This is done using `esc`; see the corresponding [Julia documentation](http://docs.julialang.org/en/release-0.4/manual/metaprogramming/#hygiene), and perhaps books on Lisp." 3221 | ] 3222 | } 3223 | ], 3224 | "metadata": { 3225 | "kernelspec": { 3226 | "display_name": "Julia 0.4.6", 3227 | "language": "julia", 3228 | "name": "julia-0.4" 3229 | }, 3230 | "language_info": { 3231 | "file_extension": ".jl", 3232 | "mimetype": "application/julia", 3233 | "name": "julia", 3234 | "version": "0.4.6" 3235 | }, 3236 | "widgets": { 3237 | "state": {}, 3238 | "version": "1.1.2" 3239 | } 3240 | }, 3241 | "nbformat": 4, 3242 | "nbformat_minor": 0 3243 | } 3244 | -------------------------------------------------------------------------------- /live_versions/2. Metaprogramming.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Introduction to metaprogramming: \"Code that creates code\" " 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "Julia has strong **metaprogramming** capabilities. What does this mean?\n", 15 | "\n", 16 | "> **meta**: something on a higher level\n", 17 | "\n", 18 | "**metaprogramming** = \"higher-level programming\"\n", 19 | "\n", 20 | "i.e. writing code (a program) to manipulate not data, but code (that itself manipulates data)\n" 21 | ] 22 | }, 23 | { 24 | "cell_type": "markdown", 25 | "metadata": {}, 26 | "source": [ 27 | "## Motivating example\n", 28 | "\n", 29 | "Metaprogramming has many different uses, several of which we will explore. It is often a way to add, or change, the syntax of Julia, to write a \"domain-specific language\" (DSL), that converts a simple syntax (but that is not standard Julia syntax) into true Julia code.\n", 30 | "\n", 31 | "As a motivating example, we might like to be able to write" 32 | ] 33 | }, 34 | { 35 | "cell_type": "code", 36 | "execution_count": 2, 37 | "metadata": { 38 | "collapsed": false 39 | }, 40 | "outputs": [ 41 | { 42 | "ename": "LoadError", 43 | "evalue": "LoadError: UndefVarError: ∑_ not defined\nwhile loading In[2], in expression starting on line 1", 44 | "output_type": "error", 45 | "traceback": [ 46 | "LoadError: UndefVarError: ∑_ not defined\nwhile loading In[2], in expression starting on line 1", 47 | "" 48 | ] 49 | } 50 | ], 51 | "source": [ 52 | "∑_{i ≠ j}, 1/(λ_i - λ_j) # NOT JULIA SYNTAX!" 53 | ] 54 | }, 55 | { 56 | "cell_type": "markdown", 57 | "metadata": {}, 58 | "source": [ 59 | "and have it *automatically converted* into something like" 60 | ] 61 | }, 62 | { 63 | "cell_type": "code", 64 | "execution_count": null, 65 | "metadata": { 66 | "collapsed": false 67 | }, 68 | "outputs": [], 69 | "source": [ 70 | "total = zero(λ[1])\n", 71 | "\n", 72 | "for i in 1:N\n", 73 | " j == i && continue\n", 74 | " \n", 75 | " total += 1 / (λ[i] - λ[j])\n", 76 | "end\n", 77 | "\n", 78 | "total" 79 | ] 80 | }, 81 | { 82 | "cell_type": "markdown", 83 | "metadata": {}, 84 | "source": [ 85 | "## A simpler example " 86 | ] 87 | }, 88 | { 89 | "cell_type": "markdown", 90 | "metadata": {}, 91 | "source": [ 92 | "This is too hard [**exercise**: write this functionality and make it into a package!], let's start out with a simpler example: Wilkinson-type polynomials.\n", 93 | "The [Wilkinson polynomial](https://en.wikipedia.org/wiki/Wilkinson's_polynomial) is\n", 94 | "\n", 95 | "$$p_{20}(x) := (x-1) \\cdot (x-2) \\cdot \\cdots \\cdot (x-20) = \\prod_{i=1}^{20} (x-i).$$\n", 96 | "\n", 97 | "[Polynomials like this are interesting, since the eigenvalues of the associated \"companion matrix\", which are used to find the roots of the polynomial, are very sensitive to perturbations of the coefficients of the polynomial.]\n", 98 | "\n", 99 | "Suppose we wish to define this polynomial in Julia. The simple way would be to write it out explicitly:" 100 | ] 101 | }, 102 | { 103 | "cell_type": "code", 104 | "execution_count": 3, 105 | "metadata": { 106 | "collapsed": false 107 | }, 108 | "outputs": [ 109 | { 110 | "data": { 111 | "text/plain": [ 112 | "p_5 (generic function with 1 method)" 113 | ] 114 | }, 115 | "execution_count": 3, 116 | "metadata": {}, 117 | "output_type": "execute_result" 118 | } 119 | ], 120 | "source": [ 121 | "p_5(x) = (x-1) * (x-2) * (x-3) * (x-4) * (x-5)" 122 | ] 123 | }, 124 | { 125 | "cell_type": "markdown", 126 | "metadata": {}, 127 | "source": [ 128 | "$p_{10}$ is already a pain to type by hand, $p_{20}$ more so, and $p_{100}$ is basically impossible. But this is just a case of repetition, and computers are designed for that. One possible definition uses a `for` loop:" 129 | ] 130 | }, 131 | { 132 | "cell_type": "code", 133 | "execution_count": 4, 134 | "metadata": { 135 | "collapsed": false 136 | }, 137 | "outputs": [ 138 | { 139 | "data": { 140 | "text/plain": [ 141 | "wilkinson (generic function with 1 method)" 142 | ] 143 | }, 144 | "execution_count": 4, 145 | "metadata": {}, 146 | "output_type": "execute_result" 147 | } 148 | ], 149 | "source": [ 150 | "function wilkinson(n, x)\n", 151 | " result = x - 1\n", 152 | " \n", 153 | " for i in 2:n\n", 154 | " result *= (x - i)\n", 155 | " end\n", 156 | " \n", 157 | " result\n", 158 | "end" 159 | ] 160 | }, 161 | { 162 | "cell_type": "markdown", 163 | "metadata": {}, 164 | "source": [ 165 | "We can use an anonymous function to define the function $p_n$:" 166 | ] 167 | }, 168 | { 169 | "cell_type": "code", 170 | "execution_count": 5, 171 | "metadata": { 172 | "collapsed": false 173 | }, 174 | "outputs": [ 175 | { 176 | "data": { 177 | "text/plain": [ 178 | "p (generic function with 1 method)" 179 | ] 180 | }, 181 | "execution_count": 5, 182 | "metadata": {}, 183 | "output_type": "execute_result" 184 | } 185 | ], 186 | "source": [ 187 | "p(n) = x -> wilkinson(n, x)" 188 | ] 189 | }, 190 | { 191 | "cell_type": "code", 192 | "execution_count": 6, 193 | "metadata": { 194 | "collapsed": false 195 | }, 196 | "outputs": [ 197 | { 198 | "data": { 199 | "text/plain": [ 200 | "-0.20790000000000022" 201 | ] 202 | }, 203 | "execution_count": 6, 204 | "metadata": {}, 205 | "output_type": "execute_result" 206 | } 207 | ], 208 | "source": [ 209 | "p(4)(3.1)" 210 | ] 211 | }, 212 | { 213 | "cell_type": "markdown", 214 | "metadata": {}, 215 | "source": [ 216 | "[In Julia 0.4, anonymous functions have a performance penalty, although they no longer do in 0.5.]\n", 217 | "\n", 218 | "It seems, though, that it should be possible to use the original definition of the function to write the equivalent of" 219 | ] 220 | }, 221 | { 222 | "cell_type": "code", 223 | "execution_count": null, 224 | "metadata": { 225 | "collapsed": false 226 | }, 227 | "outputs": [], 228 | "source": [ 229 | "p_5(x) = (x-1)*(x-2)*(x-3)*(x-4)*(x-5)" 230 | ] 231 | }, 232 | { 233 | "cell_type": "markdown", 234 | "metadata": {}, 235 | "source": [ 236 | "for $n=100$.\n", 237 | "In other languages, this would often be accomplished by manipulating strings that represent the \"surface syntax\", i.e. the string of characters that you would actually type, and then evaluate this string. However, in Julia\n", 238 | "**we never use strings for this**, since Julia has a way to **refer to Julia code objects within Julia**." 239 | ] 240 | }, 241 | { 242 | "cell_type": "markdown", 243 | "metadata": {}, 244 | "source": [ 245 | "## Expressions " 246 | ] 247 | }, 248 | { 249 | "cell_type": "markdown", 250 | "metadata": { 251 | "collapsed": false 252 | }, 253 | "source": [ 254 | "Let's take the simplest polynomial, that we write as `(x-1) * (x-2)`. We can view this as a piece of Julia code, called an **expression**, and can tell Julia this as follows:" 255 | ] 256 | }, 257 | { 258 | "cell_type": "code", 259 | "execution_count": 7, 260 | "metadata": { 261 | "collapsed": false 262 | }, 263 | "outputs": [ 264 | { 265 | "data": { 266 | "text/plain": [ 267 | "quote # In[7], line 2:\n", 268 | " (x - 1) * (x - 2)\n", 269 | "end" 270 | ] 271 | }, 272 | "execution_count": 7, 273 | "metadata": {}, 274 | "output_type": "execute_result" 275 | } 276 | ], 277 | "source": [ 278 | "ex = quote \n", 279 | " (x - 1) * (x - 2)\n", 280 | " end" 281 | ] 282 | }, 283 | { 284 | "cell_type": "markdown", 285 | "metadata": {}, 286 | "source": [ 287 | "For small pieces of code like this, an alternative syntax uses the `:( ... )` operator:" 288 | ] 289 | }, 290 | { 291 | "cell_type": "code", 292 | "execution_count": 8, 293 | "metadata": { 294 | "collapsed": false 295 | }, 296 | "outputs": [ 297 | { 298 | "data": { 299 | "text/plain": [ 300 | ":((x - 1) * (x - 2))" 301 | ] 302 | }, 303 | "execution_count": 8, 304 | "metadata": {}, 305 | "output_type": "execute_result" 306 | } 307 | ], 308 | "source": [ 309 | "ex = :( (x-1) * (x-2) )" 310 | ] 311 | }, 312 | { 313 | "cell_type": "markdown", 314 | "metadata": {}, 315 | "source": [ 316 | "What does Julia think this is?" 317 | ] 318 | }, 319 | { 320 | "cell_type": "code", 321 | "execution_count": 9, 322 | "metadata": { 323 | "collapsed": false 324 | }, 325 | "outputs": [ 326 | { 327 | "data": { 328 | "text/plain": [ 329 | "Expr" 330 | ] 331 | }, 332 | "execution_count": 9, 333 | "metadata": {}, 334 | "output_type": "execute_result" 335 | } 336 | ], 337 | "source": [ 338 | "typeof(ex)" 339 | ] 340 | }, 341 | { 342 | "cell_type": "markdown", 343 | "metadata": {}, 344 | "source": [ 345 | "We see that Julia has a type `Expr` that represents an **expression object**, that we can think of as a \"Julia code snippet\"." 346 | ] 347 | }, 348 | { 349 | "cell_type": "markdown", 350 | "metadata": {}, 351 | "source": [ 352 | "We would like to be able to execute this code; we can do so with `eval` (\"evaluate\"):" 353 | ] 354 | }, 355 | { 356 | "cell_type": "code", 357 | "execution_count": 10, 358 | "metadata": { 359 | "collapsed": false 360 | }, 361 | "outputs": [ 362 | { 363 | "data": { 364 | "text/plain": [ 365 | ":((x - 1) * (x - 2))" 366 | ] 367 | }, 368 | "execution_count": 10, 369 | "metadata": {}, 370 | "output_type": "execute_result" 371 | } 372 | ], 373 | "source": [ 374 | "ex" 375 | ] 376 | }, 377 | { 378 | "cell_type": "code", 379 | "execution_count": 11, 380 | "metadata": { 381 | "collapsed": false 382 | }, 383 | "outputs": [ 384 | { 385 | "ename": "LoadError", 386 | "evalue": "LoadError: UndefVarError: x not defined\nwhile loading In[11], in expression starting on line 1", 387 | "output_type": "error", 388 | "traceback": [ 389 | "LoadError: UndefVarError: x not defined\nwhile loading In[11], in expression starting on line 1", 390 | "" 391 | ] 392 | } 393 | ], 394 | "source": [ 395 | "eval(ex)" 396 | ] 397 | }, 398 | { 399 | "cell_type": "markdown", 400 | "metadata": {}, 401 | "source": [ 402 | "We see that `x` is not defined, since Julia is trying to do the same as if we had written the following straight at the prompt" 403 | ] 404 | }, 405 | { 406 | "cell_type": "code", 407 | "execution_count": 12, 408 | "metadata": { 409 | "collapsed": false 410 | }, 411 | "outputs": [ 412 | { 413 | "ename": "LoadError", 414 | "evalue": "LoadError: UndefVarError: x not defined\nwhile loading In[12], in expression starting on line 1", 415 | "output_type": "error", 416 | "traceback": [ 417 | "LoadError: UndefVarError: x not defined\nwhile loading In[12], in expression starting on line 1", 418 | "" 419 | ] 420 | } 421 | ], 422 | "source": [ 423 | "(x-1) * (x-2)" 424 | ] 425 | }, 426 | { 427 | "cell_type": "markdown", 428 | "metadata": {}, 429 | "source": [ 430 | "If we give `x` a value first, then it works:" 431 | ] 432 | }, 433 | { 434 | "cell_type": "code", 435 | "execution_count": 13, 436 | "metadata": { 437 | "collapsed": false 438 | }, 439 | "outputs": [ 440 | { 441 | "data": { 442 | "text/plain": [ 443 | "3.75" 444 | ] 445 | }, 446 | "execution_count": 13, 447 | "metadata": {}, 448 | "output_type": "execute_result" 449 | } 450 | ], 451 | "source": [ 452 | "x = 3.5\n", 453 | "(x-1) * (x-2)" 454 | ] 455 | }, 456 | { 457 | "cell_type": "markdown", 458 | "metadata": {}, 459 | "source": [ 460 | "And so hence does the `eval`:" 461 | ] 462 | }, 463 | { 464 | "cell_type": "code", 465 | "execution_count": 14, 466 | "metadata": { 467 | "collapsed": false 468 | }, 469 | "outputs": [ 470 | { 471 | "data": { 472 | "text/plain": [ 473 | "3.75" 474 | ] 475 | }, 476 | "execution_count": 14, 477 | "metadata": {}, 478 | "output_type": "execute_result" 479 | } 480 | ], 481 | "source": [ 482 | "eval(ex)" 483 | ] 484 | }, 485 | { 486 | "cell_type": "markdown", 487 | "metadata": {}, 488 | "source": [ 489 | "For example, we can try to write a simple formula evaluator:" 490 | ] 491 | }, 492 | { 493 | "cell_type": "code", 494 | "execution_count": 20, 495 | "metadata": { 496 | "collapsed": false 497 | }, 498 | "outputs": [ 499 | { 500 | "data": { 501 | "text/plain": [ 502 | "\"3x^2 + 4x - 3sin(x)\"" 503 | ] 504 | }, 505 | "execution_count": 20, 506 | "metadata": {}, 507 | "output_type": "execute_result" 508 | } 509 | ], 510 | "source": [ 511 | "formula = \"3x^2 + 4x - 3sin(x)\"" 512 | ] 513 | }, 514 | { 515 | "cell_type": "code", 516 | "execution_count": 16, 517 | "metadata": { 518 | "collapsed": false 519 | }, 520 | "outputs": [ 521 | { 522 | "data": { 523 | "text/plain": [ 524 | "\"3x\"" 525 | ] 526 | }, 527 | "execution_count": 16, 528 | "metadata": {}, 529 | "output_type": "execute_result" 530 | } 531 | ], 532 | "source": [ 533 | "eval(formula)" 534 | ] 535 | }, 536 | { 537 | "cell_type": "markdown", 538 | "metadata": {}, 539 | "source": [ 540 | "This does not do what we expect, since we have a string, not a Julia expression:" 541 | ] 542 | }, 543 | { 544 | "cell_type": "code", 545 | "execution_count": 17, 546 | "metadata": { 547 | "collapsed": false 548 | }, 549 | "outputs": [ 550 | { 551 | "data": { 552 | "text/plain": [ 553 | "ASCIIString" 554 | ] 555 | }, 556 | "execution_count": 17, 557 | "metadata": {}, 558 | "output_type": "execute_result" 559 | } 560 | ], 561 | "source": [ 562 | "typeof(formula)" 563 | ] 564 | }, 565 | { 566 | "cell_type": "markdown", 567 | "metadata": {}, 568 | "source": [ 569 | "To convert this string into an expression, we use `parse`:" 570 | ] 571 | }, 572 | { 573 | "cell_type": "code", 574 | "execution_count": 18, 575 | "metadata": { 576 | "collapsed": false 577 | }, 578 | "outputs": [ 579 | { 580 | "data": { 581 | "text/plain": [ 582 | ":(3x)" 583 | ] 584 | }, 585 | "execution_count": 18, 586 | "metadata": {}, 587 | "output_type": "execute_result" 588 | } 589 | ], 590 | "source": [ 591 | "formula2 = parse(formula)" 592 | ] 593 | }, 594 | { 595 | "cell_type": "code", 596 | "execution_count": 19, 597 | "metadata": { 598 | "collapsed": false 599 | }, 600 | "outputs": [ 601 | { 602 | "data": { 603 | "text/plain": [ 604 | "10.5" 605 | ] 606 | }, 607 | "execution_count": 19, 608 | "metadata": {}, 609 | "output_type": "execute_result" 610 | } 611 | ], 612 | "source": [ 613 | "eval(formula2)" 614 | ] 615 | }, 616 | { 617 | "cell_type": "code", 618 | "execution_count": 21, 619 | "metadata": { 620 | "collapsed": false 621 | }, 622 | "outputs": [ 623 | { 624 | "data": { 625 | "text/plain": [ 626 | ":((3 * x ^ 2 + 4x) - 3 * sin(x))" 627 | ] 628 | }, 629 | "execution_count": 21, 630 | "metadata": {}, 631 | "output_type": "execute_result" 632 | } 633 | ], 634 | "source": [ 635 | "ex = parse(\"3x^2 + 4x - 3sin(x)\")" 636 | ] 637 | }, 638 | { 639 | "cell_type": "code", 640 | "execution_count": 22, 641 | "metadata": { 642 | "collapsed": false 643 | }, 644 | "outputs": [ 645 | { 646 | "data": { 647 | "text/plain": [ 648 | "51.80234968306886" 649 | ] 650 | }, 651 | "execution_count": 22, 652 | "metadata": {}, 653 | "output_type": "execute_result" 654 | } 655 | ], 656 | "source": [ 657 | "eval(ex)" 658 | ] 659 | }, 660 | { 661 | "cell_type": "code", 662 | "execution_count": null, 663 | "metadata": { 664 | "collapsed": true 665 | }, 666 | "outputs": [], 667 | "source": [ 668 | "f(x) = 3x^2 + 4x - 3sin(x)" 669 | ] 670 | }, 671 | { 672 | "cell_type": "markdown", 673 | "metadata": {}, 674 | "source": [ 675 | "Note that the really hard work, that of parsing the expression, i.e. converting it into an internal representation in terms of a tree, has already been done for us by Julia. It is thus, happily, usually not necessary for us to write our own parser; we just leverage Julia's own! This is one of many ways in which Julia minimises the work that we need to do, compared to other languages." 676 | ] 677 | }, 678 | { 679 | "cell_type": "markdown", 680 | "metadata": {}, 681 | "source": [ 682 | "## Structure of `Expr`essions " 683 | ] 684 | }, 685 | { 686 | "cell_type": "markdown", 687 | "metadata": {}, 688 | "source": [ 689 | "An expression object of type `Expr` has an internal structure that corresponds to the **abstract syntax tree** (AST) of the expression, i.e. a tree, whose nodes are operations, and whose children are subtrees.\n", 690 | "We can see this in two ways, using `dump`:" 691 | ] 692 | }, 693 | { 694 | "cell_type": "code", 695 | "execution_count": 23, 696 | "metadata": { 697 | "collapsed": false 698 | }, 699 | "outputs": [ 700 | { 701 | "name": "stdout", 702 | "output_type": "stream", 703 | "text": [ 704 | "Expr \n", 705 | " head: Symbol call\n", 706 | " args: Array(Any,(3,))\n", 707 | " 1: Symbol -\n", 708 | " 2: Expr \n", 709 | " head: Symbol call\n", 710 | " args: Array(Any,(3,))\n", 711 | " 1: Symbol +\n", 712 | " 2: Expr \n", 713 | " head: Symbol call\n", 714 | " args: Array(Any,(3,))\n", 715 | " typ: Any\n", 716 | " 3: Expr \n", 717 | " head: Symbol call\n", 718 | " args: Array(Any,(3,))\n", 719 | " typ: Any\n", 720 | " typ: Any\n", 721 | " 3: Expr \n", 722 | " head: Symbol call\n", 723 | " args: Array(Any,(3,))\n", 724 | " 1: Symbol *\n", 725 | " 2: Int64 3\n", 726 | " 3: Expr \n", 727 | " head: Symbol call\n", 728 | " args: Array(Any,(2,))\n", 729 | " typ: Any\n", 730 | " typ: Any\n", 731 | " typ: Any\n" 732 | ] 733 | } 734 | ], 735 | "source": [ 736 | "dump(ex)" 737 | ] 738 | }, 739 | { 740 | "cell_type": "markdown", 741 | "metadata": {}, 742 | "source": [ 743 | "which shows everything [up to a pre-determined depth] in detail, or " 744 | ] 745 | }, 746 | { 747 | "cell_type": "code", 748 | "execution_count": 24, 749 | "metadata": { 750 | "collapsed": false 751 | }, 752 | "outputs": [ 753 | { 754 | "name": "stdout", 755 | "output_type": "stream", 756 | "text": [ 757 | "(:call, :-, (:call, :+, (:call, :*, 3, (:call, :^, :x, 2)), (:call, :*, 4, :x)), (:call, :*, 3, (:call, :sin, :x)))" 758 | ] 759 | } 760 | ], 761 | "source": [ 762 | "Meta.show_sexpr(ex)" 763 | ] 764 | }, 765 | { 766 | "cell_type": "code", 767 | "execution_count": 25, 768 | "metadata": { 769 | "collapsed": false 770 | }, 771 | "outputs": [ 772 | { 773 | "data": { 774 | "text/plain": [ 775 | ":((x - 1) * (x - 2))" 776 | ] 777 | }, 778 | "execution_count": 25, 779 | "metadata": {}, 780 | "output_type": "execute_result" 781 | } 782 | ], 783 | "source": [ 784 | "ex = :((x-1) * (x-2))" 785 | ] 786 | }, 787 | { 788 | "cell_type": "code", 789 | "execution_count": 26, 790 | "metadata": { 791 | "collapsed": false 792 | }, 793 | "outputs": [ 794 | { 795 | "name": "stdout", 796 | "output_type": "stream", 797 | "text": [ 798 | "Expr \n", 799 | " head: Symbol call\n", 800 | " args: Array(Any,(3,))\n", 801 | " 1: Symbol *\n", 802 | " 2: Expr \n", 803 | " head: Symbol call\n", 804 | " args: Array(Any,(3,))\n", 805 | " 1: Symbol -\n", 806 | " 2: Symbol x\n", 807 | " 3: Int64 1\n", 808 | " typ: Any\n", 809 | " 3: Expr \n", 810 | " head: Symbol call\n", 811 | " args: Array(Any,(3,))\n", 812 | " 1: Symbol -\n", 813 | " 2: Symbol x\n", 814 | " 3: Int64 2\n", 815 | " typ: Any\n", 816 | " typ: Any\n" 817 | ] 818 | } 819 | ], 820 | "source": [ 821 | "dump(ex)" 822 | ] 823 | }, 824 | { 825 | "cell_type": "markdown", 826 | "metadata": {}, 827 | "source": [ 828 | "which gives a compact version (similar to an [S-expression](https://en.wikipedia.org/wiki/S-expression) in Lisp, whence the name of the function, which is in the `Meta` submodule in `Base`). They both give representations of the syntax tree describing the hierarchical structure of the expression.\n", 829 | "\n", 830 | "The point is that since an `Expr`ession is a **standard Julia objects**, we can **use standard Julia commands to manipulate it**! \n", 831 | "\n", 832 | "As usual, doing `ex.` gives a list of the fields in the type `Expr`, or we can use `fieldnames(ex)`:" 833 | ] 834 | }, 835 | { 836 | "cell_type": "code", 837 | "execution_count": 27, 838 | "metadata": { 839 | "collapsed": false 840 | }, 841 | "outputs": [ 842 | { 843 | "data": { 844 | "text/plain": [ 845 | "3-element Array{Symbol,1}:\n", 846 | " :head\n", 847 | " :args\n", 848 | " :typ " 849 | ] 850 | }, 851 | "execution_count": 27, 852 | "metadata": {}, 853 | "output_type": "execute_result" 854 | } 855 | ], 856 | "source": [ 857 | "fieldnames(ex)" 858 | ] 859 | }, 860 | { 861 | "cell_type": "markdown", 862 | "metadata": {}, 863 | "source": [ 864 | "and then examine the fields:" 865 | ] 866 | }, 867 | { 868 | "cell_type": "code", 869 | "execution_count": 28, 870 | "metadata": { 871 | "collapsed": false 872 | }, 873 | "outputs": [ 874 | { 875 | "data": { 876 | "text/plain": [ 877 | ":call" 878 | ] 879 | }, 880 | "execution_count": 28, 881 | "metadata": {}, 882 | "output_type": "execute_result" 883 | } 884 | ], 885 | "source": [ 886 | "ex.head" 887 | ] 888 | }, 889 | { 890 | "cell_type": "markdown", 891 | "metadata": {}, 892 | "source": [ 893 | "This tells us that the expression represents a function call, whose arguments are the following:" 894 | ] 895 | }, 896 | { 897 | "cell_type": "code", 898 | "execution_count": 29, 899 | "metadata": { 900 | "collapsed": false 901 | }, 902 | "outputs": [ 903 | { 904 | "data": { 905 | "text/plain": [ 906 | "3-element Array{Any,1}:\n", 907 | " :* \n", 908 | " :(x - 1)\n", 909 | " :(x - 2)" 910 | ] 911 | }, 912 | "execution_count": 29, 913 | "metadata": {}, 914 | "output_type": "execute_result" 915 | } 916 | ], 917 | "source": [ 918 | "ex.args" 919 | ] 920 | }, 921 | { 922 | "cell_type": "markdown", 923 | "metadata": {}, 924 | "source": [ 925 | "[`ex.typ` is not useful for the user.]" 926 | ] 927 | }, 928 | { 929 | "cell_type": "markdown", 930 | "metadata": {}, 931 | "source": [ 932 | "The first element of the array `ex.args` is the function to be called:" 933 | ] 934 | }, 935 | { 936 | "cell_type": "code", 937 | "execution_count": 30, 938 | "metadata": { 939 | "collapsed": false 940 | }, 941 | "outputs": [ 942 | { 943 | "data": { 944 | "text/plain": [ 945 | ":*" 946 | ] 947 | }, 948 | "execution_count": 30, 949 | "metadata": {}, 950 | "output_type": "execute_result" 951 | } 952 | ], 953 | "source": [ 954 | "ex.args[1]" 955 | ] 956 | }, 957 | { 958 | "cell_type": "markdown", 959 | "metadata": {}, 960 | "source": [ 961 | "and the second element is" 962 | ] 963 | }, 964 | { 965 | "cell_type": "code", 966 | "execution_count": 31, 967 | "metadata": { 968 | "collapsed": false 969 | }, 970 | "outputs": [ 971 | { 972 | "data": { 973 | "text/plain": [ 974 | ":(x - 1)" 975 | ] 976 | }, 977 | "execution_count": 31, 978 | "metadata": {}, 979 | "output_type": "execute_result" 980 | } 981 | ], 982 | "source": [ 983 | "ex.args[2]" 984 | ] 985 | }, 986 | { 987 | "cell_type": "markdown", 988 | "metadata": {}, 989 | "source": [ 990 | "that is, it is itself an `Expr`ession:" 991 | ] 992 | }, 993 | { 994 | "cell_type": "code", 995 | "execution_count": 32, 996 | "metadata": { 997 | "collapsed": false 998 | }, 999 | "outputs": [ 1000 | { 1001 | "data": { 1002 | "text/plain": [ 1003 | "Expr" 1004 | ] 1005 | }, 1006 | "execution_count": 32, 1007 | "metadata": {}, 1008 | "output_type": "execute_result" 1009 | } 1010 | ], 1011 | "source": [ 1012 | "typeof(ans)" 1013 | ] 1014 | }, 1015 | { 1016 | "cell_type": "markdown", 1017 | "metadata": {}, 1018 | "source": [ 1019 | "Thus the structure of `Expr`essions is fundamentally recursive, and so lends itself naturally to recursive algorithms." 1020 | ] 1021 | }, 1022 | { 1023 | "cell_type": "markdown", 1024 | "metadata": {}, 1025 | "source": [ 1026 | "We can dive, in turn, into the structure of each element:" 1027 | ] 1028 | }, 1029 | { 1030 | "cell_type": "code", 1031 | "execution_count": 36, 1032 | "metadata": { 1033 | "collapsed": false 1034 | }, 1035 | "outputs": [ 1036 | { 1037 | "data": { 1038 | "text/plain": [ 1039 | ":(=)" 1040 | ] 1041 | }, 1042 | "execution_count": 36, 1043 | "metadata": {}, 1044 | "output_type": "execute_result" 1045 | } 1046 | ], 1047 | "source": [ 1048 | "ex2 = :(a = b + c)\n", 1049 | "ex2.head" 1050 | ] 1051 | }, 1052 | { 1053 | "cell_type": "code", 1054 | "execution_count": 34, 1055 | "metadata": { 1056 | "collapsed": false 1057 | }, 1058 | "outputs": [ 1059 | { 1060 | "data": { 1061 | "text/plain": [ 1062 | ":call" 1063 | ] 1064 | }, 1065 | "execution_count": 34, 1066 | "metadata": {}, 1067 | "output_type": "execute_result" 1068 | } 1069 | ], 1070 | "source": [ 1071 | "ex.head" 1072 | ] 1073 | }, 1074 | { 1075 | "cell_type": "code", 1076 | "execution_count": 37, 1077 | "metadata": { 1078 | "collapsed": false 1079 | }, 1080 | "outputs": [ 1081 | { 1082 | "data": { 1083 | "text/plain": [ 1084 | ":(x - 1)" 1085 | ] 1086 | }, 1087 | "execution_count": 37, 1088 | "metadata": {}, 1089 | "output_type": "execute_result" 1090 | } 1091 | ], 1092 | "source": [ 1093 | "ex.args[2]" 1094 | ] 1095 | }, 1096 | { 1097 | "cell_type": "code", 1098 | "execution_count": 39, 1099 | "metadata": { 1100 | "collapsed": false 1101 | }, 1102 | "outputs": [ 1103 | { 1104 | "data": { 1105 | "text/plain": [ 1106 | ":-" 1107 | ] 1108 | }, 1109 | "execution_count": 39, 1110 | "metadata": {}, 1111 | "output_type": "execute_result" 1112 | } 1113 | ], 1114 | "source": [ 1115 | "ex.args[2].args[1]" 1116 | ] 1117 | }, 1118 | { 1119 | "cell_type": "code", 1120 | "execution_count": 40, 1121 | "metadata": { 1122 | "collapsed": false 1123 | }, 1124 | "outputs": [ 1125 | { 1126 | "data": { 1127 | "text/plain": [ 1128 | ":x" 1129 | ] 1130 | }, 1131 | "execution_count": 40, 1132 | "metadata": {}, 1133 | "output_type": "execute_result" 1134 | } 1135 | ], 1136 | "source": [ 1137 | "ex.args[2].args[2]" 1138 | ] 1139 | }, 1140 | { 1141 | "cell_type": "markdown", 1142 | "metadata": {}, 1143 | "source": [ 1144 | "What is this `:x`?" 1145 | ] 1146 | }, 1147 | { 1148 | "cell_type": "code", 1149 | "execution_count": 41, 1150 | "metadata": { 1151 | "collapsed": false 1152 | }, 1153 | "outputs": [ 1154 | { 1155 | "data": { 1156 | "text/plain": [ 1157 | "Symbol" 1158 | ] 1159 | }, 1160 | "execution_count": 41, 1161 | "metadata": {}, 1162 | "output_type": "execute_result" 1163 | } 1164 | ], 1165 | "source": [ 1166 | "typeof(ex.args[2].args[2])" 1167 | ] 1168 | }, 1169 | { 1170 | "cell_type": "markdown", 1171 | "metadata": {}, 1172 | "source": [ 1173 | "We see that it has a special `Symbol` type, which represents the atoms (smallest parts) of an expression." 1174 | ] 1175 | }, 1176 | { 1177 | "cell_type": "markdown", 1178 | "metadata": {}, 1179 | "source": [ 1180 | "## Modifying expressions " 1181 | ] 1182 | }, 1183 | { 1184 | "cell_type": "markdown", 1185 | "metadata": {}, 1186 | "source": [ 1187 | "Now, what happens if we **modify** this? Let's make a copy of the expression first so that we can compare the two." 1188 | ] 1189 | }, 1190 | { 1191 | "cell_type": "code", 1192 | "execution_count": 50, 1193 | "metadata": { 1194 | "collapsed": false 1195 | }, 1196 | "outputs": [ 1197 | { 1198 | "data": { 1199 | "text/plain": [ 1200 | ":((x - 1) * (x - 2))" 1201 | ] 1202 | }, 1203 | "execution_count": 50, 1204 | "metadata": {}, 1205 | "output_type": "execute_result" 1206 | } 1207 | ], 1208 | "source": [ 1209 | "ex = :((x - 1) * (x - 2))" 1210 | ] 1211 | }, 1212 | { 1213 | "cell_type": "code", 1214 | "execution_count": 51, 1215 | "metadata": { 1216 | "collapsed": false 1217 | }, 1218 | "outputs": [ 1219 | { 1220 | "data": { 1221 | "text/plain": [ 1222 | ":((x - 1) * (x - 2))" 1223 | ] 1224 | }, 1225 | "execution_count": 51, 1226 | "metadata": {}, 1227 | "output_type": "execute_result" 1228 | } 1229 | ], 1230 | "source": [ 1231 | "ex2 = copy(ex)" 1232 | ] 1233 | }, 1234 | { 1235 | "cell_type": "code", 1236 | "execution_count": 52, 1237 | "metadata": { 1238 | "collapsed": false 1239 | }, 1240 | "outputs": [ 1241 | { 1242 | "data": { 1243 | "text/plain": [ 1244 | ":z" 1245 | ] 1246 | }, 1247 | "execution_count": 52, 1248 | "metadata": {}, 1249 | "output_type": "execute_result" 1250 | } 1251 | ], 1252 | "source": [ 1253 | "ex2.args[2] = :z" 1254 | ] 1255 | }, 1256 | { 1257 | "cell_type": "code", 1258 | "execution_count": 53, 1259 | "metadata": { 1260 | "collapsed": false 1261 | }, 1262 | "outputs": [ 1263 | { 1264 | "data": { 1265 | "text/plain": [ 1266 | ":(z * (x - 2))" 1267 | ] 1268 | }, 1269 | "execution_count": 53, 1270 | "metadata": {}, 1271 | "output_type": "execute_result" 1272 | } 1273 | ], 1274 | "source": [ 1275 | "ex2" 1276 | ] 1277 | }, 1278 | { 1279 | "cell_type": "code", 1280 | "execution_count": 54, 1281 | "metadata": { 1282 | "collapsed": false 1283 | }, 1284 | "outputs": [ 1285 | { 1286 | "data": { 1287 | "text/plain": [ 1288 | ":((x - 1) * (x - 2))" 1289 | ] 1290 | }, 1291 | "execution_count": 54, 1292 | "metadata": {}, 1293 | "output_type": "execute_result" 1294 | } 1295 | ], 1296 | "source": [ 1297 | "ex" 1298 | ] 1299 | }, 1300 | { 1301 | "cell_type": "code", 1302 | "execution_count": 43, 1303 | "metadata": { 1304 | "collapsed": false 1305 | }, 1306 | "outputs": [ 1307 | { 1308 | "data": { 1309 | "text/plain": [ 1310 | ":x" 1311 | ] 1312 | }, 1313 | "execution_count": 43, 1314 | "metadata": {}, 1315 | "output_type": "execute_result" 1316 | } 1317 | ], 1318 | "source": [ 1319 | "ex2.args[2].args[2]" 1320 | ] 1321 | }, 1322 | { 1323 | "cell_type": "code", 1324 | "execution_count": 44, 1325 | "metadata": { 1326 | "collapsed": false 1327 | }, 1328 | "outputs": [ 1329 | { 1330 | "data": { 1331 | "text/plain": [ 1332 | ":z" 1333 | ] 1334 | }, 1335 | "execution_count": 44, 1336 | "metadata": {}, 1337 | "output_type": "execute_result" 1338 | } 1339 | ], 1340 | "source": [ 1341 | "ex2.args[2].args[2] = :z" 1342 | ] 1343 | }, 1344 | { 1345 | "cell_type": "markdown", 1346 | "metadata": {}, 1347 | "source": [ 1348 | "We have changed something inside the object `ex2`, so let's look at it:" 1349 | ] 1350 | }, 1351 | { 1352 | "cell_type": "code", 1353 | "execution_count": 45, 1354 | "metadata": { 1355 | "collapsed": false 1356 | }, 1357 | "outputs": [ 1358 | { 1359 | "data": { 1360 | "text/plain": [ 1361 | ":((z - 1) * (x - 2))" 1362 | ] 1363 | }, 1364 | "execution_count": 45, 1365 | "metadata": {}, 1366 | "output_type": "execute_result" 1367 | } 1368 | ], 1369 | "source": [ 1370 | "ex2" 1371 | ] 1372 | }, 1373 | { 1374 | "cell_type": "code", 1375 | "execution_count": null, 1376 | "metadata": { 1377 | "collapsed": false 1378 | }, 1379 | "outputs": [], 1380 | "source": [ 1381 | "ex" 1382 | ] 1383 | }, 1384 | { 1385 | "cell_type": "markdown", 1386 | "metadata": {}, 1387 | "source": [ 1388 | "The original expression has, indeed, changed! That is, we have taken a piece of Julia code, and used Julia itself to manipulate it into a different piece of Julia code. This is one of the simplest examples of metaprogramming." 1389 | ] 1390 | }, 1391 | { 1392 | "cell_type": "markdown", 1393 | "metadata": {}, 1394 | "source": [ 1395 | "Now we can define `x` and `z` and evaluate the expressions:" 1396 | ] 1397 | }, 1398 | { 1399 | "cell_type": "code", 1400 | "execution_count": 55, 1401 | "metadata": { 1402 | "collapsed": false 1403 | }, 1404 | "outputs": [ 1405 | { 1406 | "data": { 1407 | "text/plain": [ 1408 | "4.5" 1409 | ] 1410 | }, 1411 | "execution_count": 55, 1412 | "metadata": {}, 1413 | "output_type": "execute_result" 1414 | } 1415 | ], 1416 | "source": [ 1417 | "x = 3.5; z = 4.5" 1418 | ] 1419 | }, 1420 | { 1421 | "cell_type": "code", 1422 | "execution_count": 56, 1423 | "metadata": { 1424 | "collapsed": false 1425 | }, 1426 | "outputs": [ 1427 | { 1428 | "data": { 1429 | "text/plain": [ 1430 | "(3.75,6.75)" 1431 | ] 1432 | }, 1433 | "execution_count": 56, 1434 | "metadata": {}, 1435 | "output_type": "execute_result" 1436 | } 1437 | ], 1438 | "source": [ 1439 | "eval(ex), eval(ex2)" 1440 | ] 1441 | }, 1442 | { 1443 | "cell_type": "markdown", 1444 | "metadata": {}, 1445 | "source": [ 1446 | "## Walking a syntax tree " 1447 | ] 1448 | }, 1449 | { 1450 | "cell_type": "markdown", 1451 | "metadata": {}, 1452 | "source": [ 1453 | "What if we wanted to replace *all* of the `x`s in a given expression. The problem is that they may be buried arbitrarily deeply in the nested syntax tree. So we have to traverse the tree, in order to visit each piece of it and check if it is an `:x`." 1454 | ] 1455 | }, 1456 | { 1457 | "cell_type": "markdown", 1458 | "metadata": {}, 1459 | "source": [ 1460 | "**Exercise**: Write a function that takes an expression object and replaces **all** of the `:x`s by `:z`s." 1461 | ] 1462 | }, 1463 | { 1464 | "cell_type": "code", 1465 | "execution_count": 59, 1466 | "metadata": { 1467 | "collapsed": false 1468 | }, 1469 | "outputs": [ 1470 | { 1471 | "data": { 1472 | "text/plain": [ 1473 | "3-element Array{Tuple{Int64,Int64},1}:\n", 1474 | " (1,4)\n", 1475 | " (2,5)\n", 1476 | " (3,6)" 1477 | ] 1478 | }, 1479 | "execution_count": 59, 1480 | "metadata": {}, 1481 | "output_type": "execute_result" 1482 | } 1483 | ], 1484 | "source": [ 1485 | "v = [4, 5, 6]\n", 1486 | "collect(enumerate(v))" 1487 | ] 1488 | }, 1489 | { 1490 | "cell_type": "code", 1491 | "execution_count": 66, 1492 | "metadata": { 1493 | "collapsed": false 1494 | }, 1495 | "outputs": [ 1496 | { 1497 | "data": { 1498 | "text/plain": [ 1499 | "traverse! (generic function with 1 method)" 1500 | ] 1501 | }, 1502 | "execution_count": 66, 1503 | "metadata": {}, 1504 | "output_type": "execute_result" 1505 | } 1506 | ], 1507 | "source": [ 1508 | "function traverse!(ex::Expr)\n", 1509 | " for i in 1:length(ex.args) # enumerate(ex.args)\n", 1510 | " \n", 1511 | " if ex.args[i] == :x\n", 1512 | " #ex.args[i] = :z # we cannot use arg here, since it is a copy, not a reference\n", 1513 | " ex.args[i] = :z\n", 1514 | " end\n", 1515 | " \n", 1516 | " if isa(ex.args[i], Expr)\n", 1517 | " traverse!(ex.args[i])\n", 1518 | " end\n", 1519 | " end\n", 1520 | "end" 1521 | ] 1522 | }, 1523 | { 1524 | "cell_type": "code", 1525 | "execution_count": 67, 1526 | "metadata": { 1527 | "collapsed": false 1528 | }, 1529 | "outputs": [ 1530 | { 1531 | "data": { 1532 | "text/plain": [ 1533 | ":((x - 1) * (x - 2))" 1534 | ] 1535 | }, 1536 | "execution_count": 67, 1537 | "metadata": {}, 1538 | "output_type": "execute_result" 1539 | } 1540 | ], 1541 | "source": [ 1542 | "ex = :((x-1)*(x-2))" 1543 | ] 1544 | }, 1545 | { 1546 | "cell_type": "code", 1547 | "execution_count": 68, 1548 | "metadata": { 1549 | "collapsed": false 1550 | }, 1551 | "outputs": [ 1552 | { 1553 | "data": { 1554 | "text/plain": [ 1555 | ":((z - 1) * (z - 2))" 1556 | ] 1557 | }, 1558 | "execution_count": 68, 1559 | "metadata": {}, 1560 | "output_type": "execute_result" 1561 | } 1562 | ], 1563 | "source": [ 1564 | "traverse!(ex)\n", 1565 | "ex" 1566 | ] 1567 | }, 1568 | { 1569 | "cell_type": "markdown", 1570 | "metadata": {}, 1571 | "source": [ 1572 | "Of course, we can now make this potentially more useful by generalising the function, allowing us to replace one sub-expression by another:" 1573 | ] 1574 | }, 1575 | { 1576 | "cell_type": "code", 1577 | "execution_count": 72, 1578 | "metadata": { 1579 | "collapsed": false 1580 | }, 1581 | "outputs": [ 1582 | { 1583 | "data": { 1584 | "text/plain": [ 1585 | "traverse! (generic function with 2 methods)" 1586 | ] 1587 | }, 1588 | "execution_count": 72, 1589 | "metadata": {}, 1590 | "output_type": "execute_result" 1591 | } 1592 | ], 1593 | "source": [ 1594 | "function traverse!(ex::Expr, find, replace)\n", 1595 | " for (i, arg) in enumerate(ex.args)\n", 1596 | " \n", 1597 | " if arg == find\n", 1598 | " ex.args[i] = replace # we cannot use arg here, since it is a copy, not a reference\n", 1599 | " end\n", 1600 | " \n", 1601 | " if isa(arg, Expr)\n", 1602 | " traverse!(arg, find, replace) # recursive\n", 1603 | " end\n", 1604 | " end\n", 1605 | " \n", 1606 | " ex\n", 1607 | "end" 1608 | ] 1609 | }, 1610 | { 1611 | "cell_type": "code", 1612 | "execution_count": 73, 1613 | "metadata": { 1614 | "collapsed": false 1615 | }, 1616 | "outputs": [ 1617 | { 1618 | "data": { 1619 | "text/plain": [ 1620 | ":((x - 10) * (x - 2))" 1621 | ] 1622 | }, 1623 | "execution_count": 73, 1624 | "metadata": {}, 1625 | "output_type": "execute_result" 1626 | } 1627 | ], 1628 | "source": [ 1629 | "traverse!( :( (x-1) * (x-2) ), :(x-1), :(x-10) )" 1630 | ] 1631 | }, 1632 | { 1633 | "cell_type": "code", 1634 | "execution_count": 74, 1635 | "metadata": { 1636 | "collapsed": false 1637 | }, 1638 | "outputs": [ 1639 | { 1640 | "data": { 1641 | "text/plain": [ 1642 | ":((z - 1) * (z - 2))" 1643 | ] 1644 | }, 1645 | "execution_count": 74, 1646 | "metadata": {}, 1647 | "output_type": "execute_result" 1648 | } 1649 | ], 1650 | "source": [ 1651 | "traverse!( :( (x-1) * (x-2) ), :(x), :(z) )" 1652 | ] 1653 | }, 1654 | { 1655 | "cell_type": "markdown", 1656 | "metadata": {}, 1657 | "source": [ 1658 | "This is, of course, not general enough - for example, we cannot replace something of the form `:(x - a)` with `:(x - 2a)` with the current version; this would require more sophisticated pattern matching capabilities - but it demonstrates the basic idea." 1659 | ] 1660 | }, 1661 | { 1662 | "cell_type": "markdown", 1663 | "metadata": {}, 1664 | "source": [ 1665 | "#### Exercise\n", 1666 | "Julia by default uses standard 64-bit (or 32-bit) integers, which leads to surprising behaviour, e.g." 1667 | ] 1668 | }, 1669 | { 1670 | "cell_type": "code", 1671 | "execution_count": 76, 1672 | "metadata": { 1673 | "collapsed": false 1674 | }, 1675 | "outputs": [ 1676 | { 1677 | "data": { 1678 | "text/plain": [ 1679 | "-9223372036854775808" 1680 | ] 1681 | }, 1682 | "execution_count": 76, 1683 | "metadata": {}, 1684 | "output_type": "execute_result" 1685 | } 1686 | ], 1687 | "source": [ 1688 | "2^32 * 2^31" 1689 | ] 1690 | }, 1691 | { 1692 | "cell_type": "markdown", 1693 | "metadata": {}, 1694 | "source": [ 1695 | "No warning is given that there was an overflow in this calculation. \n", 1696 | "\n", 1697 | "However, in `Base` there are *checked* operations, such as `checked_mul`, which do throw an exception on overflow:" 1698 | ] 1699 | }, 1700 | { 1701 | "cell_type": "code", 1702 | "execution_count": 77, 1703 | "metadata": { 1704 | "collapsed": false 1705 | }, 1706 | "outputs": [ 1707 | { 1708 | "ename": "LoadError", 1709 | "evalue": "LoadError: OverflowError()\nwhile loading In[77], in expression starting on line 1", 1710 | "output_type": "error", 1711 | "traceback": [ 1712 | "LoadError: OverflowError()\nwhile loading In[77], in expression starting on line 1", 1713 | "", 1714 | " in checked_mul at int.jl:514" 1715 | ] 1716 | } 1717 | ], 1718 | "source": [ 1719 | "Base.checked_mul(2^60, 2^60)" 1720 | ] 1721 | }, 1722 | { 1723 | "cell_type": "markdown", 1724 | "metadata": {}, 1725 | "source": [ 1726 | "#### Exercise\n", 1727 | "Write a function `checked` that replaces standard functions (`-`, `+`, `*`, `/`) in an expression by their corresponding checked counterparts." 1728 | ] 1729 | }, 1730 | { 1731 | "cell_type": "markdown", 1732 | "metadata": {}, 1733 | "source": [ 1734 | "## Code generation" 1735 | ] 1736 | }, 1737 | { 1738 | "cell_type": "markdown", 1739 | "metadata": {}, 1740 | "source": [ 1741 | "Let's return to the original question: how to create a long polynomial expression. \n", 1742 | "So far, we have not seen how to *add* code, only *change* code that is already there. \n", 1743 | "\n", 1744 | "A natural idea is to build the code up step by step; this is known as **code generation**." 1745 | ] 1746 | }, 1747 | { 1748 | "cell_type": "markdown", 1749 | "metadata": {}, 1750 | "source": [ 1751 | "Let's start again from the first term:" 1752 | ] 1753 | }, 1754 | { 1755 | "cell_type": "code", 1756 | "execution_count": 79, 1757 | "metadata": { 1758 | "collapsed": false 1759 | }, 1760 | "outputs": [ 1761 | { 1762 | "data": { 1763 | "text/plain": [ 1764 | ":(x - 1)" 1765 | ] 1766 | }, 1767 | "execution_count": 79, 1768 | "metadata": {}, 1769 | "output_type": "execute_result" 1770 | } 1771 | ], 1772 | "source": [ 1773 | "ex = :(x-1)" 1774 | ] 1775 | }, 1776 | { 1777 | "cell_type": "code", 1778 | "execution_count": 80, 1779 | "metadata": { 1780 | "collapsed": false 1781 | }, 1782 | "outputs": [ 1783 | { 1784 | "data": { 1785 | "text/plain": [ 1786 | "Expr" 1787 | ] 1788 | }, 1789 | "execution_count": 80, 1790 | "metadata": {}, 1791 | "output_type": "execute_result" 1792 | } 1793 | ], 1794 | "source": [ 1795 | "typeof(ex)" 1796 | ] 1797 | }, 1798 | { 1799 | "cell_type": "markdown", 1800 | "metadata": {}, 1801 | "source": [ 1802 | "We would like to take what we have and create code that is \"what we have multiplied by `:(x-2)`\". Let's try:" 1803 | ] 1804 | }, 1805 | { 1806 | "cell_type": "code", 1807 | "execution_count": 81, 1808 | "metadata": { 1809 | "collapsed": false 1810 | }, 1811 | "outputs": [ 1812 | { 1813 | "data": { 1814 | "text/plain": [ 1815 | ":(ex * (x - 2))" 1816 | ] 1817 | }, 1818 | "execution_count": 81, 1819 | "metadata": {}, 1820 | "output_type": "execute_result" 1821 | } 1822 | ], 1823 | "source": [ 1824 | "ex_new = :(ex * (x-2))" 1825 | ] 1826 | }, 1827 | { 1828 | "cell_type": "markdown", 1829 | "metadata": {}, 1830 | "source": [ 1831 | "This doesn't work, since `ex` is treated as a symbol, whereas we need the *value* contained in the *variable* called `ex`. This is obtained using the `$` operator, a procedure called **interpolation**. (Compare this to string interpolation.)" 1832 | ] 1833 | }, 1834 | { 1835 | "cell_type": "code", 1836 | "execution_count": 82, 1837 | "metadata": { 1838 | "collapsed": false 1839 | }, 1840 | "outputs": [ 1841 | { 1842 | "data": { 1843 | "text/plain": [ 1844 | "\"Hello David\"" 1845 | ] 1846 | }, 1847 | "execution_count": 82, 1848 | "metadata": {}, 1849 | "output_type": "execute_result" 1850 | } 1851 | ], 1852 | "source": [ 1853 | "name = \"David\"\n", 1854 | "s = \"Hello $name\"" 1855 | ] 1856 | }, 1857 | { 1858 | "cell_type": "code", 1859 | "execution_count": 83, 1860 | "metadata": { 1861 | "collapsed": false 1862 | }, 1863 | "outputs": [ 1864 | { 1865 | "data": { 1866 | "text/plain": [ 1867 | ":((x - 1) * (x - 2))" 1868 | ] 1869 | }, 1870 | "execution_count": 83, 1871 | "metadata": {}, 1872 | "output_type": "execute_result" 1873 | } 1874 | ], 1875 | "source": [ 1876 | "ex = :( $ex * (x-2) )" 1877 | ] 1878 | }, 1879 | { 1880 | "cell_type": "markdown", 1881 | "metadata": { 1882 | "collapsed": false 1883 | }, 1884 | "source": [ 1885 | "Now we can continue:" 1886 | ] 1887 | }, 1888 | { 1889 | "cell_type": "code", 1890 | "execution_count": 84, 1891 | "metadata": { 1892 | "collapsed": false 1893 | }, 1894 | "outputs": [ 1895 | { 1896 | "data": { 1897 | "text/plain": [ 1898 | ":(((x - 1) * (x - 2)) * (x - 3))" 1899 | ] 1900 | }, 1901 | "execution_count": 84, 1902 | "metadata": {}, 1903 | "output_type": "execute_result" 1904 | } 1905 | ], 1906 | "source": [ 1907 | "ex = :( $ex * (x-3) ) " 1908 | ] 1909 | }, 1910 | { 1911 | "cell_type": "markdown", 1912 | "metadata": {}, 1913 | "source": [ 1914 | "Finally we see how to construct our loop:" 1915 | ] 1916 | }, 1917 | { 1918 | "cell_type": "code", 1919 | "execution_count": 89, 1920 | "metadata": { 1921 | "collapsed": true 1922 | }, 1923 | "outputs": [], 1924 | "source": [ 1925 | "n = 10\n", 1926 | "ex = :(x-1)\n", 1927 | "\n", 1928 | "for i in 2:n\n", 1929 | " ex = :( $ex * (x - i) )\n", 1930 | "end" 1931 | ] 1932 | }, 1933 | { 1934 | "cell_type": "code", 1935 | "execution_count": 88, 1936 | "metadata": { 1937 | "collapsed": false 1938 | }, 1939 | "outputs": [ 1940 | { 1941 | "data": { 1942 | "text/plain": [ 1943 | ":((((((((((x - 1) * (x - 2)) * (x - 3)) * (x - 4)) * (x - 5)) * (x - 6)) * (x - 7)) * (x - 8)) * (x - 9)) * (x - 10))" 1944 | ] 1945 | }, 1946 | "execution_count": 88, 1947 | "metadata": {}, 1948 | "output_type": "execute_result" 1949 | } 1950 | ], 1951 | "source": [ 1952 | "ex" 1953 | ] 1954 | }, 1955 | { 1956 | "cell_type": "markdown", 1957 | "metadata": {}, 1958 | "source": [ 1959 | "This did not work, since once again we did not want \"the code '`i`'\", but rather the value of the variable `i`. So:" 1960 | ] 1961 | }, 1962 | { 1963 | "cell_type": "code", 1964 | "execution_count": 97, 1965 | "metadata": { 1966 | "collapsed": true 1967 | }, 1968 | "outputs": [], 1969 | "source": [ 1970 | "n = 10\n", 1971 | "\n", 1972 | "ex = :(x-1)\n", 1973 | "\n", 1974 | "for i in 2:n\n", 1975 | " ex = :( $ex * (x - $i) )\n", 1976 | "end" 1977 | ] 1978 | }, 1979 | { 1980 | "cell_type": "code", 1981 | "execution_count": 98, 1982 | "metadata": { 1983 | "collapsed": false 1984 | }, 1985 | "outputs": [ 1986 | { 1987 | "data": { 1988 | "text/plain": [ 1989 | ":((((((((((x - 1) * (x - 2)) * (x - 3)) * (x - 4)) * (x - 5)) * (x - 6)) * (x - 7)) * (x - 8)) * (x - 9)) * (x - 10))" 1990 | ] 1991 | }, 1992 | "execution_count": 98, 1993 | "metadata": {}, 1994 | "output_type": "execute_result" 1995 | } 1996 | ], 1997 | "source": [ 1998 | "ex" 1999 | ] 2000 | }, 2001 | { 2002 | "cell_type": "markdown", 2003 | "metadata": {}, 2004 | "source": [ 2005 | "This is almost what we would write by hand, except for the large number of parentheses." 2006 | ] 2007 | }, 2008 | { 2009 | "cell_type": "markdown", 2010 | "metadata": {}, 2011 | "source": [ 2012 | "Now we need to produce the name of the function:" 2013 | ] 2014 | }, 2015 | { 2016 | "cell_type": "code", 2017 | "execution_count": 100, 2018 | "metadata": { 2019 | "collapsed": false 2020 | }, 2021 | "outputs": [ 2022 | { 2023 | "data": { 2024 | "text/plain": [ 2025 | "\"p_10\"" 2026 | ] 2027 | }, 2028 | "execution_count": 100, 2029 | "metadata": {}, 2030 | "output_type": "execute_result" 2031 | } 2032 | ], 2033 | "source": [ 2034 | "string(\"p_\", n)" 2035 | ] 2036 | }, 2037 | { 2038 | "cell_type": "code", 2039 | "execution_count": 99, 2040 | "metadata": { 2041 | "collapsed": false 2042 | }, 2043 | "outputs": [ 2044 | { 2045 | "data": { 2046 | "text/plain": [ 2047 | ":p_10" 2048 | ] 2049 | }, 2050 | "execution_count": 99, 2051 | "metadata": {}, 2052 | "output_type": "execute_result" 2053 | } 2054 | ], 2055 | "source": [ 2056 | "name = symbol(\"p_\", n) # use `Symbol` instead of `symbol` in Julia v0.5" 2057 | ] 2058 | }, 2059 | { 2060 | "cell_type": "markdown", 2061 | "metadata": {}, 2062 | "source": [ 2063 | "The code is then" 2064 | ] 2065 | }, 2066 | { 2067 | "cell_type": "code", 2068 | "execution_count": 101, 2069 | "metadata": { 2070 | "collapsed": false 2071 | }, 2072 | "outputs": [ 2073 | { 2074 | "data": { 2075 | "text/plain": [ 2076 | ":(p_10(x) = begin # In[101], line 1:\n", 2077 | " (((((((((x - 1) * (x - 2)) * (x - 3)) * (x - 4)) * (x - 5)) * (x - 6)) * (x - 7)) * (x - 8)) * (x - 9)) * (x - 10)\n", 2078 | " end)" 2079 | ] 2080 | }, 2081 | "execution_count": 101, 2082 | "metadata": {}, 2083 | "output_type": "execute_result" 2084 | } 2085 | ], 2086 | "source": [ 2087 | "code = :( $name(x) = $ex )" 2088 | ] 2089 | }, 2090 | { 2091 | "cell_type": "markdown", 2092 | "metadata": {}, 2093 | "source": [ 2094 | "We can wrap this in a function:" 2095 | ] 2096 | }, 2097 | { 2098 | "cell_type": "code", 2099 | "execution_count": 103, 2100 | "metadata": { 2101 | "collapsed": false 2102 | }, 2103 | "outputs": [ 2104 | { 2105 | "data": { 2106 | "text/plain": [ 2107 | "make_wilkinson (generic function with 1 method)" 2108 | ] 2109 | }, 2110 | "execution_count": 103, 2111 | "metadata": {}, 2112 | "output_type": "execute_result" 2113 | } 2114 | ], 2115 | "source": [ 2116 | "function make_wilkinson(n)\n", 2117 | " \n", 2118 | " ex = :(x-1)\n", 2119 | "\n", 2120 | " for i in 2:n\n", 2121 | " ex = :( $ex * (x - $i) )\n", 2122 | " end\n", 2123 | " \n", 2124 | " name = symbol(\"p_\", n) \n", 2125 | " code = :( $name(x) = $ex )\n", 2126 | " \n", 2127 | " eval(code)\n", 2128 | "end" 2129 | ] 2130 | }, 2131 | { 2132 | "cell_type": "markdown", 2133 | "metadata": {}, 2134 | "source": [ 2135 | "Finally we evaluate this:" 2136 | ] 2137 | }, 2138 | { 2139 | "cell_type": "code", 2140 | "execution_count": 104, 2141 | "metadata": { 2142 | "collapsed": false 2143 | }, 2144 | "outputs": [ 2145 | { 2146 | "data": { 2147 | "text/plain": [ 2148 | "p_10 (generic function with 1 method)" 2149 | ] 2150 | }, 2151 | "execution_count": 104, 2152 | "metadata": {}, 2153 | "output_type": "execute_result" 2154 | } 2155 | ], 2156 | "source": [ 2157 | "make_wilkinson(10)" 2158 | ] 2159 | }, 2160 | { 2161 | "cell_type": "code", 2162 | "execution_count": 105, 2163 | "metadata": { 2164 | "collapsed": false 2165 | }, 2166 | "outputs": [ 2167 | { 2168 | "data": { 2169 | "text/plain": [ 2170 | "p_100 (generic function with 1 method)" 2171 | ] 2172 | }, 2173 | "execution_count": 105, 2174 | "metadata": {}, 2175 | "output_type": "execute_result" 2176 | } 2177 | ], 2178 | "source": [ 2179 | "make_wilkinson(100)" 2180 | ] 2181 | }, 2182 | { 2183 | "cell_type": "markdown", 2184 | "metadata": {}, 2185 | "source": [ 2186 | "This creates a function with the name `p_10` that does what we would write by hand." 2187 | ] 2188 | }, 2189 | { 2190 | "cell_type": "markdown", 2191 | "metadata": {}, 2192 | "source": [ 2193 | "Let's compare the two options by evaluating the function on a grid of values:" 2194 | ] 2195 | }, 2196 | { 2197 | "cell_type": "code", 2198 | "execution_count": 106, 2199 | "metadata": { 2200 | "collapsed": false 2201 | }, 2202 | "outputs": [ 2203 | { 2204 | "data": { 2205 | "text/plain": [ 2206 | "f2 (generic function with 1 method)" 2207 | ] 2208 | }, 2209 | "execution_count": 106, 2210 | "metadata": {}, 2211 | "output_type": "execute_result" 2212 | } 2213 | ], 2214 | "source": [ 2215 | "function f1(range)\n", 2216 | " total = 0.0\n", 2217 | " for x in range\n", 2218 | " total += p_100(x)\n", 2219 | " end\n", 2220 | " return total\n", 2221 | "end\n", 2222 | "\n", 2223 | "function f2(range)\n", 2224 | " total = 0.0\n", 2225 | " for x in range\n", 2226 | " total += wilkinson(100, x)\n", 2227 | " end\n", 2228 | " return total\n", 2229 | "end" 2230 | ] 2231 | }, 2232 | { 2233 | "cell_type": "code", 2234 | "execution_count": null, 2235 | "metadata": { 2236 | "collapsed": true 2237 | }, 2238 | "outputs": [], 2239 | "source": [] 2240 | }, 2241 | { 2242 | "cell_type": "code", 2243 | "execution_count": 112, 2244 | "metadata": { 2245 | "collapsed": false 2246 | }, 2247 | "outputs": [ 2248 | { 2249 | "data": { 2250 | "text/plain": [ 2251 | "1.2080035401369522" 2252 | ] 2253 | }, 2254 | "execution_count": 112, 2255 | "metadata": {}, 2256 | "output_type": "execute_result" 2257 | } 2258 | ], 2259 | "source": [ 2260 | "range = -10:0.000001:10\n", 2261 | "t1 = @elapsed f1(range);\n", 2262 | "t2 = @elapsed f2(range);\n", 2263 | "t2 / t1" 2264 | ] 2265 | }, 2266 | { 2267 | "cell_type": "markdown", 2268 | "metadata": {}, 2269 | "source": [ 2270 | "We see that the generated code with the unrolled loop is 10% faster than the naive loop with 100 terms." 2271 | ] 2272 | }, 2273 | { 2274 | "cell_type": "markdown", 2275 | "metadata": {}, 2276 | "source": [ 2277 | "### Starting from empty" 2278 | ] 2279 | }, 2280 | { 2281 | "cell_type": "markdown", 2282 | "metadata": {}, 2283 | "source": [ 2284 | "In some cases, it is useful to start from something empty and add statements to it.\n", 2285 | "To do this, we can do, for example," 2286 | ] 2287 | }, 2288 | { 2289 | "cell_type": "code", 2290 | "execution_count": 113, 2291 | "metadata": { 2292 | "collapsed": false 2293 | }, 2294 | "outputs": [ 2295 | { 2296 | "data": { 2297 | "text/plain": [ 2298 | "quote \n", 2299 | "end" 2300 | ] 2301 | }, 2302 | "execution_count": 113, 2303 | "metadata": {}, 2304 | "output_type": "execute_result" 2305 | } 2306 | ], 2307 | "source": [ 2308 | "code = quote end # empty" 2309 | ] 2310 | }, 2311 | { 2312 | "cell_type": "code", 2313 | "execution_count": 114, 2314 | "metadata": { 2315 | "collapsed": false 2316 | }, 2317 | "outputs": [ 2318 | { 2319 | "name": "stdout", 2320 | "output_type": "stream", 2321 | "text": [ 2322 | "Expr \n", 2323 | " head: Symbol block\n", 2324 | " args: Array(Any,(0,))\n", 2325 | " typ: Any\n" 2326 | ] 2327 | } 2328 | ], 2329 | "source": [ 2330 | "dump(code)" 2331 | ] 2332 | }, 2333 | { 2334 | "cell_type": "markdown", 2335 | "metadata": {}, 2336 | "source": [ 2337 | "[Note that in Julia 0.5, this is not \"empty\", since it has a `:line` line-number node.]\n", 2338 | "\n", 2339 | "We can add statements using the standard Julia `push!`:" 2340 | ] 2341 | }, 2342 | { 2343 | "cell_type": "code", 2344 | "execution_count": 115, 2345 | "metadata": { 2346 | "collapsed": false 2347 | }, 2348 | "outputs": [ 2349 | { 2350 | "data": { 2351 | "text/plain": [ 2352 | "quote \n", 2353 | " a = 1\n", 2354 | " b = a + 1\n", 2355 | "end" 2356 | ] 2357 | }, 2358 | "execution_count": 115, 2359 | "metadata": {}, 2360 | "output_type": "execute_result" 2361 | } 2362 | ], 2363 | "source": [ 2364 | "Base.push!(code.args, :(a = 1))\n", 2365 | "Base.push!(code.args, :(b = a + 1))\n", 2366 | "code" 2367 | ] 2368 | }, 2369 | { 2370 | "cell_type": "code", 2371 | "execution_count": 116, 2372 | "metadata": { 2373 | "collapsed": false 2374 | }, 2375 | "outputs": [ 2376 | { 2377 | "data": { 2378 | "text/plain": [ 2379 | "2" 2380 | ] 2381 | }, 2382 | "execution_count": 116, 2383 | "metadata": {}, 2384 | "output_type": "execute_result" 2385 | } 2386 | ], 2387 | "source": [ 2388 | "eval(code)" 2389 | ] 2390 | }, 2391 | { 2392 | "cell_type": "code", 2393 | "execution_count": 117, 2394 | "metadata": { 2395 | "collapsed": false 2396 | }, 2397 | "outputs": [ 2398 | { 2399 | "data": { 2400 | "text/plain": [ 2401 | "1" 2402 | ] 2403 | }, 2404 | "execution_count": 117, 2405 | "metadata": {}, 2406 | "output_type": "execute_result" 2407 | } 2408 | ], 2409 | "source": [ 2410 | "a" 2411 | ] 2412 | }, 2413 | { 2414 | "cell_type": "code", 2415 | "execution_count": 118, 2416 | "metadata": { 2417 | "collapsed": false 2418 | }, 2419 | "outputs": [ 2420 | { 2421 | "data": { 2422 | "text/plain": [ 2423 | "2" 2424 | ] 2425 | }, 2426 | "execution_count": 118, 2427 | "metadata": {}, 2428 | "output_type": "execute_result" 2429 | } 2430 | ], 2431 | "source": [ 2432 | "b" 2433 | ] 2434 | }, 2435 | { 2436 | "cell_type": "code", 2437 | "execution_count": 119, 2438 | "metadata": { 2439 | "collapsed": false 2440 | }, 2441 | "outputs": [ 2442 | { 2443 | "name": "stdout", 2444 | "output_type": "stream", 2445 | "text": [ 2446 | "Expr \n", 2447 | " head: Symbol call\n", 2448 | " args: Array(Any,(4,))\n", 2449 | " 1: Symbol *\n", 2450 | " 2: Symbol a\n", 2451 | " 3: Symbol b\n", 2452 | " 4: Symbol c\n", 2453 | " typ: Any\n" 2454 | ] 2455 | } 2456 | ], 2457 | "source": [ 2458 | "dump(:(a * b * c))" 2459 | ] 2460 | }, 2461 | { 2462 | "cell_type": "markdown", 2463 | "metadata": {}, 2464 | "source": [ 2465 | "**Exercise**: Build up the Wilkinson example using this technique." 2466 | ] 2467 | }, 2468 | { 2469 | "cell_type": "markdown", 2470 | "metadata": {}, 2471 | "source": [ 2472 | "## Repetitive code" 2473 | ] 2474 | }, 2475 | { 2476 | "cell_type": "markdown", 2477 | "metadata": {}, 2478 | "source": [ 2479 | "Code generation is used frequently in Julia code when repetitive code is required. For example, let's return to the idea of wrapping a type:" 2480 | ] 2481 | }, 2482 | { 2483 | "cell_type": "code", 2484 | "execution_count": 121, 2485 | "metadata": { 2486 | "collapsed": false 2487 | }, 2488 | "outputs": [], 2489 | "source": [ 2490 | "type OurFloat\n", 2491 | " x::Float64\n", 2492 | "end" 2493 | ] 2494 | }, 2495 | { 2496 | "cell_type": "markdown", 2497 | "metadata": {}, 2498 | "source": [ 2499 | "We can generate objects of this type:" 2500 | ] 2501 | }, 2502 | { 2503 | "cell_type": "code", 2504 | "execution_count": 123, 2505 | "metadata": { 2506 | "collapsed": false 2507 | }, 2508 | "outputs": [ 2509 | { 2510 | "data": { 2511 | "text/plain": [ 2512 | "OurFloat(4.0)" 2513 | ] 2514 | }, 2515 | "execution_count": 123, 2516 | "metadata": {}, 2517 | "output_type": "execute_result" 2518 | } 2519 | ], 2520 | "source": [ 2521 | "a = OurFloat(3)\n", 2522 | "b = OurFloat(4)" 2523 | ] 2524 | }, 2525 | { 2526 | "cell_type": "markdown", 2527 | "metadata": {}, 2528 | "source": [ 2529 | "But arithmetic operations are not defined:" 2530 | ] 2531 | }, 2532 | { 2533 | "cell_type": "code", 2534 | "execution_count": 124, 2535 | "metadata": { 2536 | "collapsed": false 2537 | }, 2538 | "outputs": [ 2539 | { 2540 | "ename": "LoadError", 2541 | "evalue": "LoadError: MethodError: `+` has no method matching +(::OurFloat, ::OurFloat)\nClosest candidates are:\n +(::Any, ::Any, !Matched::Any, !Matched::Any...)\nwhile loading In[124], in expression starting on line 1", 2542 | "output_type": "error", 2543 | "traceback": [ 2544 | "LoadError: MethodError: `+` has no method matching +(::OurFloat, ::OurFloat)\nClosest candidates are:\n +(::Any, ::Any, !Matched::Any, !Matched::Any...)\nwhile loading In[124], in expression starting on line 1", 2545 | "" 2546 | ] 2547 | } 2548 | ], 2549 | "source": [ 2550 | "a + b" 2551 | ] 2552 | }, 2553 | { 2554 | "cell_type": "markdown", 2555 | "metadata": {}, 2556 | "source": [ 2557 | "We can define them in the natural way:" 2558 | ] 2559 | }, 2560 | { 2561 | "cell_type": "code", 2562 | "execution_count": 125, 2563 | "metadata": { 2564 | "collapsed": false 2565 | }, 2566 | "outputs": [ 2567 | { 2568 | "data": { 2569 | "text/plain": [ 2570 | "- (generic function with 205 methods)" 2571 | ] 2572 | }, 2573 | "execution_count": 125, 2574 | "metadata": {}, 2575 | "output_type": "execute_result" 2576 | } 2577 | ], 2578 | "source": [ 2579 | "import Base: +, -, *, /\n", 2580 | "+(a::OurFloat, b::OurFloat) = a.x + b.x\n", 2581 | "-(a::OurFloat, b::OurFloat) = a.x - b.x" 2582 | ] 2583 | }, 2584 | { 2585 | "cell_type": "markdown", 2586 | "metadata": {}, 2587 | "source": [ 2588 | "But this will quickly get dull, and we could easily make a mistake. \n", 2589 | "As usual, whenever we are repeating something more than twice, we should try to automate it.\n", 2590 | "\n", 2591 | "We have some code of the form\n", 2592 | "\n", 2593 | " *op*(a::OurFloat, b::OurFloat) = a.x *op* b.x\n", 2594 | " \n", 2595 | "where `*op*` is supposed to represent the operator. Julia allows us to do this almost literally; we just need to substitute in the *value* of the *variable* `op`! This is an **exercise**" 2596 | ] 2597 | }, 2598 | { 2599 | "cell_type": "code", 2600 | "execution_count": 127, 2601 | "metadata": { 2602 | "collapsed": false 2603 | }, 2604 | "outputs": [ 2605 | { 2606 | "name": "stdout", 2607 | "output_type": "stream", 2608 | "text": [ 2609 | "op = :+\n", 2610 | "code = :(a::OurFloat + b::OurFloat = begin # In[127], line 3:\n", 2611 | " a.x + b.x\n", 2612 | " end)\n", 2613 | "op = :-\n", 2614 | "code = :(a::OurFloat - b::OurFloat = begin # In[127], line 3:\n", 2615 | " a.x - b.x\n", 2616 | " end)\n", 2617 | "op = :*\n", 2618 | "code = :(a::OurFloat * b::OurFloat = begin # In[127], line 3:\n", 2619 | " a.x * b.x\n", 2620 | " end)\n", 2621 | "op = :/\n", 2622 | "code = :(a::OurFloat / b::OurFloat = begin # In[127], line 3:\n", 2623 | " a.x / b.x\n", 2624 | " end)\n" 2625 | ] 2626 | } 2627 | ], 2628 | "source": [ 2629 | "for op in (:+, :-, :*, :/)\n", 2630 | " @show op\n", 2631 | " code = :( $(op)(a::OurFloat, b::OurFloat) = $(op)(a.x, b.x) )\n", 2632 | " @show code\n", 2633 | "end" 2634 | ] 2635 | }, 2636 | { 2637 | "cell_type": "markdown", 2638 | "metadata": {}, 2639 | "source": [ 2640 | "Finally we need to evaluate the code. The combination of `eval` and `:(...)` that we used above can be abbreviated to `@eval`:" 2641 | ] 2642 | }, 2643 | { 2644 | "cell_type": "code", 2645 | "execution_count": 128, 2646 | "metadata": { 2647 | "collapsed": false 2648 | }, 2649 | "outputs": [], 2650 | "source": [ 2651 | "for op in (:+, :-, :*, :/)\n", 2652 | " @eval $(op)(a::OurFloat, b::OurFloat) = $(op)(a.x, b.x) \n", 2653 | "end" 2654 | ] 2655 | }, 2656 | { 2657 | "cell_type": "code", 2658 | "execution_count": 129, 2659 | "metadata": { 2660 | "collapsed": false 2661 | }, 2662 | "outputs": [ 2663 | { 2664 | "data": { 2665 | "text/plain": [ 2666 | "12.0" 2667 | ] 2668 | }, 2669 | "execution_count": 129, 2670 | "metadata": {}, 2671 | "output_type": "execute_result" 2672 | } 2673 | ], 2674 | "source": [ 2675 | "a*b" 2676 | ] 2677 | }, 2678 | { 2679 | "cell_type": "code", 2680 | "execution_count": 130, 2681 | "metadata": { 2682 | "collapsed": false 2683 | }, 2684 | "outputs": [ 2685 | { 2686 | "data": { 2687 | "text/html": [ 2688 | "*(a::OurFloat, b::OurFloat) at In[128]:2" 2689 | ], 2690 | "text/plain": [ 2691 | "*(a::OurFloat, b::OurFloat) at In[128]:2" 2692 | ] 2693 | }, 2694 | "execution_count": 130, 2695 | "metadata": {}, 2696 | "output_type": "execute_result" 2697 | } 2698 | ], 2699 | "source": [ 2700 | "@which a*b" 2701 | ] 2702 | }, 2703 | { 2704 | "cell_type": "code", 2705 | "execution_count": 131, 2706 | "metadata": { 2707 | "collapsed": false 2708 | }, 2709 | "outputs": [ 2710 | { 2711 | "data": { 2712 | "text/html": [ 2713 | "sin(x::Float64) at math.jl:137" 2714 | ], 2715 | "text/plain": [ 2716 | "sin(x::Float64) at math.jl:137" 2717 | ] 2718 | }, 2719 | "execution_count": 131, 2720 | "metadata": {}, 2721 | "output_type": "execute_result" 2722 | } 2723 | ], 2724 | "source": [ 2725 | "@which sin(3.5)" 2726 | ] 2727 | }, 2728 | { 2729 | "cell_type": "markdown", 2730 | "metadata": {}, 2731 | "source": [ 2732 | "## Macros" 2733 | ] 2734 | }, 2735 | { 2736 | "cell_type": "markdown", 2737 | "metadata": {}, 2738 | "source": [ 2739 | "It is very common in Julia to use things that look like functions but whose names start with `@`, e.g. `@time`, `@which`, etc. These are not functions in the standard sense, but rather are a kind of \"super-function\", called **macros**. \n", 2740 | "\n", 2741 | "A macro takes a **piece of Julia code** (`Expr`ession object) as its argument, manipulates that code to turn it into a new one, and **returns a new piece of code** as its output. The effect of a macro call is to insert that new piece of code in place of the old code, which is consequently compiled by the Julia compiler. \n", 2742 | "\n", 2743 | "Note that the user *does not need to explicitly pass an `Expr`ession object, since this is done automatically*." 2744 | ] 2745 | }, 2746 | { 2747 | "cell_type": "markdown", 2748 | "metadata": {}, 2749 | "source": [ 2750 | "To see this, let's define a simple macro:" 2751 | ] 2752 | }, 2753 | { 2754 | "cell_type": "code", 2755 | "execution_count": 132, 2756 | "metadata": { 2757 | "collapsed": false 2758 | }, 2759 | "outputs": [], 2760 | "source": [ 2761 | "macro simple(expr)\n", 2762 | " @show expr\n", 2763 | " nothing # return nothing for the moment\n", 2764 | "end" 2765 | ] 2766 | }, 2767 | { 2768 | "cell_type": "code", 2769 | "execution_count": 137, 2770 | "metadata": { 2771 | "collapsed": false 2772 | }, 2773 | "outputs": [ 2774 | { 2775 | "ename": "LoadError", 2776 | "evalue": "LoadError: UndefVarError: ≲ not defined\nwhile loading In[137], in expression starting on line 3", 2777 | "output_type": "error", 2778 | "traceback": [ 2779 | "LoadError: UndefVarError: ≲ not defined\nwhile loading In[137], in expression starting on line 3", 2780 | "" 2781 | ] 2782 | } 2783 | ], 2784 | "source": [ 2785 | "x = 3\n", 2786 | "y = 4\n", 2787 | "x ≲ y" 2788 | ] 2789 | }, 2790 | { 2791 | "cell_type": "code", 2792 | "execution_count": 138, 2793 | "metadata": { 2794 | "collapsed": false 2795 | }, 2796 | "outputs": [ 2797 | { 2798 | "name": "stdout", 2799 | "output_type": "stream", 2800 | "text": [ 2801 | "expr = :((3 * x ^ 2 + 4x) - 2)\n" 2802 | ] 2803 | } 2804 | ], 2805 | "source": [ 2806 | "@simple 3x^2 + 4x - 2" 2807 | ] 2808 | }, 2809 | { 2810 | "cell_type": "markdown", 2811 | "metadata": {}, 2812 | "source": [ 2813 | "We see that the Julia code that follows the macro call is passed to the macro *already having been parsed into an `Expr` object*." 2814 | ] 2815 | }, 2816 | { 2817 | "cell_type": "markdown", 2818 | "metadata": {}, 2819 | "source": [ 2820 | "Suppose we redefine the macro as" 2821 | ] 2822 | }, 2823 | { 2824 | "cell_type": "code", 2825 | "execution_count": 139, 2826 | "metadata": { 2827 | "collapsed": false 2828 | }, 2829 | "outputs": [], 2830 | "source": [ 2831 | "macro simple(expr)\n", 2832 | " @show expr\n", 2833 | " expr # returns expr\n", 2834 | "end" 2835 | ] 2836 | }, 2837 | { 2838 | "cell_type": "markdown", 2839 | "metadata": {}, 2840 | "source": [ 2841 | "Then we get" 2842 | ] 2843 | }, 2844 | { 2845 | "cell_type": "code", 2846 | "execution_count": 140, 2847 | "metadata": { 2848 | "collapsed": false 2849 | }, 2850 | "outputs": [ 2851 | { 2852 | "name": "stdout", 2853 | "output_type": "stream", 2854 | "text": [ 2855 | "expr = :(w + z)\n" 2856 | ] 2857 | }, 2858 | { 2859 | "ename": "LoadError", 2860 | "evalue": "LoadError: UndefVarError: w not defined\nwhile loading In[140], in expression starting on line 1", 2861 | "output_type": "error", 2862 | "traceback": [ 2863 | "LoadError: UndefVarError: w not defined\nwhile loading In[140], in expression starting on line 1", 2864 | "" 2865 | ] 2866 | } 2867 | ], 2868 | "source": [ 2869 | "@simple w + z" 2870 | ] 2871 | }, 2872 | { 2873 | "cell_type": "markdown", 2874 | "metadata": {}, 2875 | "source": [ 2876 | "What is happening here is that the macro returns the expression `:(x+y)`, and this is then evaluated using `eval`.\n", 2877 | "The result is that Julia tries to calculate the value of the expression `x+y`, but the variable `x` is not defined. Let's define `y` and `z`, but not `x`:" 2878 | ] 2879 | }, 2880 | { 2881 | "cell_type": "code", 2882 | "execution_count": null, 2883 | "metadata": { 2884 | "collapsed": false 2885 | }, 2886 | "outputs": [], 2887 | "source": [ 2888 | "y = 3; z = 4" 2889 | ] 2890 | }, 2891 | { 2892 | "cell_type": "code", 2893 | "execution_count": null, 2894 | "metadata": { 2895 | "collapsed": false 2896 | }, 2897 | "outputs": [], 2898 | "source": [ 2899 | "x" 2900 | ] 2901 | }, 2902 | { 2903 | "cell_type": "code", 2904 | "execution_count": null, 2905 | "metadata": { 2906 | "collapsed": false 2907 | }, 2908 | "outputs": [], 2909 | "source": [ 2910 | "@simple x+y" 2911 | ] 2912 | }, 2913 | { 2914 | "cell_type": "markdown", 2915 | "metadata": {}, 2916 | "source": [ 2917 | "Now let's define a new macro `@replace` that uses our previous `replace` function:" 2918 | ] 2919 | }, 2920 | { 2921 | "cell_type": "code", 2922 | "execution_count": 146, 2923 | "metadata": { 2924 | "collapsed": false 2925 | }, 2926 | "outputs": [], 2927 | "source": [ 2928 | "macro traverse(expr)\n", 2929 | " traverse!(expr)\n", 2930 | " @show expr\n", 2931 | " #Meta.quot(expr)\n", 2932 | " expr\n", 2933 | "end" 2934 | ] 2935 | }, 2936 | { 2937 | "cell_type": "code", 2938 | "execution_count": 147, 2939 | "metadata": { 2940 | "collapsed": false 2941 | }, 2942 | "outputs": [ 2943 | { 2944 | "name": "stdout", 2945 | "output_type": "stream", 2946 | "text": [ 2947 | "expr = :(z + y)\n" 2948 | ] 2949 | }, 2950 | { 2951 | "data": { 2952 | "text/plain": [ 2953 | "8.5" 2954 | ] 2955 | }, 2956 | "execution_count": 147, 2957 | "metadata": {}, 2958 | "output_type": "execute_result" 2959 | } 2960 | ], 2961 | "source": [ 2962 | "@traverse x + y" 2963 | ] 2964 | }, 2965 | { 2966 | "cell_type": "markdown", 2967 | "metadata": {}, 2968 | "source": [ 2969 | "## `macroexpand`" 2970 | ] 2971 | }, 2972 | { 2973 | "cell_type": "markdown", 2974 | "metadata": {}, 2975 | "source": [ 2976 | "We can discover what a macro does using the `macroexpand` function which takes a code expression:" 2977 | ] 2978 | }, 2979 | { 2980 | "cell_type": "code", 2981 | "execution_count": 149, 2982 | "metadata": { 2983 | "collapsed": false 2984 | }, 2985 | "outputs": [ 2986 | { 2987 | "name": "stdout", 2988 | "output_type": "stream", 2989 | "text": [ 2990 | " 0.000003 seconds (5 allocations: 176 bytes)\n" 2991 | ] 2992 | }, 2993 | { 2994 | "data": { 2995 | "text/plain": [ 2996 | "-0.5440211108893698" 2997 | ] 2998 | }, 2999 | "execution_count": 149, 3000 | "metadata": {}, 3001 | "output_type": "execute_result" 3002 | } 3003 | ], 3004 | "source": [ 3005 | "@time sin(10)" 3006 | ] 3007 | }, 3008 | { 3009 | "cell_type": "code", 3010 | "execution_count": 150, 3011 | "metadata": { 3012 | "collapsed": false 3013 | }, 3014 | "outputs": [ 3015 | { 3016 | "data": { 3017 | "text/plain": [ 3018 | ":(sin(10))" 3019 | ] 3020 | }, 3021 | "execution_count": 150, 3022 | "metadata": {}, 3023 | "output_type": "execute_result" 3024 | } 3025 | ], 3026 | "source": [ 3027 | "ex = :(sin(10))" 3028 | ] 3029 | }, 3030 | { 3031 | "cell_type": "code", 3032 | "execution_count": 151, 3033 | "metadata": { 3034 | "collapsed": false 3035 | }, 3036 | "outputs": [ 3037 | { 3038 | "data": { 3039 | "text/plain": [ 3040 | "quote # util.jl, line 153:\n", 3041 | " local #101#stats = Base.gc_num() # util.jl, line 154:\n", 3042 | " local #103#elapsedtime = Base.time_ns() # util.jl, line 155:\n", 3043 | " local #102#val = sin(10) # util.jl, line 156:\n", 3044 | " #103#elapsedtime = Base.-(Base.time_ns(),#103#elapsedtime) # util.jl, line 157:\n", 3045 | " local #104#diff = Base.GC_Diff(Base.gc_num(),#101#stats) # util.jl, line 158:\n", 3046 | " Base.time_print(#103#elapsedtime,#104#diff.allocd,#104#diff.total_time,Base.gc_alloc_count(#104#diff)) # util.jl, line 160:\n", 3047 | " #102#val\n", 3048 | "end" 3049 | ] 3050 | }, 3051 | "execution_count": 151, 3052 | "metadata": {}, 3053 | "output_type": "execute_result" 3054 | } 3055 | ], 3056 | "source": [ 3057 | "macroexpand(:(@time sin(10)))" 3058 | ] 3059 | }, 3060 | { 3061 | "cell_type": "markdown", 3062 | "metadata": {}, 3063 | "source": [ 3064 | "As an example of the use of a macro, let's look at the `@interval` macro from the [`ValidatedNumerics.jl` package](https://github.com/dpsanders/ValidatedNumerics.jl):" 3065 | ] 3066 | }, 3067 | { 3068 | "cell_type": "code", 3069 | "execution_count": null, 3070 | "metadata": { 3071 | "collapsed": false 3072 | }, 3073 | "outputs": [], 3074 | "source": [ 3075 | "# Pkg.add(\"ValidatedNumerics\")" 3076 | ] 3077 | }, 3078 | { 3079 | "cell_type": "code", 3080 | "execution_count": 153, 3081 | "metadata": { 3082 | "collapsed": false 3083 | }, 3084 | "outputs": [], 3085 | "source": [ 3086 | "using ValidatedNumerics" 3087 | ] 3088 | }, 3089 | { 3090 | "cell_type": "code", 3091 | "execution_count": 154, 3092 | "metadata": { 3093 | "collapsed": false 3094 | }, 3095 | "outputs": [ 3096 | { 3097 | "data": { 3098 | "text/plain": [ 3099 | "[0.0999999, 0.100001]" 3100 | ] 3101 | }, 3102 | "execution_count": 154, 3103 | "metadata": {}, 3104 | "output_type": "execute_result" 3105 | } 3106 | ], 3107 | "source": [ 3108 | "@interval(0.1)" 3109 | ] 3110 | }, 3111 | { 3112 | "cell_type": "markdown", 3113 | "metadata": {}, 3114 | "source": [ 3115 | "Floating-point calculations are not precise. Interval arithmetic is a way to provide a guaranteed *enclosure* of a result, i.e. an interval that contains the true result. " 3116 | ] 3117 | }, 3118 | { 3119 | "cell_type": "code", 3120 | "execution_count": 155, 3121 | "metadata": { 3122 | "collapsed": false 3123 | }, 3124 | "outputs": [ 3125 | { 3126 | "data": { 3127 | "text/plain": [ 3128 | ":(ValidatedNumerics.convert(Interval{parameters.precision_type},0.1))" 3129 | ] 3130 | }, 3131 | "execution_count": 155, 3132 | "metadata": {}, 3133 | "output_type": "execute_result" 3134 | } 3135 | ], 3136 | "source": [ 3137 | "macroexpand(:(@interval(0.1)))" 3138 | ] 3139 | }, 3140 | { 3141 | "cell_type": "code", 3142 | "execution_count": 156, 3143 | "metadata": { 3144 | "collapsed": false 3145 | }, 3146 | "outputs": [ 3147 | { 3148 | "data": { 3149 | "text/plain": [ 3150 | "[1.07989, 1.0799]" 3151 | ] 3152 | }, 3153 | "execution_count": 156, 3154 | "metadata": {}, 3155 | "output_type": "execute_result" 3156 | } 3157 | ], 3158 | "source": [ 3159 | "@interval sin(0.1) + cos(0.2)" 3160 | ] 3161 | }, 3162 | { 3163 | "cell_type": "code", 3164 | "execution_count": 158, 3165 | "metadata": { 3166 | "collapsed": false 3167 | }, 3168 | "outputs": [ 3169 | { 3170 | "data": { 3171 | "text/plain": [ 3172 | ":(ValidatedNumerics.+(sin(ValidatedNumerics.convert(Interval{parameters.precision_type},0.1)),cos(ValidatedNumerics.convert(Interval{parameters.precision_type},0.2))))" 3173 | ] 3174 | }, 3175 | "execution_count": 158, 3176 | "metadata": {}, 3177 | "output_type": "execute_result" 3178 | } 3179 | ], 3180 | "source": [ 3181 | "hello = macroexpand(:(@interval sin(0.1) + cos(0.2)))" 3182 | ] 3183 | }, 3184 | { 3185 | "cell_type": "markdown", 3186 | "metadata": {}, 3187 | "source": [ 3188 | "This has the structure `sin(interval(0.1)) + cos(interval(0.2))` (although the details are a bit more complicated).\n", 3189 | "\n", 3190 | "So basically the `@interval` macro does something very similar to our `replace!` macro: it walks the tree of the expression; finds parts of it that are of a certain type (numeric literals); and wraps those in the `make_interval` function" 3191 | ] 3192 | }, 3193 | { 3194 | "cell_type": "markdown", 3195 | "metadata": {}, 3196 | "source": [ 3197 | "## Exercise " 3198 | ] 3199 | }, 3200 | { 3201 | "cell_type": "markdown", 3202 | "metadata": {}, 3203 | "source": [ 3204 | "Define a `@checked` macro that uses the `check_arithmetic` function from a previous exercise to wrap convert a piece of code that uses arithmetic operations into the corresponding code that uses the checked versions instead." 3205 | ] 3206 | }, 3207 | { 3208 | "cell_type": "markdown", 3209 | "metadata": {}, 3210 | "source": [ 3211 | "## Macro hygiene" 3212 | ] 3213 | }, 3214 | { 3215 | "cell_type": "markdown", 3216 | "metadata": {}, 3217 | "source": [ 3218 | "An important, but slippery topic with macros is so-called \"hygiene\". This refers to where variables are considered to be defined: inside the macro itself, or where the code that the macro creates is evaluated. We unfortunately do not have time to give this topic a detailed discussion. \n", 3219 | "\n", 3220 | "Basically, we need to allow variables to \"escape\" from the environment of the macro into the surrounding code, when the code returned by a macro refers to these external variables, rather than variables internal to the macro. This is done using `esc`; see the corresponding [Julia documentation](http://docs.julialang.org/en/release-0.4/manual/metaprogramming/#hygiene), and perhaps books on Lisp." 3221 | ] 3222 | } 3223 | ], 3224 | "metadata": { 3225 | "kernelspec": { 3226 | "display_name": "Julia 0.4.6", 3227 | "language": "julia", 3228 | "name": "julia-0.4" 3229 | }, 3230 | "language_info": { 3231 | "file_extension": ".jl", 3232 | "mimetype": "application/julia", 3233 | "name": "julia", 3234 | "version": "0.4.6" 3235 | }, 3236 | "widgets": { 3237 | "state": {}, 3238 | "version": "1.1.2" 3239 | } 3240 | }, 3241 | "nbformat": 4, 3242 | "nbformat_minor": 0 3243 | } 3244 | --------------------------------------------------------------------------------