{ "cells": [ { "cell_type": "markdown", "id": "e0d8818d", "metadata": {}, "source": [ "## CS 201: Running Time of Programs\n", "

\n", "" ] }, { "cell_type": "markdown", "id": "6b96883d", "metadata": {}, "source": [ "

Running time of programs I.

\n", "\n", "

\n", "

Summary.

\n", "\n" ] }, { "cell_type": "markdown", "id": "f3af0183", "metadata": {}, "source": [ "\n", "\n", "Youtube insertion sort\n", "\n", "

\n", "The pseudocode for insertion sort from the Wikipedia article:\n", "

\n",
    "for i ← 1 to length(A)\n",
    "    x ← A[i]\n",
    "    j ← i\n",
    "    while j > 0 and A[j-1] > x\n",
    "        A[j] ← A[j-1]\n",
    "        j ← j - 1\n",
    "    A[j] ← x\n",
    "
" ] }, { "cell_type": "markdown", "id": "b5d7e305", "metadata": {}, "source": [ "This is (slightly) incorrect: the array A has 0-based indexing,\n", "so when i = length(A), the array reference A[i] will be out of\n", "bounds; imagine the first line corrected to have length(A)-1 instead\n", "of length(A).\n", "We analyzed this program by thinking of it translated\n", "into TC-201 assembly language and estimating the number of TC-201\n", "assembly language instructions that would be executed to\n", "sort an array of n numbers." ] }, { "cell_type": "markdown", "id": "32b2529e", "metadata": {}, "source": [ "

\n", "The new element here is how to account for the time to access\n", "the array A.\n", "In this pseudocode, the array A is a finite sequence of mutable\n", " elements (like a vector in Racket) each of which is accessed by\n", "its index, a number from 0 to the length of the array minus 1.\n", " \n", "

\n", " Here are examples of Racket vectors." ] }, { "cell_type": "code", "execution_count": 1, "id": "b329ea77", "metadata": {}, "outputs": [], "source": [ "(define v (make-vector 5 1))" ] }, { "cell_type": "code", "execution_count": 2, "id": "9648e25c", "metadata": {}, "outputs": [ { "data": { "text/html": [ "'#(1 1 1 1 1)" ], "text/plain": [ "'#(1 1 1 1 1)" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "v" ] }, { "cell_type": "code", "execution_count": 3, "id": "2e0c4e7c", "metadata": {}, "outputs": [ { "data": { "text/html": [ "#t" ], "text/plain": [ "#t" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(vector? v)" ] }, { "cell_type": "code", "execution_count": 4, "id": "31632bc9", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "length: contract violation\n", " expected: list?\n", " given: '#(1 1 1 1 1)\n" ] } ], "source": [ "(length v)" ] }, { "cell_type": "code", "execution_count": 5, "id": "128ec03d", "metadata": {}, "outputs": [ { "data": { "text/html": [ "5" ], "text/plain": [ "5" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(vector-length v)" ] }, { "cell_type": "code", "execution_count": 6, "id": "4c38c624", "metadata": {}, "outputs": [ { "data": { "text/html": [ "1" ], "text/plain": [ "1" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(vector-ref v 0)" ] }, { "cell_type": "code", "execution_count": 7, "id": "6cf13e9f", "metadata": {}, "outputs": [], "source": [ "(vector-set! v 0 8)" ] }, { "cell_type": "code", "execution_count": 8, "id": "3ddb1e36", "metadata": {}, "outputs": [ { "data": { "text/html": [ "8" ], "text/plain": [ "8" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(vector-ref v 0)" ] }, { "cell_type": "markdown", "id": "34c89b9d", "metadata": {}, "source": [ "The vector-set! bang (!) suffix indicates that the function has a side-effect.\n", "It can alter the contents of the vector.\n", "

\n", " The important point here is that vector-length and vector-ref are constant time operations, $O(1)$,\n", " unlike length and list-ref for lists which are $O(n)$.\n", " The list operations must traverse the list from left to right. The vector \n", " operations know exactly where to go, based on the index. " ] }, { "cell_type": "markdown", "id": "776442b5", "metadata": {}, "source": [ "

TC-201 Sort

\n", "\n", "\n", "\n", "If we consider a TC-201 implementation of an array A of numbers\n", "we could allocate a number of contiguous memory registers\n", "equal to the length of the array, and store the numbers in\n", "order in those memory locations.\n", "In addition, we would store the address of the start of\n", "the array in another memory location, call it astart.\n", "Then the TC-201 instructions to find the value of $A[i]$\n", "would load astart into the accumulator, add the value of i\n", "to it, and store it in a temporary location, say temp,\n", "and then execute loadi temp to get the value of $A[i]$ into\n", "the accumulator.\n", "This is a constant number of TC-201 instructions to access any\n", "of the elements of the array.\n", "To change the value of $A[i]$, we could use a similar address calculation\n", "to get the memory address of the i-th element of $A$ into temp,\n", "and then execute load value and storei temp to change the\n", "value of $A[i]$ to the number in the memory register value.\n", "This is similarly a constant number of instructions.\n", "Thus it makes sense to count array references as constant time operations.\n" ] }, { "cell_type": "markdown", "id": "f9953d3d", "metadata": {}, "source": [ "

\n", "With that understanding of array references, \n", "a straightforward translation of a for-loop into TC-201 instructions,\n", "and\n", "the observation that assignment, addition, subtraction and comparison\n", "are constant time operations,\n", "our conclusions about the running time of this\n", "implementation of insertion sort were: \n", "best case time of Theta(n) (Θ(n))\n", "and worst case time of Theta(n2) (Θ(n2)).\n", "\n", "

\n", "See tc201sort.rkt. We also modified \n", "simulate-lite to return how many steps were executed.\n", "\n", "

\n", "Please see the notes:\n", "\n", "Running time of a program for a simpler example.\n" ] }, { "cell_type": "markdown", "id": "99c9ada6", "metadata": {}, "source": [ "

Running time of programs II.

\n", "\n", "

\n", "

Summary.

\n", "\n", "

\n", "\n", "

\n", "Please see the notes:\n", "\n", "Running time of a program and\n", "\n", "List representation.\n", "For the insert procedure, see \n", "\n", "Sorting.\n" ] }, { "cell_type": "markdown", "id": "24efe880", "metadata": {}, "source": [ "

Closures

\n", "\n", "See Lexical Scope and Function Closures\n", "\n", "

\n", "Closures are a way of providing a local environment for a function definition that \n", " is distinct from the global environment. Here is an example - first with a global\n", " scope and then with a local, lexical scope." ] }, { "cell_type": "code", "execution_count": 9, "id": "d4906910", "metadata": {}, "outputs": [], "source": [ "(define (make-global-adder)\n", " (lambda (y) (+ x y)))" ] }, { "cell_type": "code", "execution_count": 10, "id": "e4fe5bd5", "metadata": {}, "outputs": [], "source": [ "(define x 10)" ] }, { "cell_type": "code", "execution_count": 11, "id": "4a74d58d", "metadata": {}, "outputs": [], "source": [ "(define addx (make-global-adder))" ] }, { "cell_type": "code", "execution_count": 12, "id": "c298ea90", "metadata": {}, "outputs": [ { "data": { "text/html": [ "10" ], "text/plain": [ "10" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "x" ] }, { "cell_type": "code", "execution_count": 13, "id": "5d21f25f", "metadata": {}, "outputs": [ { "data": { "text/html": [ "13" ], "text/plain": [ "13" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(addx 3)" ] }, { "cell_type": "code", "execution_count": 14, "id": "c20ea29a", "metadata": {}, "outputs": [], "source": [ "(define x 20)" ] }, { "cell_type": "code", "execution_count": 15, "id": "2cebcf97", "metadata": {}, "outputs": [ { "data": { "text/html": [ "23" ], "text/plain": [ "23" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(addx 3)" ] }, { "cell_type": "markdown", "id": "68f15790", "metadata": {}, "source": [ "The function addx depends on the current value of x, rather than\n", "the value of x at the time the function was defined. Here is another (better) way." ] }, { "cell_type": "code", "execution_count": 16, "id": "38e9ce98", "metadata": {}, "outputs": [], "source": [ "(define (make-adder x)\n", " (lambda (y) (+ x y)))" ] }, { "cell_type": "code", "execution_count": 17, "id": "9f755f86", "metadata": {}, "outputs": [], "source": [ "(define add2 (make-adder 2))\n", "(define add3 (make-adder 3))" ] }, { "cell_type": "code", "execution_count": 18, "id": "f72f66f0", "metadata": {}, "outputs": [ { "data": { "text/html": [ "10" ], "text/plain": [ "10" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(add2 8)" ] }, { "cell_type": "code", "execution_count": 19, "id": "3bbfb9a0", "metadata": {}, "outputs": [ { "data": { "text/html": [ "11" ], "text/plain": [ "11" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(add3 8)" ] }, { "cell_type": "markdown", "id": "b197dc91", "metadata": {}, "source": [ "In make-adder the variable x is local and in lexical scope for\n", "the lambda expression. When the lambda expression executes, it uses the value of x that we in effect when make-adder was called." ] }, { "cell_type": "markdown", "id": "6cfc5598", "metadata": {}, "source": [ "make-adder creates a closure that combines a local environment, defining x, with a function definition. We will see that closures provide a way to \n", "implement object oriented programming, where an object contains both data and methods (procedures)." ] }, { "cell_type": "markdown", "id": "8e147292", "metadata": {}, "source": [ "

Running time of programs III.

\n", "\n", "

\n", "Summary. Running times of insertion sort and merge sort.\n", "

\n", "\n", "

\n", "Please see the notes:\n", "\n", "Sorting.\n" ] }, { "cell_type": "markdown", "id": "36ded973", "metadata": {}, "source": [ "Above, we used a modified version of simulate-lite to measure how many steps were executed by our TC201 program.\n", "

\n", " Racket has procedures to measure the execution time of your code. First, we define three \n", " procedures to sum up the first n integers. The first is the vanilla recursive definition." ] }, { "cell_type": "code", "execution_count": 20, "id": "5b310006", "metadata": {}, "outputs": [], "source": [ "(define (sum1 n)\n", " (if (zero? n)\n", " 0\n", " (+ n (sum1 (- n 1)))))" ] }, { "cell_type": "markdown", "id": "76fcdb50", "metadata": {}, "source": [ "The second is the tail-recursive version." ] }, { "cell_type": "code", "execution_count": 21, "id": "583353cf", "metadata": {}, "outputs": [], "source": [ "(define (sum2 n [result 0])\n", " (if (zero? n)\n", " result\n", " (sum2 (- n 1) (+ n result))))" ] }, { "cell_type": "markdown", "id": "c3944a77", "metadata": {}, "source": [ "The third is Gauss's closed form definition." ] }, { "cell_type": "code", "execution_count": 22, "id": "e4e27d07", "metadata": {}, "outputs": [], "source": [ "(define (sum3 n)\n", " (/ (* n (+ n 1))\n", " 2))" ] }, { "cell_type": "markdown", "id": "99e2545d", "metadata": {}, "source": [ "We use the Racket procedure time-apply to measure the execution time." ] }, { "cell_type": "code", "execution_count": 23, "id": "8a68fb8d", "metadata": {}, "outputs": [ { "data": { "text/html": [ "'(500000500000)" ], "text/plain": [ "'(500000500000)" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/html": [ "159" ], "text/plain": [ "159" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/html": [ "160" ], "text/plain": [ "160" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/html": [ "73" ], "text/plain": [ "73" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(time-apply sum1 '(1000000))" ] }, { "cell_type": "markdown", "id": "99afc498", "metadata": {}, "source": [ "time-apply returns four values: a list containing the result(s) of applying proc to the arguments in lst, the number of milliseconds of CPU time required to obtain this result, the number of “real” milliseconds required for the result, and the number of milliseconds of CPU time (included in the first result) spent on garbage collection." ] }, { "cell_type": "code", "execution_count": 24, "id": "c652b121", "metadata": {}, "outputs": [ { "data": { "text/html": [ "'(500000500000)" ], "text/plain": [ "'(500000500000)" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/html": [ "6" ], "text/plain": [ "6" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/html": [ "6" ], "text/plain": [ "6" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/html": [ "0" ], "text/plain": [ "0" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(time-apply sum2 '(1000000))" ] }, { "cell_type": "code", "execution_count": 25, "id": "1b87abf7", "metadata": {}, "outputs": [ { "data": { "text/html": [ "'(500000500000)" ], "text/plain": [ "'(500000500000)" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/html": [ "0" ], "text/plain": [ "0" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/html": [ "0" ], "text/plain": [ "0" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/html": [ "0" ], "text/plain": [ "0" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(time-apply sum3 '(1000000))" ] }, { "cell_type": "markdown", "id": "ea3ae2c8", "metadata": {}, "source": [ "Tail-recursion is considerably faster than plain recursion, and require no garbage collection. That means that it does not run out of free memory.\n", "

\n", "The closed form solution is virtually instantaneous. We need a more fine grained measure of \n", " time. We will use Racket's current-inexact-milliseconds function." ] }, { "cell_type": "code", "execution_count": 26, "id": "0cfa8c22", "metadata": {}, "outputs": [], "source": [ "(define (timeit func args)\n", " (let ((start (current-inexact-milliseconds))\n", " (val (apply func args))\n", " (end (current-inexact-milliseconds)))\n", " (list val (- end start))))" ] }, { "cell_type": "code", "execution_count": 27, "id": "36ff0c32", "metadata": {}, "outputs": [ { "data": { "text/html": [ "'(500000500000 150.05908203125)" ], "text/plain": [ "'(500000500000 150.05908203125)" ] }, "execution_count": 27, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(timeit sum1 '(1000000))" ] }, { "cell_type": "code", "execution_count": 28, "id": "f9fd3eee", "metadata": {}, "outputs": [ { "data": { "text/html": [ "'(500000500000 6.009033203125)" ], "text/plain": [ "'(500000500000 6.009033203125)" ] }, "execution_count": 28, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(timeit sum2 '(1000000))" ] }, { "cell_type": "code", "execution_count": 29, "id": "4e20f93b", "metadata": {}, "outputs": [ { "data": { "text/html": [ "'(500000500000 0.0)" ], "text/plain": [ "'(500000500000 0.0)" ] }, "execution_count": 29, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(timeit sum3 '(1000000))" ] }, { "cell_type": "markdown", "id": "e31a39fd", "metadata": {}, "source": [ "We will revisit these methods below in the section on plotting the results." ] }, { "cell_type": "markdown", "id": "e5b9c2e2", "metadata": {}, "source": [ "

Mutators (and a little bit about objects).

\n", "\n", "

\n", "

Summary.

\n", "
  • Representing an \"object\" as a procedure with local data.\n", "
  • The Racket mutator set! and how to use it to implement\n", "a counter object.\n", "

    \n", "\n", "

    \n", "This topic is useful for solving problem 7 in hw 8.\n", "

    \n", "Please see the notes:\n", "\n", "Environments in Racket and the Mutator set!.\n", "

    \n", "counter.rkt\n" ] }, { "cell_type": "markdown", "id": "48742502", "metadata": {}, "source": [ "In a setting of pure functional programming, calling a procedure\n", "with the same arguments will always produce the same result.\n", "This is not the case with Racket's library procedure random.\n", "Calling (random 10) might return 6 one time and 0 the next.\n", "The random procedure has some local state that changes with\n", "each call and allows it to produce different responses to\n", "the same argument.\n", "An \"object\" in the sense of object oriented programming can\n", "be considered to be a bundle of data and procedures (or \"methods\")\n", "to operate on the data.\n", "We can represent an object in Racket as a procedure with\n", "local state.\n", "\n", "\n", "

    \n", " Before we do that, we look at one of Racket's mutators, namely set! - \n", " pronounced set-bang.\n", "The exclamation point is part of the name of the procedure, and\n", "is a convention to indicate that the procedure is a mutator, that is,\n", "changes the value of a variable or other data.\n", " The form of set! is\n", "

    \n",
        "    (set! variable expression)\n",
        "
    \n", "The expression is evaluated to get a value, and\n", "then the variable is looked up in the relevant environment\n", "(just as we would look it up to find its value) -- in that\n", "environment its binding is changed to the value of the\n", "expression.\n", "Note that the variable must already be defined in order\n", "to use it in a set! expression.\n", "Attempting to set! a variable that is not defined is an error.\n", "This is analogous to having to declare a variable before it\n", "can have a value assigned to it.\n", "

    " ] }, { "cell_type": "markdown", "id": "a236f47b", "metadata": {}, "source": [ "

    \n", " Example of the use of set! in the top-level environment." ] }, { "cell_type": "code", "execution_count": 30, "id": "dedc14c1", "metadata": {}, "outputs": [], "source": [ "(define count 0)" ] }, { "cell_type": "code", "execution_count": 31, "id": "5e1e7eef", "metadata": {}, "outputs": [ { "data": { "text/html": [ "0" ], "text/plain": [ "0" ] }, "execution_count": 31, "metadata": {}, "output_type": "execute_result" } ], "source": [ "count" ] }, { "cell_type": "code", "execution_count": 32, "id": "756c5e00", "metadata": {}, "outputs": [], "source": [ "(set! count (+ 1 count))" ] }, { "cell_type": "code", "execution_count": 33, "id": "51d44f8d", "metadata": {}, "outputs": [ { "data": { "text/html": [ "1" ], "text/plain": [ "1" ] }, "execution_count": 33, "metadata": {}, "output_type": "execute_result" } ], "source": [ "count" ] }, { "cell_type": "markdown", "id": "effef060", "metadata": {}, "source": [ "Evaluating the expression (define count 0) adds count to\n", "the top-level environment with its value bound to 0.\n", "Then evaluating the expression (set! count (+ 1 count))\n", "evaluates the expression (+ 1 count) in the usual way to\n", "get $1$, and looks up count, finding it in the top-level\n", "environment, where its value is currently $0$.\n", "The value of count is changed to $1$ in the top-level\n", "environment; now when its value is looked up, it will be $1$.\n", "\n", "\n", "

    \n", "This behavior is enough to define a simple counter procedure\n", "which will return different values for the same arguments,\n", " depending on the value of count.\n", "We write the following procedure." ] }, { "cell_type": "code", "execution_count": 34, "id": "118e4962", "metadata": {}, "outputs": [], "source": [ "(define (counter cmd)\n", " (case cmd\n", " [(increment!) (set! count (+ 1 count))]\n", " [(zero!) (set! count 0)])\n", " count)" ] }, { "cell_type": "code", "execution_count": 35, "id": "b9a6a807", "metadata": {}, "outputs": [ { "data": { "text/html": [ "2" ], "text/plain": [ "2" ] }, "execution_count": 35, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(counter 'increment!)" ] }, { "cell_type": "code", "execution_count": 36, "id": "9a78d8fb", "metadata": {}, "outputs": [ { "data": { "text/html": [ "3" ], "text/plain": [ "3" ] }, "execution_count": 36, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(counter 'increment!)" ] }, { "cell_type": "code", "execution_count": 37, "id": "7a9f9ec0", "metadata": {}, "outputs": [ { "data": { "text/html": [ "4" ], "text/plain": [ "4" ] }, "execution_count": 37, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(counter 'increment!)" ] }, { "cell_type": "code", "execution_count": 38, "id": "b6857c3f", "metadata": {}, "outputs": [ { "data": { "text/html": [ "4" ], "text/plain": [ "4" ] }, "execution_count": 38, "metadata": {}, "output_type": "execute_result" } ], "source": [ "count" ] }, { "cell_type": "code", "execution_count": 39, "id": "ce05c62e", "metadata": {}, "outputs": [ { "data": { "text/html": [ "0" ], "text/plain": [ "0" ] }, "execution_count": 39, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(counter 'zero!)" ] }, { "cell_type": "code", "execution_count": 40, "id": "640afbf2", "metadata": {}, "outputs": [ { "data": { "text/html": [ "0" ], "text/plain": [ "0" ] }, "execution_count": 40, "metadata": {}, "output_type": "execute_result" } ], "source": [ "count" ] }, { "cell_type": "markdown", "id": "cff3a28f", "metadata": {}, "source": [ "Note that when this procedure refers to count,\n", "the search to find the relevant environment finds it\n", "in the top-level environment, (where we assume we\n", "previously defined it and incremented it to 1.)\n", "Thus in this case the variable count functions as\n", "a \"global\" variable, accessible everywhere in the\n", "file.\n", "(We could also have written counter without the case\n", "statement, using a cond expression and equal? tests.)\n", "\n", "\n", "

    \n", "One property we might want for an object is that its\n", "data is not global, but local and private to the\n", "object, so that it cannot be read or changed without\n", "invoking the \"methods\" (or procedures) associated with\n", "the object.\n", "We can achieve this goal with the following definition.\n", "Assume that we have re-entered the Racket interpreter\n", "afresh, so that the preceding definition of count and\n", "counter are gone.\n" ] }, { "cell_type": "code", "execution_count": 41, "id": "32aeb4a3", "metadata": {}, "outputs": [], "source": [ "(define counter1\n", " (let ((count 0))\n", " (lambda (cmd)\n", " (case cmd\n", " [(increment!) (set! count (+ 1 count))]\n", " [(zero!) (set! count 0)])\n", " count)))" ] }, { "cell_type": "code", "execution_count": 42, "id": "936da2cf", "metadata": {}, "outputs": [ { "data": { "text/html": [ "1" ], "text/plain": [ "1" ] }, "execution_count": 42, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(counter1 'increment!)" ] }, { "cell_type": "code", "execution_count": 43, "id": "02a0178c", "metadata": {}, "outputs": [ { "data": { "text/html": [ "2" ], "text/plain": [ "2" ] }, "execution_count": 43, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(counter1 'increment!)" ] }, { "cell_type": "code", "execution_count": 44, "id": "84380491", "metadata": {}, "outputs": [ { "data": { "text/html": [ "0" ], "text/plain": [ "0" ] }, "execution_count": 44, "metadata": {}, "output_type": "execute_result" } ], "source": [ "count" ] }, { "cell_type": "markdown", "id": "47980975", "metadata": {}, "source": [ "This creates a procedure named counter1 with a private local\n", "variable count, whose value can only be inspected\n", "and changed by calls to the procedure.\n", "This protects against other procedures accidentally or\n", "deliberately changing the value of count other than through\n", "the interface provided by this procedure.\n", "For an analysis of (a variant of) this procedure, and\n", "a higher-level counter-creation procedure, in terms of\n", "Racket environments, please see the notes:\n", "\n", "Environments in Racket and the Mutator set!.\n", "

    \n", "\n", "

    \n", "If we arrange the lets and lambdas in a different fashion,\n", "we get a counter procedure that UTTERLY FAILS at its task." ] }, { "cell_type": "code", "execution_count": 45, "id": "e2c106f1", "metadata": {}, "outputs": [], "source": [ "(define not-a-counter\n", " (lambda (cmd)\n", " (let ((count 0))\n", " (case cmd\n", " [(increment!) (set! count (+ 1 count)) count]\n", " [(zero!) (set! count 0) count]))))" ] }, { "cell_type": "markdown", "id": "aa1ac640", "metadata": {}, "source": [ "As examples of the behavior of this procedure, we have the\n", "following." ] }, { "cell_type": "code", "execution_count": 46, "id": "9d7f8797", "metadata": {}, "outputs": [ { "data": { "text/html": [ "1" ], "text/plain": [ "1" ] }, "execution_count": 46, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(not-a-counter 'increment!)" ] }, { "cell_type": "code", "execution_count": 47, "id": "8f710795", "metadata": {}, "outputs": [ { "data": { "text/html": [ "0" ], "text/plain": [ "0" ] }, "execution_count": 47, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(not-a-counter 'zero!)" ] }, { "cell_type": "code", "execution_count": 48, "id": "6629cfc8", "metadata": {}, "outputs": [ { "data": { "text/html": [ "1" ], "text/plain": [ "1" ] }, "execution_count": 48, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(not-a-counter 'increment!)" ] }, { "cell_type": "code", "execution_count": 49, "id": "09e6b802", "metadata": {}, "outputs": [ { "data": { "text/html": [ "1" ], "text/plain": [ "1" ] }, "execution_count": 49, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(not-a-counter 'increment!)" ] }, { "cell_type": "markdown", "id": "d5ea37e5", "metadata": {}, "source": [ "Think about what is happening in terms of environments to\n", "keep this from behaving like a counter." ] }, { "cell_type": "code", "execution_count": 50, "id": "cd1240d4", "metadata": {}, "outputs": [], "source": [ "(define counter2\n", " (let ((count 0))\n", " (lambda cmd\n", " (if (null? cmd)\n", " count\n", " (begin\n", " (case (car cmd)\n", " [(increment!) (set! count (+ 1 count))]\n", " [(zero!) (set! count 0)])\n", " count)))))" ] }, { "cell_type": "code", "execution_count": 51, "id": "c761551b", "metadata": {}, "outputs": [ { "data": { "text/html": [ "0" ], "text/plain": [ "0" ] }, "execution_count": 51, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(counter2)" ] }, { "cell_type": "code", "execution_count": 52, "id": "6ef1c7a5", "metadata": {}, "outputs": [ { "data": { "text/html": [ "1" ], "text/plain": [ "1" ] }, "execution_count": 52, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(counter2 'increment!)" ] }, { "cell_type": "code", "execution_count": 53, "id": "b07b5bb4", "metadata": {}, "outputs": [ { "data": { "text/html": [ "2" ], "text/plain": [ "2" ] }, "execution_count": 53, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(counter2 'increment!)" ] }, { "cell_type": "code", "execution_count": 54, "id": "7dcfb6b7", "metadata": {}, "outputs": [ { "data": { "text/html": [ "2" ], "text/plain": [ "2" ] }, "execution_count": 54, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(counter2)" ] }, { "cell_type": "code", "execution_count": 55, "id": "19a79d22", "metadata": {}, "outputs": [ { "data": { "text/html": [ "0" ], "text/plain": [ "0" ] }, "execution_count": 55, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(counter2 'zero!)" ] }, { "cell_type": "code", "execution_count": 56, "id": "dd491c52", "metadata": {}, "outputs": [ { "data": { "text/html": [ "0" ], "text/plain": [ "0" ] }, "execution_count": 56, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(counter2)" ] }, { "cell_type": "markdown", "id": "68019aa2", "metadata": {}, "source": [ "

    make-counter

    \n", "\n", "Now we will define a function that creates objects like counter2, but with the option of specifying any initial value, not just $0$." ] }, { "cell_type": "code", "execution_count": 57, "id": "87e71268", "metadata": {}, "outputs": [], "source": [ "(define make-counter\n", " (lambda (count)\n", " (lambda (command)\n", " (case command\n", " ((zero!) (set! count 0) count)\n", " ((increment!) (set! count (+ 1 count)) count)\n", " ((value) count)\n", " (else 'error)))))" ] }, { "cell_type": "code", "execution_count": 58, "id": "973d8137", "metadata": {}, "outputs": [], "source": [ "(define c1 (make-counter 10))" ] }, { "cell_type": "code", "execution_count": 59, "id": "a77fe420", "metadata": {}, "outputs": [ { "data": { "text/html": [ "10" ], "text/plain": [ "10" ] }, "execution_count": 59, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(c1 'value)" ] }, { "cell_type": "code", "execution_count": 60, "id": "3aef8587", "metadata": {}, "outputs": [ { "data": { "text/html": [ "11" ], "text/plain": [ "11" ] }, "execution_count": 60, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(c1 'increment!)" ] }, { "cell_type": "code", "execution_count": 61, "id": "46615b30", "metadata": {}, "outputs": [ { "data": { "text/html": [ "0" ], "text/plain": [ "0" ] }, "execution_count": 61, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(c1 'zero!)" ] }, { "cell_type": "code", "execution_count": 62, "id": "35dc38d8", "metadata": {}, "outputs": [ { "data": { "text/html": [ "'error" ], "text/plain": [ "'error" ] }, "execution_count": 62, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(c1 'x)" ] }, { "cell_type": "markdown", "id": "0c7dafe7", "metadata": {}, "source": [ "### Using a Closure to Create a Stack\n", "\n", "Object-oriented programming creates entities that combine data with procedures.\n", "\n", "In racket, we can use closures for a similar effect. \n", "\n", "Below we create a Racket procedure (make-stack name) that takes a symbol name and returns a Racket procedure that implements a stack object with local storage including a list representing a push down stack - a last-in, first-out (LIFO) data structure, which can process the following commands: \n", "\n", "" ] }, { "cell_type": "code", "execution_count": 168, "id": "f98ec96f", "metadata": {}, "outputs": [], "source": [ "(require racket) ;; need to define procedure first" ] }, { "cell_type": "code", "execution_count": 169, "id": "eb12bc77", "metadata": {}, "outputs": [], "source": [ "(define (make-stack name (data empty))\n", " (let ((stack data)\n", " (size (length data)))\n", " (lambda (cmd . args)\n", " (case cmd\n", " ((name) name)\n", " ((empty?)\n", " (null? stack))\n", " ((copy)\n", " (if (null? args)\n", " 'Error:usage:copy_stack\n", " (make-stack (first args) stack)))\n", " ((show)\n", " stack)\n", " ((equal?)\n", " (if (null? args)\n", " 'Error:usage:equal_stack\n", " (equal? stack ((first args) 'show))))\n", "\n", " ((push)\n", " (if (null? args)\n", " 'Error:usage:push_element\n", " (begin\n", " (set! stack (cons (first args) stack))\n", " (set! size (+ size 1))\n", " (first args))))\n", " ((size) size)\n", "\n", " ((peek)\n", " (if (null? stack)\n", " 'Error:stack-empty\n", " (car stack)))\n", " ((pop)\n", " (if (null? stack)\n", " 'Error:stack-empty\n", " (let ((result (car stack)))\n", " (set! stack (cdr stack))\n", " (set! size (- size 1))\n", " result)))\n", " (else 'invalid-method)\n", " )))) " ] }, { "cell_type": "markdown", "id": "da38363b", "metadata": {}, "source": [ "Examples of using (make-stack name): " ] }, { "cell_type": "code", "execution_count": 170, "id": "65406e19", "metadata": {}, "outputs": [], "source": [ "(define s1 (make-stack 'stack1))" ] }, { "cell_type": "code", "execution_count": 171, "id": "7faa68ea", "metadata": {}, "outputs": [ { "data": { "text/html": [ "#t" ], "text/plain": [ "#t" ] }, "execution_count": 171, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(s1 'empty?)" ] }, { "cell_type": "code", "execution_count": 172, "id": "9c5c0024", "metadata": {}, "outputs": [ { "data": { "text/html": [ "0" ], "text/plain": [ "0" ] }, "execution_count": 172, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(s1 'size)" ] }, { "cell_type": "code", "execution_count": 173, "id": "42309dcd", "metadata": {}, "outputs": [ { "data": { "text/html": [ "'stack1" ], "text/plain": [ "'stack1" ] }, "execution_count": 173, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(s1 'name)" ] }, { "cell_type": "code", "execution_count": 174, "id": "7025b39d", "metadata": {}, "outputs": [ { "data": { "text/html": [ "5" ], "text/plain": [ "5" ] }, "execution_count": 174, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(s1 'push 5)" ] }, { "cell_type": "code", "execution_count": 175, "id": "c1cdcfa6", "metadata": {}, "outputs": [ { "data": { "text/html": [ "#f" ], "text/plain": [ "#f" ] }, "execution_count": 175, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(s1 'empty?)" ] }, { "cell_type": "code", "execution_count": 176, "id": "6172a5c4", "metadata": {}, "outputs": [ { "data": { "text/html": [ "7" ], "text/plain": [ "7" ] }, "execution_count": 176, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(s1 'push 7)" ] }, { "cell_type": "code", "execution_count": 177, "id": "6184c963", "metadata": {}, "outputs": [ { "data": { "text/html": [ "9" ], "text/plain": [ "9" ] }, "execution_count": 177, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(s1 'push 9)" ] }, { "cell_type": "code", "execution_count": 178, "id": "a0603bea", "metadata": {}, "outputs": [ { "data": { "text/html": [ "3" ], "text/plain": [ "3" ] }, "execution_count": 178, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(s1 'size)" ] }, { "cell_type": "code", "execution_count": 179, "id": "806ae9c7", "metadata": {}, "outputs": [ { "data": { "text/html": [ "9" ], "text/plain": [ "9" ] }, "execution_count": 179, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(s1 'pop)" ] }, { "cell_type": "code", "execution_count": 180, "id": "3c87edf6", "metadata": {}, "outputs": [ { "data": { "text/html": [ "2" ], "text/plain": [ "2" ] }, "execution_count": 180, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(s1 'size)" ] }, { "cell_type": "code", "execution_count": 181, "id": "c1da2740", "metadata": {}, "outputs": [], "source": [ "(define s2 (make-stack 'stack2))" ] }, { "cell_type": "code", "execution_count": 182, "id": "24f4a0aa", "metadata": {}, "outputs": [ { "data": { "text/html": [ "'stack1" ], "text/plain": [ "'stack1" ] }, "execution_count": 182, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(s1 'name)" ] }, { "cell_type": "code", "execution_count": 183, "id": "e1f0a47a", "metadata": {}, "outputs": [ { "data": { "text/html": [ "'stack2" ], "text/plain": [ "'stack2" ] }, "execution_count": 183, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(s2 'name)" ] }, { "cell_type": "code", "execution_count": 184, "id": "af4108b6", "metadata": {}, "outputs": [ { "data": { "text/html": [ "'Error:stack-empty" ], "text/plain": [ "'Error:stack-empty" ] }, "execution_count": 184, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(s2 'pop)" ] }, { "cell_type": "code", "execution_count": 185, "id": "18c2ee87", "metadata": {}, "outputs": [ { "data": { "text/html": [ "'Error:usage:push_element" ], "text/plain": [ "'Error:usage:push_element" ] }, "execution_count": 185, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(s2 'push)" ] }, { "cell_type": "code", "execution_count": 186, "id": "cc79560e", "metadata": {}, "outputs": [ { "data": { "text/html": [ "#f" ], "text/plain": [ "#f" ] }, "execution_count": 186, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(s1 'equal? s2)" ] }, { "cell_type": "code", "execution_count": 187, "id": "5cca1ef6", "metadata": {}, "outputs": [ { "data": { "text/html": [ "#t" ], "text/plain": [ "#t" ] }, "execution_count": 187, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(s1 'equal? s1)" ] }, { "cell_type": "code", "execution_count": 188, "id": "d27a940e", "metadata": {}, "outputs": [ { "data": { "text/html": [ "'(7 5)" ], "text/plain": [ "'(7 5)" ] }, "execution_count": 188, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(s1 'show)" ] }, { "cell_type": "code", "execution_count": 189, "id": "fd3395ba", "metadata": {}, "outputs": [ { "data": { "text/html": [ "7" ], "text/plain": [ "7" ] }, "execution_count": 189, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(s1 'peek)" ] }, { "cell_type": "code", "execution_count": 190, "id": "c4d4e326", "metadata": {}, "outputs": [], "source": [ "(define s3 (s1 'copy 'stack3))" ] }, { "cell_type": "code", "execution_count": 191, "id": "0fa98a9e", "metadata": {}, "outputs": [ { "data": { "text/html": [ "'stack3" ], "text/plain": [ "'stack3" ] }, "execution_count": 191, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(s3 'name)" ] }, { "cell_type": "code", "execution_count": 192, "id": "d7ba254f", "metadata": {}, "outputs": [ { "data": { "text/html": [ "'(7 5)" ], "text/plain": [ "'(7 5)" ] }, "execution_count": 192, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(s3 'show)" ] }, { "cell_type": "code", "execution_count": 193, "id": "aba53db0", "metadata": {}, "outputs": [ { "data": { "text/html": [ "#f" ], "text/plain": [ "#f" ] }, "execution_count": 193, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(s3 'empty?)" ] }, { "cell_type": "code", "execution_count": 194, "id": "f52feec9", "metadata": {}, "outputs": [ { "data": { "text/html": [ "#t" ], "text/plain": [ "#t" ] }, "execution_count": 194, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(s1 'equal? s3)" ] }, { "cell_type": "markdown", "id": "dddf1be8", "metadata": {}, "source": [ "What is the run time complexity of the following stack operations, using big-O notation?\n", "\n", "\n" ] }, { "cell_type": "markdown", "id": "45d78818", "metadata": {}, "source": [ "

    Plotting Data in Racket

    \n", "\n", "\n", "See Graphs and Plots in Racket and Plot: Graph Plotting.\n", "\n", "Need to use Dr Racket. Does not seem to work in Jupyter notebooks. In Python, we\n", "can Plot \n", " Function Runtimes.\n", "

    \n", " See also Big-O Cheat Sheet" ] }, { "cell_type": "code", "execution_count": 195, "id": "fd9ad335", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "instantiate-linklet: mismatch;\n", " reference to a variable that is uninitialized;\n", " possibly, bytecode file needs re-compile because dependencies changed\n", " name: has-x-selection?\n", " exporting instance: \"/usr/share/racket/pkgs/gui-lib/mred/private/wx/platform.rkt\"\n", " importing instance: \"/usr/share/racket/pkgs/gui-lib/mred/private/wx/common/clipboard.rkt\"\n", " context...:\n", " temp37_0\n", " for-loop\n", " run-module-instance!125\n", " for-loop\n", " [repeats 1 more time]\n", " run-module-instance!125\n", " for-loop\n", " [repeats 1 more time]\n", " run-module-instance!125\n", " for-loop\n", " [repeats 1 more time]\n", " run-module-instance!125\n", " for-loop\n", " [repeats 1 more time]\n", " run-module-instance!125\n", " for-loop\n", " ...\n" ] } ], "source": [ "(require plot)" ] }, { "cell_type": "code", "execution_count": null, "id": "53578c16", "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "id": "fa48d903", "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Racket", "language": "racket", "name": "racket" }, "language_info": { "codemirror_mode": "scheme", "file_extension": ".rkt", "mimetype": "text/x-racket", "name": "Racket", "pygments_lexer": "racket", "version": "7.4" } }, "nbformat": 4, "nbformat_minor": 5 }