{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "## CS 201: Introduction to Racket\n", "\n", "

\n", "\n", " \n", "See racket.html\n", " \n", "### Acknowledgement\n", " \n", "These notes were originally written by Professor Dana Angluin of the Yale Computer Science Department, who taught CS 201 for many years. The present author has supplemented them over the years with the aim of preserving the tone and rigor of Professor Angluin.\n", " \n", "### Why Racket?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "First, check out the racket web site.\n", "\n", "- Why do we use Racket?\n", "- How many of you have studied Latin?\n", "\n", "Racket, like Latin, is not widely used. It is not practical. \n", "How many classic scholars have been disappointed on their \n", "first visit to Latin America?\n", "\n", "Many western languages have roots in Latin. \n", "\n", "Many languages can trace their syntax and vocabulary to Latin origins. \n", "Studying Latin provides a convenient way for studying language itself and \n", "the way language evolves. In addition, a study of Latin informs our understanding of grammar in general and vocabulary for particular western languages.\n", "\n", "The same is true of Racket. Racket itself is relatively new, dating from the 1990's. \n", "However, Racket is derived from Scheme (1970's) and LISP (1950's). \n", "LISP is one of the oldest and most influential programming languages around. \n", "Learning Latin helps you with French, Spanish, English, Italian, and Romanian. \n", "You will likewise see elements of Racket in many modern languages, such as Python." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "Also, learning a programming language is less like learning a natural language, \n", "like French or Chinese, but more like learning to drive a car. \n", "It should not matter much what kind of car you use when you learn to drive. \n", "(Exception: per P.J. O'Rourke, what is the best kind of car to use when learning \n", "to drive a stick shift?) \n", "You are learning generic driving skills. Once you learn to drive, it should not \n", "matter much what kind of car you are driving. At some point, you will \n", "fly somewhere and rent a car that you have never driven before. Within five \n", "minutes, you will be on the road because most of driving is independent of \n", "any specific model of car.\n", "\n", "The same is true of programming. \n", "The programming skills you learn in this course will transfer \n", "to almost any language you use in the future.\n", "\n", "- Perlis epigram #26: There will always be things we wish to \n", "say in our programs that in all known languages can only be said poorly.\n", "\n", "### Evaluating Expressions\n", "\n", "We consider rules for the evaluation of expressions in Racket.\n", "\n", "We first have to tell Jupyter notebooks which version of racket we want. As it turns out, we are using plain, vanilla racket." ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [], "source": [ "(require racket)\n", "(require racket/base)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You will likely be using Dr. Racket, an IDE (Interactive Development Environment) for racket. Here is an image of a Dr Racket session.\n", "\n", "" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 1. Constants evaluate to themselves.\n", "\n", "Constants are expressions like the numbers 18 and -1, the string \"hi there!\", and the Boolean values #t and #f (for true and false.) As examples of evaluating these expressions in the code field of jupyter notebooks, we have the following." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Integers" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "text/html": [ "18" ], "text/plain": [ "18" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "18" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/html": [ "-1" ], "text/plain": [ "-1" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "-1" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Strings" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\"hi there!\"" ], "text/plain": [ "\"hi there!\"" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "\"hi there!\"" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\"\"" ], "text/plain": [ "\"\"" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "\"\" ;; empty string" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\"this ; is not a comment\"" ], "text/plain": [ "\"this ; is not a comment\"" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "\"this ; is not a comment\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that the semi-colon is the comment character. Everything to the right\n", "of a semi-colon is ignored by racket, unless the semi-colon is in a string.\n", "\n", "#### Booleans (true and false)" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#t" ], "text/plain": [ "#t" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "#t" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#f" ], "text/plain": [ "#f" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "#f" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Numbers are actually a fairly complex subject in most programming languages, \n", "so for the time being we will consider only integers, that is, positive, negative, \n", "and zero whole numbers." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Applications are procedure calls\n", "\n", "The term \"application\" means procedure call. The syntax of an application is as follows.\n", "\n", "

\n",
    "    (proc arg1 ... argn)\n",
    "
\n", "\n", "where proc is an expression (which evaluates to a procedure), and arg1, arg2, ..., argn are expressions (which are evaluated to determine the arguments to the procedure.) \n", "\n", "The math formulas $$f(x)$$ and $$g(x,y,z)$$ \n", "\n", "correspond to the racket expressions\n", "(f x) and (g x y z).\n", "\n", "The rule for evaluating an application is the following\n", "\n", "### 2. An application is evaluated by evaluating the first expression\n", "\n", "(whose value must be a procedure) and each of the rest of the\n", "expressions; the procedure is called with the values of the rest\n", "of the expressions as its actual arguments; when the procedure\n", "returns a value, that value is the value of the application.\n", "As an example of an application, we have the following." ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/html": [ "22" ], "text/plain": [ "22" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(+ 18 4)" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#<procedure:+>" ], "text/plain": [ "#" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "+" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In the application (+ 18 4), + is an identifier which evaluates to a procedure, namely, the built-in procedure to add numbers. The expressions 18 and 4 are constants, which evaluate to themselves according to rule (1). Then the built-in procedure to add numbers is called with the arguments 18 and 4, and returns the value 22, which becomes the value of the whole application expression. Just how + evaluates to a procedure will be seen shortly. As further examples of applications, we have the following." ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "text/html": [ "14" ], "text/plain": [ "14" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(- 18 4)" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/html": [ "18" ], "text/plain": [ "18" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(* 6 3)" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/html": [ "3" ], "text/plain": [ "3" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(quotient 22 6)" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "text/html": [ "4" ], "text/plain": [ "4" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(remainder 22 6)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The identifiers -, *, quotient, and remainder evaluate to the built-in procedures (respectively) subtract, multiply, take the integer quotient, and take the integer remainder. (Using the \"division algorithm\" we divide 6 into 22 getting a quotient of 3 and a remainder of 4, so 22 = 3*6+4.)\n", "\n", "Note that Racket is uncompromising: in an application, the expression for the procedure *always* comes first. What happens when (inevitably) we write something like (18 - 4)? We get an error message, something like:" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "application: not a procedure;\n", " expected a procedure that can be applied to arguments\n", " given: 18\n", " arguments...:\n", " #\n", " 4\n", " context...:\n", " eval-one-top12\n", " /usr/share/racket/pkgs/sandbox-lib/racket/sandbox.rkt:510:0: call-with-custodian-shutdown\n", " /usr/share/racket/collects/racket/private/more-scheme.rkt:148:2: call-with-break-parameterization\n", " .../more-scheme.rkt:261:28\n", " /usr/share/racket/pkgs/sandbox-lib/racket/sandbox.rkt:878:5: loop\n" ] } ], "source": [ "(18 - 4)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Well, I told a lie. There is an exception to the prefix notation using the following dot notation." ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "data": { "text/html": [ "14" ], "text/plain": [ "14" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(18 . - . 4)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We point this out not as an endorsement, but as a warning, much like telling you about poison ivy or rattlesnakes. You should know that they exist, but steer clear of them.\n", "\n", "The first expression after the left parenthesis is 18, which is a number, not a procedure. The error message comes from the code to evaluate an application and is trying to tell us that it expected a procedure but got 18 instead. Feel free to try things out in the interaction window to see what happens -- it will help you to be able to interpret error messages if/when you see them in response to running your own programs.\n", "\n", "The third rule is simple but powerful.\n", "\n", "#### 3. The rules apply recursively.\n", "This means that sub-expressions are evaluated the same way as any other expressions. As an example, consider the following." ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "text/html": [ "22" ], "text/plain": [ "22" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(+ (* 3 6) 4)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The outer set of parenthesis indicate an application. The first expression is +, which evaluates to the built-in procedure to add numbers. The second expression is (* 3 6), which is another application. We need to find its value to know what the first argument to the addition procedure should be. So (recursively) we evaluate this expression. It is also an application, with first expression *, which evaluates to the built-in procedure to multiply numbers. The other expressions are 3 and 6, which are constants and evaluate to themselves. Then the multiplication procedure is called with arguments 3 and 6 and returns 18. Now we know the value of the first argument to the addition procedure. The expression for the second argument is 4, which is a constant that evaluates to itself. Now we can call the addition procedure with arguments 18 and 4, and it returns 22, which becomes the value of the whole expression.\n", "\n", "So what rule can we use to evaluate an identifier like +, *, or remainder? We need the concept of an \"environment\", which is a table with entries consisting of an identifier and its value. The identifier is said to be \"bound\" to the value in the environment. There is a top-level environment already defined when you start Dr. Racket; it contains identifiers such as +, *, and remainder, and gives their values as the built-in procedures to add numbers, multiply numbers, and take the remainder of two numbers. We could picture this top-level environment as a table as follows." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n",
    "     identifier   |    value\n",
    "    --------------------------------\n",
    "    |     +       |   built-in addition procedure\n",
    "    --------------------------------\n",
    "    |     *       |   built-in multiplication procedure\n",
    "    --------------------------------\n",
    "    | remainder   |   built-in remainder procedure\n",
    "    ---------------------------------\n",
    "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "(Of course, there are many more than three entries in the top-level environment.) Then the rule for evaluating identifiers can be stated as follows.\n", "\n", "#### 4. The value of an identifier is found by looking it up in the \"relevant\" environment.\n", "\n", "\n", "This is well defined up to the specification of the \"relevant\" environment. For the moment, there is only one environment we will consider, namely, the top-level environment, so that will be the \"relevant\" one. Returning to the application (+ 18 4), we see that the first expression in the application, the identifier +, is evaluated by looking up its value in the top-level environment, where its value is found to be the built-in addition procedure.\n", "\n", "Can we add entries to the top-level environment? Yes, we can do so using the \"special form\" whose keyword is define. The terminology \"special form\" means an expression that looks somewhat like an application, but actually has a different evaluation rule. The syntax of define is as follows.\n", "\n", "
\n",
    "(define identifier expression)\n",
    "
\n", "The evaluation rule for this expression is as follows." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### 5. A define expression adds the identifier to the relevant environment\n", "with a value obtained by evaluating the expression.\n", "If the identifier is already in the relevant environment, its binding is changed to the value obtained by evaluating the expression. A define expression may look a bit like an assignment statement, but that is the wrong way to think about it. You'll use it in your homework primarily to define procedures in Dr. Racket's definitions window. As an example, if we evaluate the following expression:" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [], "source": [ "(define age 18)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "then the identifier age is added to the top-level environment with the value of the expression 18 (namely 18 itself) as its value. So we can then picture the top-level environment as follows.\n", "
\n",
    "     identifier   |    value\n",
    "    --------------------------------\n",
    "    |     +       |   built-in addition procedure\n",
    "    --------------------------------\n",
    "    |     *       |   built-in multiplication procedure\n",
    "    --------------------------------\n",
    "    | remainder   |   built-in remainder procedure\n",
    "    ---------------------------------\n",
    "    |    age      |   18\n",
    "    ---------------------------------\n",
    "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Then we can evaluate age as follows." ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "data": { "text/html": [ "18" ], "text/plain": [ "18" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "age" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "And we can proceed to use age in other expressions, for example the following." ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [], "source": [ "(define new-age (+ age 4))" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "data": { "text/html": [ "22" ], "text/plain": [ "22" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "new-age" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In this case, another identifier, new-age (note that the dash is part of the identifier), is added to the top-level environment, with the value 22, which is the result of evaluating the expression (+ age 4). In detail, + is evaluated by looking it up in the top-level environment, where its value is found to be the built-in addition procedure. The identifier age is also evaluated by looking it up in the top-level environment, where its value is found to be the number 18. The expression 4 evaluates to itself, and the addition procedure is called with the arguments 18 and 4, and returns the value 22. This value is bound to the identifier new-age in the top-level environment, which we can now picture as follows.\n", "
\n",
    "     identifier   |    value\n",
    "    --------------------------------\n",
    "    |     +       |   built-in addition procedure\n",
    "    --------------------------------\n",
    "    |     *       |   built-in multiplication procedure\n",
    "    --------------------------------\n",
    "    | remainder   |   built-in remainder procedure\n",
    "    ---------------------------------\n",
    "    |    age      |   18\n",
    "    ---------------------------------\n",
    "    |  new-age    |   22\n",
    "    ---------------------------------\n",
    "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "When we evaluate new-age, its value is looked up in the top-level environment and found to be 22. Note that when we quit and restart Dr. Racket, the top-level environment returns to its initial contents, so age and new-age would no longer be in the top-level environment in that case." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

Special Forms

\n", "\n", "How can we tell a \"special form\" from an ordinary application expression? They are both enclosed in parentheses, but a \"special form\" is one of a small number of keywords (e.g., define) as the first expression in the list. Note that parentheses in Racket have a rather different function from their use in mathematics, where they can be used for grouping and are sometimes optional. It is important to retrain your intuition so that you do not think of parentheses in Racket as negligible or innocuous.\n", "\n", "The Racket Guide lists a finite number of special forms. In this course, we will focus on a handful: define, if, cond, and, or, quote, let, let*, case, struct, and lambda. No big drama.\n", "\n", "Try evaluating the expression +, and you will see how Racket represents the built-in procedure +. Try defining * to be + and see what happens. (Remember that you can restore the initial top-level environment by quitting and re-starting Dr. Racket.)\n", "\n" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "(define someage 200)" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/html": [ "400" ], "text/plain": [ "400" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(+ someage someage)" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#<procedure:+>" ], "text/plain": [ "#" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "+" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "(define old+ +)\n", "(define old* *)" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "(define + *)\n", "(define * old+)" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/html": [ "49" ], "text/plain": [ "49" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(+ 7 7)" ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [ { "data": { "text/html": [ "14" ], "text/plain": [ "14" ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(* 7 7)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Enough of this nonsense." ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [], "source": [ "(require racket)\n", "(require racket/base)" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [], "source": [ "(define + old+)\n", "(define * old*)" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "data": { "text/html": [ "49" ], "text/plain": [ "49" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(* 7 7)" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "data": { "text/html": [ "14" ], "text/plain": [ "14" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(+ 7 7)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Can we please write some code?\n", "\n", "We are programmers! When are we going to define our own procedures? There is a special form with keyword lambda that causes a procedure to be created. The syntax is as follows.\n", "
\n",
    "    (lambda (arg1 ... argn) expression)\n",
    "
\n", "The keyword lambda signals that this is a lambda-expression. The (arg1 ... argn) component is a finite sequence of identifiers arg1, arg2, and so on, up to argn, that gives names to the \"formal arguments\" of the procedure. The final expression is the \"body\" of the procedure and indicates how to compute the value of the procedure from its arguments. As an example, we can evaluate the following expression." ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#<procedure>" ], "text/plain": [ "#" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(lambda (n) (+ n 4))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Evaluating this expression creates a procedure of one formal argument, n, that takes a number, adds 4 to it, and returns the resulting sum. In fact, in this case, the procedure is created, but neither applied nor named, so it just drifts off into the ether. We could not only create it, but also apply it, as follows." ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "data": { "text/html": [ "22" ], "text/plain": [ "22" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "((lambda (n) (+ n 4)) 18)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "What happened here? The outer parentheses are an application (after the first left parenthesis there is another left parenthesis, not a keyword.) The first expression, (lambda (n) (+ n 4)), is evaluated, which creates a procedure of one argument that adds 4 to its argument and returns the sum. The second expression, 18, is evaluated (to itself), and the procedure that we just created is called on the argument 18. The procedure adds 4 to 18 and returns 22, which is the value of the application. At least the procedure got applied in this case, but only once, and then got lost in the bit bucket. To use a procedure multiple times, we can give it a name, e.g., by using the define special form. We'll see more details of procedures, and more examples, below.\n", "\n", "You may define functions that take a variable number of arguments." ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [], "source": [ "(define x (lambda (a b) (+ a b)))" ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [ { "data": { "text/html": [ "7" ], "text/plain": [ "7" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(x 3 4)" ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [], "source": [ "(define y (lambda n n))" ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [ { "data": { "text/html": [ "'(1 2 3)" ], "text/plain": [ "'(1 2 3)" ] }, "execution_count": 27, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(y 1 2 3)" ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [ { "data": { "text/html": [ "'(1 2 3 4 5 6)" ], "text/plain": [ "'(1 2 3 4 5 6)" ] }, "execution_count": 28, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(y 1 2 3 4 5 6)" ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [ { "data": { "text/html": [ "'()" ], "text/plain": [ "'()" ] }, "execution_count": 29, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(y)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The single quote is an abbreviation of another special form: quote, which instructs racket not to evaluate its arguments." ] }, { "cell_type": "code", "execution_count": 30, "metadata": {}, "outputs": [ { "data": { "text/html": [ "'(add 3 4)" ], "text/plain": [ "'(add 3 4)" ] }, "execution_count": 30, "metadata": {}, "output_type": "execute_result" } ], "source": [ "'(add 3 4)" ] }, { "cell_type": "code", "execution_count": 31, "metadata": {}, "outputs": [ { "data": { "text/html": [ "'(add 3 4)" ], "text/plain": [ "'(add 3 4)" ] }, "execution_count": 31, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(quote (add 3 4))" ] }, { "cell_type": "code", "execution_count": 32, "metadata": {}, "outputs": [], "source": [ "(define z (lambda n (apply + n)))" ] }, { "cell_type": "code", "execution_count": 33, "metadata": {}, "outputs": [ { "data": { "text/html": [ "7" ], "text/plain": [ "7" ] }, "execution_count": 33, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(z 3 4)" ] }, { "cell_type": "code", "execution_count": 34, "metadata": {}, "outputs": [ { "data": { "text/html": [ "18" ], "text/plain": [ "18" ] }, "execution_count": 34, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(z 3 4 5 6)" ] }, { "cell_type": "code", "execution_count": 35, "metadata": {}, "outputs": [], "source": [ "(define (z2 . n) (apply + n))" ] }, { "cell_type": "code", "execution_count": 36, "metadata": {}, "outputs": [ { "data": { "text/html": [ "18" ], "text/plain": [ "18" ] }, "execution_count": 36, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(z2 3 4 5 6)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We simply follow the lambda keyword with an argument name not enclosed in parens. The apply procedure uses the procedure of its first argument and evaluates the rest of the arguments.\n", "\n", "We can also specify functions with at least n arguments." ] }, { "cell_type": "code", "execution_count": 37, "metadata": {}, "outputs": [], "source": [ "(define w (lambda (a b . c) (+ a b (apply + c))))" ] }, { "cell_type": "code", "execution_count": 38, "metadata": {}, "outputs": [ { "data": { "text/html": [ "3" ], "text/plain": [ "3" ] }, "execution_count": 38, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(w 1 2)" ] }, { "cell_type": "code", "execution_count": 39, "metadata": {}, "outputs": [ { "data": { "text/html": [ "21" ], "text/plain": [ "21" ] }, "execution_count": 39, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(w 1 2 3 4 5 6)" ] }, { "cell_type": "code", "execution_count": 40, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "w: arity mismatch;\n", " the expected number of arguments does not match the given number\n", " expected: at least 2\n", " given: 1\n", " arguments...:\n", " 1\n", " context...:\n", " eval-one-top12\n", " /usr/share/racket/pkgs/sandbox-lib/racket/sandbox.rkt:510:0: call-with-custodian-shutdown\n", " /usr/share/racket/collects/racket/private/more-scheme.rkt:148:2: call-with-break-parameterization\n", " .../more-scheme.rkt:261:28\n", " /usr/share/racket/pkgs/sandbox-lib/racket/sandbox.rkt:878:5: loop\n" ] } ], "source": [ "(w 1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here we define function w to take at least two arguments, but allow more.\n", "See [racket1.rkt](racket1.rkt) for examples of defining racket variables and functions with variable or optional parameters." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Collatz Conjecture\n", "\n", "\n", "\n", "Want to win a Fields Medal? Solve the Collatz Conjecture! (aka, Kakutani's Problem).\n", "We define a function (collatz n) (where n is an arbitrary positive integer) which behaves as follows:\n", "\n", "- If n is even, return n/2.\n", "- If n is odd, return 3n + 1.\n", "\n", "Note: Shizuo Kakutani was a popular Yale Math professor for many years. His daughter, \n", "Michiko Kakutani, who is a Yale graduate, won the Pulitzer Prize for criticism as the New York Times book reviewer.\n", "\n", "Let's write that function in Racket: (collatz n) See \n", "collatz.rkt and notebook and HTML. (Note use of trace and untrace)" ] }, { "cell_type": "code", "execution_count": 42, "metadata": {}, "outputs": [], "source": [ "(define (collatz n)\n", " (if (= (modulo n 2) 0) \n", " (/ n 2)\n", " (+ 1 (* n 3))))" ] }, { "cell_type": "code", "execution_count": 43, "metadata": {}, "outputs": [ { "data": { "text/html": [ "34" ], "text/plain": [ "34" ] }, "execution_count": 43, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(collatz 11)" ] }, { "cell_type": "code", "execution_count": 44, "metadata": {}, "outputs": [ { "data": { "text/html": [ "17" ], "text/plain": [ "17" ] }, "execution_count": 44, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(collatz 34)" ] }, { "cell_type": "code", "execution_count": 45, "metadata": {}, "outputs": [ { "data": { "text/html": [ "52" ], "text/plain": [ "52" ] }, "execution_count": 45, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(collatz 17)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We have used the if special form:\n", "\n", "
\n",
    "(if test then-expression else-expression)\n",
    "
\n", "\n", "If the test is true, then the then-expression is evaluated and returned. If the test is false, the else-expression is evaluated and returned.\n", "\n", "Note that if if were a procedure and not a special form, both the then-expression and the else-expression would always be evaluated. This could be unfortunate.\n", "\n", "
\n",
    "(if under-attack\n",
    "    launch-missiles\n",
    "    take-a-nap)\n",
    "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can have collatz call itself with its own result. " ] }, { "cell_type": "code", "execution_count": 46, "metadata": {}, "outputs": [ { "data": { "text/html": [ "52" ], "text/plain": [ "52" ] }, "execution_count": 46, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(collatz (collatz (collatz 11)))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Next, we use recursion to define a sequence of Collatz numbers, such that the output of each call becomes the input of the next, unless and until you arrive at 1. The conjecture part is to prove that this series will always converge to 1. We define \n", "(c-series n) which uses an if statement and a let statement to create a local variable. The other function, (c-series2 n), does away with the local variable." ] }, { "cell_type": "code", "execution_count": 50, "metadata": {}, "outputs": [], "source": [ "(define (c-series n)\n", " (print n)\n", " (newline)\n", " (if (equal? n 1) 'done\n", " (let ((next (collatz n)))\n", " (c-series next))))" ] }, { "cell_type": "code", "execution_count": 51, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "11\n", "34\n", "17\n", "52\n", "26\n", "13\n", "40\n", "20\n", "10\n", "5\n", "16\n", "8\n", "4\n", "2\n", "1\n" ] }, { "data": { "text/html": [ "'done" ], "text/plain": [ "'done" ] }, "execution_count": 51, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(c-series 11)" ] }, { "cell_type": "code", "execution_count": 52, "metadata": {}, "outputs": [], "source": [ "(define (c-series2 n)\n", " (print n)\n", " (newline)\n", " (if (equal? n 1) 'done\n", " (c-series2 (collatz n))))" ] }, { "cell_type": "code", "execution_count": 53, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "11\n", "34\n", "17\n", "52\n", "26\n", "13\n", "40\n", "20\n", "10\n", "5\n", "16\n", "8\n", "4\n", "2\n", "1\n" ] }, { "data": { "text/html": [ "'done" ], "text/plain": [ "'done" ] }, "execution_count": 53, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(c-series2 11)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## More Racket.\n", "\n", "- How can you tell racket to import a file that has changed since the last time you imported it? Just \"enter!\" it again. It will reload. Note: enter! is not in our jupyter version of racket.\n", "\n", "We recall the following principles of racket:\n", "\n", "- Constants evaluate to themselves: numbers, strings, booleans (and actually procedures!)\n", "- There are many numerical types in racket with corresponding predicates:" ] }, { "cell_type": "code", "execution_count": 54, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#t" ], "text/plain": [ "#t" ] }, "execution_count": 54, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(number? 1)" ] }, { "cell_type": "code", "execution_count": 55, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#f" ], "text/plain": [ "#f" ] }, "execution_count": 55, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(number? \"hello\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Racket has a naming convention that procedures ending in ? are predicates, that is, they ask a yes or no question. Thus, number? means, is my argument a number? By extension, racket programmers may write hungry? meaning are you hungry?" ] }, { "cell_type": "code", "execution_count": 56, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#t" ], "text/plain": [ "#t" ] }, "execution_count": 56, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(complex? 2+3i)" ] }, { "cell_type": "code", "execution_count": 57, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#t" ], "text/plain": [ "#t" ] }, "execution_count": 57, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(real? pi)" ] }, { "cell_type": "code", "execution_count": 58, "metadata": {}, "outputs": [ { "data": { "text/html": [ "3.141592653589793" ], "text/plain": [ "3.141592653589793" ] }, "execution_count": 58, "metadata": {}, "output_type": "execute_result" } ], "source": [ "pi" ] }, { "cell_type": "code", "execution_count": 59, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#t" ], "text/plain": [ "#t" ] }, "execution_count": 59, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(real? pi)" ] }, { "cell_type": "code", "execution_count": 60, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#t" ], "text/plain": [ "#t" ] }, "execution_count": 60, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(real? +inf.0)" ] }, { "cell_type": "code", "execution_count": 61, "metadata": {}, "outputs": [ { "data": { "text/html": [ "-inf.0" ], "text/plain": [ "-inf.0" ] }, "execution_count": 61, "metadata": {}, "output_type": "execute_result" } ], "source": [ "-inf.0" ] }, { "cell_type": "code", "execution_count": 62, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#t" ], "text/plain": [ "#t" ] }, "execution_count": 62, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(rational? 1)" ] }, { "cell_type": "code", "execution_count": 63, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#t" ], "text/plain": [ "#t" ] }, "execution_count": 63, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(integer? 1)" ] }, { "cell_type": "code", "execution_count": 64, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#f" ], "text/plain": [ "#f" ] }, "execution_count": 64, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(integer? +inf.0)" ] }, { "cell_type": "code", "execution_count": 65, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#t" ], "text/plain": [ "#t" ] }, "execution_count": 65, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(integer? 2.0)" ] }, { "cell_type": "code", "execution_count": 66, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#f" ], "text/plain": [ "#f" ] }, "execution_count": 66, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(exact-integer? 2.0)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Representing numbers inside a computer can get complicated. Clearly, pi is an approximation. Racket distinguishes between exact and inexact integers." ] }, { "cell_type": "code", "execution_count": 67, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#t" ], "text/plain": [ "#t" ] }, "execution_count": 67, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(exact-nonnegative-integer? 0)" ] }, { "cell_type": "code", "execution_count": 68, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#f" ], "text/plain": [ "#f" ] }, "execution_count": 68, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(exact-nonnegative-integer? -1)" ] }, { "cell_type": "code", "execution_count": 69, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#f" ], "text/plain": [ "#f" ] }, "execution_count": 69, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(exact-positive-integer? 0)" ] }, { "cell_type": "code", "execution_count": 70, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#t" ], "text/plain": [ "#t" ] }, "execution_count": 70, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(inexact-real? 3.4)" ] }, { "cell_type": "code", "execution_count": 71, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#t" ], "text/plain": [ "#t" ] }, "execution_count": 71, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(inexact-real? 3.5)" ] }, { "cell_type": "code", "execution_count": 71, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#t" ], "text/plain": [ "#t" ] }, "execution_count": 71, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(flonum? 3.4)" ] }, { "cell_type": "code", "execution_count": 72, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#t" ], "text/plain": [ "#t" ] }, "execution_count": 72, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(double-flonum? 3.4)" ] }, { "cell_type": "code", "execution_count": 73, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#t" ], "text/plain": [ "#t" ] }, "execution_count": 73, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(double-flonum? 3.4444444444)" ] }, { "cell_type": "code", "execution_count": 75, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#f" ], "text/plain": [ "#f" ] }, "execution_count": 75, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(single-flonum? 3.4)" ] }, { "cell_type": "code", "execution_count": 76, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#t" ], "text/plain": [ "#t" ] }, "execution_count": 76, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(zero? 0.0)" ] }, { "cell_type": "code", "execution_count": 77, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#t" ], "text/plain": [ "#t" ] }, "execution_count": 77, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(positive? 1)" ] }, { "cell_type": "code", "execution_count": 78, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#f" ], "text/plain": [ "#f" ] }, "execution_count": 78, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(negative? 1)" ] }, { "cell_type": "code", "execution_count": 74, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#f" ], "text/plain": [ "#f" ] }, "execution_count": 74, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(even? 1)" ] }, { "cell_type": "code", "execution_count": 80, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#t" ], "text/plain": [ "#t" ] }, "execution_count": 80, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(odd? 1)" ] }, { "cell_type": "code", "execution_count": 75, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#f" ], "text/plain": [ "#f" ] }, "execution_count": 75, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(exact? pi)" ] }, { "cell_type": "code", "execution_count": 82, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#t" ], "text/plain": [ "#t" ] }, "execution_count": 82, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(inexact? pi)" ] }, { "cell_type": "code", "execution_count": 83, "metadata": {}, "outputs": [ { "data": { "text/html": [ "884279719003555/281474976710656" ], "text/plain": [ "884279719003555/281474976710656" ] }, "execution_count": 83, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(inexact->exact pi)" ] }, { "cell_type": "code", "execution_count": 76, "metadata": {}, "outputs": [ { "data": { "text/html": [ "2" ], "text/plain": [ "2" ] }, "execution_count": 76, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(inexact->exact 2.0)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We will next load the file racket2.rkt and execute the procedure (demo)" ] }, { "cell_type": "code", "execution_count": 77, "metadata": {}, "outputs": [], "source": [ "(define examples\n", " '(\n", " (number? 1)\n", " (complex? 2+3i)\n", " (real? 3.14159)\n", " (real? +inf.0)\n", " (rational? 1)\n", " (integer? 1)\n", " (integer? +inf.0)\n", " (integer? 2.0)\n", " (exact-integer? 2.0)\n", " (exact-nonnegative-integer? 0)\n", " (exact-nonnegative-integer? -1)\n", " (exact-positive-integer? 0)\n", " (inexact-real? 3.4)\n", " (inexact-real? 3.5)\n", " (flonum? 3.4)\n", " (double-flonum? 3.4)\n", " (double-flonum? 3.4444444444)\n", " (single-flonum? 3.4)\n", " (zero? 0.0)\n", " (positive? 1)\n", " (negative? 1)\n", " (even? 1)\n", " (odd? 1)\n", " (exact? 3.14159)\n", " (inexact? 3.14159)\n", " (inexact->exact 3.14159)\n", "\n", " )\n", " )" ] }, { "cell_type": "code", "execution_count": 78, "metadata": {}, "outputs": [], "source": [ "(define (demo)\n", " (map\n", " (lambda (lst)\n", " (list (car lst)\n", " (cadr lst)\n", " '==>\n", " (apply (eval (car lst)) (cdr lst))))\n", " examples))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note: map, list, car, cadr will be covered in the list section below." ] }, { "cell_type": "code", "execution_count": 79, "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/html": [ "'((number? 1 ==> #t) (complex? 2+3i ==> #t) (real? 3.14159 ==> #t) (real? +inf.0 ==> #t) (rational? 1 ==> #t) (integer? 1 ==> #t) (integer? +inf.0 ==> #f) (integer? 2.0 ==> #t) (exact-integer? 2.0 ==> #f) (exact-nonnegative-integer? 0 ==> #t) (exact-nonnegative-integer? -1 ==> #f) (exact-positive-integer? 0 ==> #f) (inexact-real? 3.4 ==> #t) (inexact-real? 3.5 ==> #t) (flonum? 3.4 ==> #t) (double-flonum? 3.4 ==> #t) (double-flonum? 3.4444444444 ==> #t) (single-flonum? 3.4 ==> #f) (zero? 0.0 ==> #t) (positive? 1 ==> #t) (negative? 1 ==> #f) (even? 1 ==> #f) (odd? 1 ==> #t) (exact? 3.14159 ==> #f) (inexact? 3.14159 ==> #t) (inexact->exact 3.14159 ==> 3537115888337719/1125899906842624))" ], "text/plain": [ "'((number? 1 ==> #t) (complex? 2+3i ==> #t) (real? 3.14159 ==> #t) (real? +inf.0 ==> #t) (rational? 1 ==> #t) (integer? 1 ==> #t) (integer? +inf.0 ==> #f) (integer? 2.0 ==> #t) (exact-integer? 2.0 ==> #f) (exact-nonnegative-integer? 0 ==> #t) (exact-nonnegative-integer? -1 ==> #f) (exact-positive-integer? 0 ==> #f) (inexact-real? 3.4 ==> #t) (inexact-real? 3.5 ==> #t) (flonum? 3.4 ==> #t) (double-flonum? 3.4 ==> #t) (double-flonum? 3.4444444444 ==> #t) (single-flonum? 3.4 ==> #f) (zero? 0.0 ==> #t) (positive? 1 ==> #t) (negative? 1 ==> #f) (even? 1 ==> #f) (odd? 1 ==> #t) (exact? 3.14159 ==> #f) (inexact? 3.14159 ==> #t) (inexact->exact 3.14159 ==> 3537115888337719/1125899906842624))" ] }, "execution_count": 79, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(demo)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Recapitulation of Rules for Evaluating Expressions\n", "\n", "\n", "- The leftmost form in a list is evaluated as a procedure, and the remaining elements of the list are passed as arguments to the procedure.\n", "\n", "- The last value is the value returned by the procedure.\n", "\n", "- If you want your procedure to return no value, have the tail position be \n", "(void ...). This may be useful if your function is called merely for its side-effects, such as input/output.\n", "\n", "- The rules apply recursively: (* (+ 9 9) (- 10 2))\n", "\n", "- The value of an identifier is found by looking it up in the relevant environment. This is just a big table. Be careful: You can clobber definitions.\n", "(define + *)\n", "\n", "- A define expression adds the identifier to the relevant environment with a value obtained by evaluating the expression.\n", "\n", "- define is a special form not a procedure. Another special form is lambda which allows you to define a procedure without adding it to the environment.\n", "
\n",
    "(lambda (n) (+ n 4))\n",
    "
\n", "creates a procedure which can be applied to arguments:\n", "
\n",
    "((lambda (n) (+ n 4)) 18)\n",
    "
\n", "\n", "- Note: lambda expressions are available these days in most programming languages, including java, python, ruby, r, and haskell." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "(define x (lambda (n) (+ n 1)))" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "text/html": [ "4" ], "text/plain": [ "4" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(x 3)" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "(define (x2 n) (+ n 1))" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/html": [ "4" ], "text/plain": [ "4" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(x2 3)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

Defining Racket Procedures

\n", "\n", "The evaluation of a lambda expression creates a procedure. Above, we saw how to create and apply a (one time use) procedure in a single expression, for example:" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/html": [ "22" ], "text/plain": [ "22" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "((lambda (n) (+ n 4)) 18)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "But we'd like to be able to re-use our procedures. We can do so by using define, as in the following example." ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "(define plus-four (lambda (n) (+ n 4)))" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/html": [ "10" ], "text/plain": [ "10" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(plus-four 6)" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/html": [ "3" ], "text/plain": [ "3" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(plus-four -1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We'll now look at the details of what happened here. Recall that there is an initial top-level environment when you start Racket, which we may picture as follows.\n", "
\n",
    "     identifier   |    value\n",
    "    --------------------------------\n",
    "                .....\n",
    "    --------------------------------\n",
    "    |     +       |   built-in addition procedure\n",
    "    --------------------------------\n",
    "    |     *       |   built-in multiplication procedure\n",
    "    --------------------------------\n",
    "    | remainder   |   built-in remainder procedure\n",
    "    ---------------------------------\n",
    "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "(Here the dots are included to remind you that there are many other entries in the initial top-level environment.) In the above example, when the define special form is evaluated, the identifier plus-four is added to the top-level environment, and its value is the result of evaluating the expression (lambda (n) (+ n 4)), which is a procedure with formal arguments: n, and body: (+ n 4). So the top-level environment becomes:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n",
    "     identifier   |    value\n",
    "    --------------------------------\n",
    "                .....\n",
    "    --------------------------------\n",
    "    |     +       |   built-in addition procedure\n",
    "    --------------------------------\n",
    "    |     *       |   built-in multiplication procedure\n",
    "    --------------------------------\n",
    "    | remainder   |   built-in remainder procedure\n",
    "    ---------------------------------\n",
    "    |  plus-four  |   user procedure with formal arguments: n\n",
    "    |             |   and body: (+ n 4)\n",
    "    ---------------------------------\n",
    "
\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that the procedure has not been applied to any arguments yet. When the next expression, (plus-four 6), is evaluated, the procedure we just created is applied, as follows. The left parenthesis is not followed by a keyword, so this is an application. Using the rules for an application, the identifier plus-four is looked up (in the top-level environment) and its value is found to be a procedure -- so far, so good. The rest of the expression, in this case just 6, are evaluated, and the procedure is called on the values, again, just 6." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The process of calling the procedure on its argument can now be understood as follows. A *new* local environment is created using the formal arguments of the procedure (here, just the identifier n) and the corresponding actual arguments (here, just the integer 6). We can picture this new local environment as follows.\n", "
\n",
    "     identifier   |    value\n",
    "    --------------------------------\n",
    "    |     n       |      6\n",
    "    --------------------------------\n",
    "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In addition, there is a \"search pointer\" that points from this environment to the top-level environment, which indicates where to look for the value of an identifier that is not found in this environment. On the blackboard, this is just an arrow labeled \"search pointer\" pointing from this environment to the top-level environment. In this medium, we will just use text to indicate the search pointer. So, at this point, the whole environment picture is as follows.\n", "
\n",
    "    * top level environment *\n",
    "    --------------------------------\n",
    "     identifier   |    value\n",
    "    --------------------------------\n",
    "                .....\n",
    "    --------------------------------\n",
    "    |     +       |   built-in addition procedure\n",
    "    --------------------------------\n",
    "    |     *       |   built-in multiplication procedure\n",
    "    --------------------------------\n",
    "    | remainder   |   built-in remainder procedure\n",
    "    ---------------------------------\n",
    "    |  plus-four  |   user procedure with formal arguments: (n)\n",
    "    |             |   and body: (+ n 4)\n",
    "    ---------------------------------\n",
    "\n",
    "\n",
    "    * local environment *  search pointer: top-level environment\n",
    "    --------------------------------\n",
    "     identifier   |    value\n",
    "    --------------------------------\n",
    "    |     n       |      6\n",
    "    --------------------------------\n",
    "
\n", "Now that the local environment is set up, the process of applying the procedure evaluates the body of the procedure, in this case, (+ n 4), in the local environment. Now we can understand the meaning of the \"relevant\" environment in the rule for evaluating identifiers. An expression is evaluated in a current environment; to find the value of an identifier, we first look in the current environment to see if it has a binding there -- if so, that is its value. If not, then we follow the environment's search pointer (if any) to another environment and see if it has a binding there -- if so, that is its value. If not, then we follow that environment's search pointer (if any) to another environment, and so on. If this process reaches the top-level environment (which has no search pointer) and does not find a binding for the identifier there, then an error message is generated. An example of such an error message follows.\n", "\n", "
\n",
    "> x\n",
    " (..... stuff ..............)    x: undefined;\n",
    " cannot reference an identifier before its definition\n",
    "
\n", "\n", "(The \"stuff\" tells you where in the Racket system the error was detected.)\n", "Back to the application in progress: the body of the procedure, that is, the expression (+ n 4), is now evaluated in the local environment just created. The left parenthesis is not followed by a keyword, so this is an application. The first expression, +, is an identifier, and is evaluated by looking it up in the relevant environment. So first we look in the current environment, which is the local environment. It is not there, so we follow the search pointer to the top-level environment, where we find that it has as its value the built-in procedure to add numbers. The expression n is also an identifier, but in this case we find its value in the current environment, namely 6. Finally, 4 is evaluated (to itself) and we call the built-in addition procedure on 6 and 4; it returns 10, which is the value of the expression (+ n 4) in the local environment, and is the value of the application (plus-four 6)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Once the application has been evaluated, what happens to the local environment we just created? It is no longer accessible, and become eligible for \"garbage collection\" or \"recycling\", which means that the Racket system may reclaim the memory that it used for other purposes. Conceptually, it is as though the local environment is erased immediately after the application completes, so that the environment picture returns to its previous situation, as follows.\n", "
\n",
    "\n",
    "     identifier   |    value\n",
    "    --------------------------------\n",
    "                .....\n",
    "    --------------------------------\n",
    "    |     +       |   built-in addition procedure\n",
    "    --------------------------------\n",
    "    |     *       |   built-in multiplication procedure\n",
    "    --------------------------------\n",
    "    | remainder   |   built-in remainder procedure\n",
    "    ---------------------------------\n",
    "    |  plus-four  |   user procedure with formal arguments: (n)\n",
    "    |             |   and body: (+ n 4)\n",
    "    ---------------------------------\n",
    "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that the binding for plus-four in the top-level environment remains as before. If next we evaluate the expression (plus-four -1), the process is repeated analogously, creating a new local environment in which n is bound to -1, with its search pointer pointing to the top-level environment, and the body expression (+ n 4) is evaluated in this local environment and found to have value 3. Once evaluation of this application completes, its local environment is eligible for garbage collection (and may be thought of as erased.)\n", "\n", "Whew! This is a lot of detail, but it is intended to give you an inside view of how the Racket interpreter works, which in turn will enhance your understanding of functional programming. You will seldom need to think about this level of detail while you are writing your procedures in Racket." ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#<procedure:plus-four>" ], "text/plain": [ "#" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "plus-four" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "text/html": [ "9" ], "text/plain": [ "9" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(plus-four 5)" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [], "source": [ "(define n 10)" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/html": [ "21" ], "text/plain": [ "21" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(plus-four 17)" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/html": [ "10" ], "text/plain": [ "10" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "n" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [], "source": [ "(define x 11)" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [], "source": [ "(define plus-five (lambda (n) (+ x 5)))" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "data": { "text/html": [ "16" ], "text/plain": [ "16" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(plus-five 2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Alternate procedure definition syntax.\n", "\n", "We now get a bit of \"syntactic sugar\" -- to let you avoid typing lambda all the time, and to make your code a little prettier, there is an alternate syntax for defining procedures. For example, we could define the plus-four procedure as follows." ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [], "source": [ " (define (plus-four n) (+ n 4))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that the define keyword is not followed by an identifier, but by a parenthesized list of identifiers. The first one is taken to be the procedure name, and the rest of them are taken to be the formal arguments to the procedure. (If there are more than one formal arguments, they are separated by white space, not commas.) This is syntactic sugar in the sense that it is a little more user-friendly than the previous syntax, but is just an abbreviation for it. Though you won't be typing lambda all the time, you should understand how lambda expressions work.\n", "\n", "We'll write some more procedures. We'd like a procedure (last-digit n) that returns the last decimal digit of the positive integer n. As an example of its behavior we have the following.\n", "
\n",
    "> (last-digit 437)\n",
    "7\n",
    "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Rather than picturing what happens in the interaction window, we introduce a shorthand (=>) for the concept that an expression evaluates to a value. Thus, we could indicate the above example by writing the following.\n", "\n", "
\n",
    "(last-digit 437) => 7\n",
    "
\n", "\n", "This is read as follows: the expression (last-digit 437) evaluates to 7. We'll first write it using lambda, and then give the abbreviated definition. Recall that quotient and remainder are built-in procedures giving the quotient and remainder of an integer division. If we divide 437 by 10, we get a quotient of 43 and a remainder of 7, so the remainder of the input and 10 will be exactly the last decimal digit of the input. Hence we can write the last-digit procedure as follows." ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [], "source": [ "(define last-digit\n", " (lambda (x)\n", " (remainder x 10)))" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "data": { "text/html": [ "7" ], "text/plain": [ "7" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(last-digit 437)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that I've used newlines and indentation to aid the readability of this procedure. Dr. Racket will help with indentation -- if you highlight a region of code and press tab, it will indent the code according to its parenthesis nesting. This can help you find errors in your parenthesis nesting. Note also that I chose to call the formal argument x -- the particular identifiers you choose for formal arguments are not important, except that naming things well will help you program well. In the alternate procedure definition syntax, this could be rewritten as follows." ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [], "source": [ "(define (last-digit x)\n", " (remainder x 10))" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "data": { "text/html": [ "8" ], "text/plain": [ "8" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(last-digit 438)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## The factorial function.\n", "\n", "We come to our first recursive procedure definition. (Not counting the collatz series!) Recall from earlier educational experiences the definition of the factorial function, n!, for positive integers.\n", "
\n",
    "    n!   =    if n = 1 then the value is 1\n",
    "              otherwise, the value is n * (n-1)!\n",
    "\n",
    "
\n", "So, for example, to compute 4!, we have the following.\n", "
\n",
    "    4!   =    4 x 3!\n",
    "         =    4 x 3 x 2!\n",
    "         =    4 x 3 x 2 x 1!\n",
    "         =    4 x 3 x 2 x 1\n",
    "         =    24\n",
    "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The case n = 1 is a \"base case\" -- it returns a value (1) without any further references to the factorial function. The other case (n not equal to 1) is a \"recursive case\" -- we need to compute the factorial function on another value (namely (n-1)) in order to find the value of the factorial function on n.\n", "\n", "We'd like to write a procedure (factorial n) to compute the value of n! For example, we'd like (factorial 4) => 24. In order to write a procedure based on the definition above, we need two things: a way to test whether the input is equal to 1 or not, and a way to do one thing if it is, and something else if it is not. The testing can be done by using built-in predicates. A \"predicate\" is just a procedure that returns either #t (for true) or #f\n", "(for false). The built-in predicates =, <, >, <=, >= can be used to compare two numbers to determine whether they are equal (=), or the first is less than (<), greater than (>), less than or equal to (<=), or greater than or equal to (>=), the second. As examples, we have the following. (Recall that the procedure invariably comes first.)" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#f" ], "text/plain": [ "#f" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(= 3 4)" ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#t" ], "text/plain": [ "#t" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(= (+ 1 3) 4)" ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#t" ], "text/plain": [ "#t" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(< 3 4)" ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#t" ], "text/plain": [ "#t" ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(<= 3 4)" ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#t" ], "text/plain": [ "#t" ] }, "execution_count": 27, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(<= 4 4)" ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#f" ], "text/plain": [ "#f" ] }, "execution_count": 28, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(> 3 4)" ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#t" ], "text/plain": [ "#t" ] }, "execution_count": 29, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(> 4 3)" ] }, { "cell_type": "code", "execution_count": 30, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#t" ], "text/plain": [ "#t" ] }, "execution_count": 30, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(>= 4 3)" ] }, { "cell_type": "code", "execution_count": 31, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#t" ], "text/plain": [ "#t" ] }, "execution_count": 31, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(>= 4 4)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Every value in Racket has a type, and there are predicates to test the types of values. For example, the predicate number? tests whether its argument is a number. Note that the question mark is part of the identifier. As mentioned earlier, there is a convention in Racket (and Scheme) that ending the name of a procedure with a ? indicates that it is a predicate, that is, always returns #t or #f. The above predicates expect numbers as their arguments, and return an error message if this is not true. As an example, consider the following." ] }, { "cell_type": "code", "execution_count": 32, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "=: contract violation\n", " expected: number?\n", " given: \"hi\"\n", " argument position: 1st\n", " other arguments...:\n", " 7\n" ] } ], "source": [ "(= \"hi\" 7)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This error message is telling you that the built-in procedure = experienced a \"contract violation\", which means that its input didn't satisfy some requirement. In this case, it says it was expecting an argument of type number, (indicated by the predicate number?), and was given instead the string \"hi!\", and that this happened in the first argument position.\n", "\n", "For testing equality of general Racket values, you can use the predicate equal? For this predicate we have the following." ] }, { "cell_type": "code", "execution_count": 33, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#f" ], "text/plain": [ "#f" ] }, "execution_count": 33, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(equal? \"hi!\" 7)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You get no error message, just the answer that \"hi!\" and 7 are not equal.\n", "Now we know how to test whether the input n is equal to 1, namely the expression (= n 1). But we also need to be able to do one thing if it is and something else if it isn't. For this we can use the special form if. The syntax of if is as follows.\n", "\n", "
\n",
    "    (if expression1 expression2 expression3)\n",
    "
\n", "The keyword is if, and it must be followed by exactly three expressions. The evaluation rule is as follows. The condition, expression1, is evaluated. If the value is not #f, then expression2 is evaluated and its value returned. If the value is #f, then expression3 is evaluated and its value returned. Notice that (unlike in an application), we *don't* evaluate all three expressions: either we evaluate expression1 and expression2, or we evaluate expression1 and expression3, but not all three. (This is the \"coffee or tea\" exclusive or, as opposed to the \"milk or sugar\" inclusive or.)\n", "\n", "With this new special form, we can finally write (factorial n) as follows." ] }, { "cell_type": "code", "execution_count": 34, "metadata": {}, "outputs": [], "source": [ "(define factorial\n", " (lambda (n)\n", " (if (= n 1)\n", " 1\n", " (* n (factorial (- n 1))))))" ] }, { "cell_type": "code", "execution_count": 35, "metadata": {}, "outputs": [ { "data": { "text/html": [ "120" ], "text/plain": [ "120" ] }, "execution_count": 35, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(factorial 5)" ] }, { "cell_type": "code", "execution_count": 36, "metadata": {}, "outputs": [ { "data": { "text/html": [ "24" ], "text/plain": [ "24" ] }, "execution_count": 36, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(factorial 4)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Using the alternate procedure definition syntax, we have the following." ] }, { "cell_type": "code", "execution_count": 37, "metadata": {}, "outputs": [], "source": [ "(define (fact n)\n", " (if (= n 1)\n", " 1\n", " (* n (fact (- n 1)))))" ] }, { "cell_type": "code", "execution_count": 38, "metadata": {}, "outputs": [ { "data": { "text/html": [ "120" ], "text/plain": [ "120" ] }, "execution_count": 38, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(fact 5)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that the \"condition\", expression1, in the if expression is (= n 1), which tests whether n is equal to 1. The \"then case\", expression2, is just 1, which is the value that is returned when n is equal to 1. The \"else case\", expression3, is an expression that multiplies n by the result of a recursive call to the factorial procedure on the value of (- n 1), which is 1 less than n. Thus, this procedure definition mirrors the original definition we gave for n! above.\n", "\n", "To understand how (factorial 4) => 24, we see that (factorial 4) first has to compute (factorial 3) and multiply it by 4. And (factorial 3) has to compute (factorial 2) and multiply it by 3. And (factorial 2) has to compute (factorial 1) and multiply it by 2. But in the application (factorial 1), the value of the argument n is 1, so we reach the base case, and (factorial 1) simply evaluates to 1. Then (factorial 2) can multiply 1 by 2 and evaluate to 2. Then (factorial 3) can multiply 2 by 3 and evaluate to 6. Finally, (factorial 4) can multiply 6 by 4 and evaluate to 24.\n", "\n", "See racket3.rkt and try out the trace facility." ] }, { "cell_type": "code", "execution_count": 39, "metadata": {}, "outputs": [], "source": [ "(require racket/trace)" ] }, { "cell_type": "code", "execution_count": 40, "metadata": {}, "outputs": [], "source": [ "(trace fact)" ] }, { "cell_type": "code", "execution_count": 41, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ ">(fact 5)\n", "> (fact 4)\n", "> >(fact 3)\n", "> > (fact 2)\n", "> > >(fact 1)\n", "< < <1\n", "< < 2\n", "< <6\n", "< 24\n", "<120\n" ] }, { "data": { "text/html": [ "120" ], "text/plain": [ "120" ] }, "execution_count": 41, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(fact 5)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that trace displays the recursive calls to fact" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## More Racket\n", "\n", "Can you undefine a value in racket? That is, can you remove it from the namespace? The answer appears to be no, however, I am willing to hear suggestions. Here is the stackoverflow rationale.\n", "\n", "- Perlis epigram #23: To understand a program you must become both the machine and the program." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Combining Boolean values.\n", "\n", "There is a built-in procedure not and two special forms (and, or) that can be used to combine Boolean values. The syntax of not is as follows.\n", "
\n",
    "    (not exp)\n",
    "
\n", "\n", "The expression exp is evaluated, and if its value is not #f, then the value #f is returned; if its value is #f, then the value #t is returned. As examples, we have the following." ] }, { "cell_type": "code", "execution_count": 42, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#t" ], "text/plain": [ "#t" ] }, "execution_count": 42, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(not #f)" ] }, { "cell_type": "code", "execution_count": 43, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#f" ], "text/plain": [ "#f" ] }, "execution_count": 43, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(not #t)" ] }, { "cell_type": "code", "execution_count": 44, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#f" ], "text/plain": [ "#f" ] }, "execution_count": 44, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(not (= (+ 1 3) (+ 2 2))) " ] }, { "cell_type": "code", "execution_count": 45, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#t" ], "text/plain": [ "#t" ] }, "execution_count": 45, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(not (> 2 4))" ] }, { "cell_type": "code", "execution_count": 47, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#f" ], "text/plain": [ "#f" ] }, "execution_count": 47, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(not 1)" ] }, { "cell_type": "code", "execution_count": 48, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#f" ], "text/plain": [ "#f" ] }, "execution_count": 48, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(not \"\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Non-Boolean values are treated as not #f, so, for example (not 13) => #f.\n", "\n", "## The special forms: and, or.\n", "\n", "Recall that the meaning of \"special form\" is that the rules of evaluation are not those of an application. The syntax of these two special forms is as follows.\n", "
\n",
    "    (and exp1 exp2 ... expn)\n",
    "    (or exp1 exp2 ... expn)\n",
    "
\n", "\n", "Each takes an arbitrary finite number of expressions as arguments, and evaluates them in order, left to right, possibly stopping early. \n", "\n", "The evaluation rule is that if any expression is #f, then the value #f is returned for the whole and expression -- in this case, no further expressions are evaluated. If the value of exp1 is not equal? to #f, then exp2 is evaluated, and if its value is equal? to #f, then the value #f is returned for the whole and expression (and no further expressions are evaluated.) If this process continues until expn is evaluated, its value is simply returned as the value of the whole and expression. We have the following examples." ] }, { "cell_type": "code", "execution_count": 49, "metadata": { "scrolled": true }, "outputs": [ { "data": { "text/html": [ "#f" ], "text/plain": [ "#f" ] }, "execution_count": 49, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(and #f #f) " ] }, { "cell_type": "code", "execution_count": 50, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#f" ], "text/plain": [ "#f" ] }, "execution_count": 50, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(and #t #f)" ] }, { "cell_type": "code", "execution_count": 51, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#f" ], "text/plain": [ "#f" ] }, "execution_count": 51, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(and #f #t)" ] }, { "cell_type": "code", "execution_count": 123, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#t" ], "text/plain": [ "#t" ] }, "execution_count": 123, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(and #t #t)" ] }, { "cell_type": "code", "execution_count": 52, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#t" ], "text/plain": [ "#t" ] }, "execution_count": 52, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(and (= (+ 1 2) 3) (> 4 3))" ] }, { "cell_type": "code", "execution_count": 125, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#f" ], "text/plain": [ "#f" ] }, "execution_count": 125, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(and (> 4 3) (< 12 6)) " ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#t" ], "text/plain": [ "#t" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(and (= (+ 1 1) 2) (> 4 3) (< 6 12))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Non-Boolean values are treated as not #f, so we have (and 1 2 13) => 13. (This is because 1 and 2 are not equal? to #f, so we return the value of the last expression, which is 13.) This last kind of behavior is sometimes convenient, but also confusing and somewhat deprecated.\n", "\n", "The special form or is analogous to the special form and, but is looking for the first not #f value it can find, evaluating expressions left to right. When it finds a not #f value, it returns that value as the value of the whole or expression (not evaluating any further expressions.) If all the expressions evaluate to #f, then #f is returned as the value of the or expression. Some examples follow." ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#f" ], "text/plain": [ "#f" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(or #f #f)" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#t" ], "text/plain": [ "#t" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(or #t #f)" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#t" ], "text/plain": [ "#t" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(or #f #t) " ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#t" ], "text/plain": [ "#t" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(or #t #t) " ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#t" ], "text/plain": [ "#t" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(or (= (+ 1 1) 2) (< 4 3))" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#f" ], "text/plain": [ "#f" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(or (= 2 3) (> 6 7) (<= 12 6))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Once again, because non-Boolean values are treated as not #f, we have the following behavior: (or 1 2 13) => 1. (This is because the first expression evaluated, namely 1, is not #f, so its value is returned as the value of the or expression.)\n", "\n", "### Edge cases for the special forms: and, or. \n", "\n", "What happens when we evaluate (and) and (or)?" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#t" ], "text/plain": [ "#t" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(and)" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#f" ], "text/plain": [ "#f" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(or)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "These expressions do not result in errors when they are evaluated. Why are the values chosen reasonable? If we think of and, or as operating on Boolean values, then #t makes sense as an initial value for and: we keep evaluating expressions, combining them with the current value, until either the value becomes #f (which is then the final value) or we run out of expressions (and the final value is #t). Dually, #f makes sense as an initial value for or. For the same reason, these are the mathematical conventions for AND and OR over a set of expressions -- if the set is empty, AND returns true, OR returns false." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

Lists.

\n", "\n", "A list is a finite sequence of values. There is a list of no values, the empty list, which can be represented as '() or as the keyword empty. To determine whether an expression is equal? to the empty list, we can use either of the following built-in predicates.\n", "
\n",
    "    (empty? exp)\n",
    "    (null? exp)\n",
    "
\n", "Each of these evaluates the expression exp, and if its value is equal? to '(), then the value #t is returned; otherwise, the value #f is returned. For example we have the following." ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [], "source": [ "(require racket)\n", "(require racket/base)" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#t" ], "text/plain": [ "#t" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(empty? '())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " null? is the same as empty?" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#t" ], "text/plain": [ "#t" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(null? '())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "empty is the same as '()" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "text/html": [ "'()" ], "text/plain": [ "'()" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "empty" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#f" ], "text/plain": [ "#f" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(null? 13)" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#f" ], "text/plain": [ "#f" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(null? \"\")" ] }, { "cell_type": "code", "execution_count": 141, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#f" ], "text/plain": [ "#f" ] }, "execution_count": 141, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(null? \"this is not an empty list\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here are examples of lists with a nonzero number of elements; each one is given as its \"quoted\" representation -- note the leading single quote. (The special form quote, abbreviated as a leading single quote, causes its argument not to be evaluated.) First, we have a list with one element, the number 17." ] }, { "cell_type": "code", "execution_count": 142, "metadata": {}, "outputs": [ { "data": { "text/html": [ "'(17)" ], "text/plain": [ "'(17)" ] }, "execution_count": 142, "metadata": {}, "output_type": "execute_result" } ], "source": [ "'(17)" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "data": { "text/html": [ "'(+ 2 3)" ], "text/plain": [ "'(+ 2 3)" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "'(+ 2 3)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Next, a list with two elements, the number 17 followed by the number 24 -- remember that order matters in a list." ] }, { "cell_type": "code", "execution_count": 143, "metadata": {}, "outputs": [ { "data": { "text/html": [ "'(17 24)" ], "text/plain": [ "'(17 24)" ] }, "execution_count": 143, "metadata": {}, "output_type": "execute_result" } ], "source": [ "'(17 24)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The separator for the two elements is \"whitespace\" -- blanks, tabs, newlines and the like. Another list, with three elements, 17 followed by 24 followed by 6." ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "data": { "text/html": [ "'(17 24 6)" ], "text/plain": [ "'(17 24 6)" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "'(17 24 6)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The elements of a list do not have to be of the same type; here is another list with three elements, a number, a string and a Boolean." ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "data": { "text/html": [ "'(17 \"hi!\" #f)" ], "text/plain": [ "'(17 \"hi!\" #f)" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "'(17 \"hi!\" #f)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The elements of a list may themselves be lists, as in the following example." ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "data": { "text/html": [ "'((1 2) (3) 4)" ], "text/plain": [ "'((1 2) (3) 4)" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "'((1 2) (3) 4)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This list has three elements: the list '(1 2) followed by the list '(3) followed by the number 4. Lists may be nested within lists to arbitrary depth." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Selectors and constructors.\n", "\n", "When we have a compound data structure, we expect to be able to extract parts of it. The procedures that do that are selectors. We also expect to be able to assemble parts into a compound data structure. Procedures that do that are constructors. \n", "\n", "There are two basic selectors for lists: one to return the first element of a non-empty list, and one to return the rest of the elements of a non-empty list, without the first one. Each selector has two names: the LISP-historical one, and one that is considerably more mnemonic. The procedure to return the first element of a list is (car lst) or (first lst), and the procedure to return a list of all the elements except the first element is (cdr lst) or (rest lst). Here are examples, using both names for both selectors." ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "data": { "text/html": [ "17" ], "text/plain": [ "17" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(first '(17 24 6)) " ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "data": { "text/html": [ "17" ], "text/plain": [ "17" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(car '(17 24 6))" ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "data": { "text/html": [ "'(24 6)" ], "text/plain": [ "'(24 6)" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(rest '(17 24 6))" ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [ { "data": { "text/html": [ "'(24 6)" ], "text/plain": [ "'(24 6)" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(cdr '(17 24 6))" ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [ { "data": { "text/html": [ "24" ], "text/plain": [ "24" ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(first (rest '(17 24 6)))" ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [ { "data": { "text/html": [ "24" ], "text/plain": [ "24" ] }, "execution_count": 27, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(car (cdr '(17 24 6))) " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Compositions of car and cdr procedures (up to some limit, 5?) have \"syntactic sugar\" in the form of abbreviations, so that (car (cdr lst)) can be abbreviated (cadr lst). Note that in a list of at least 2 elements, cadr returns the second element of the list. Racket has (also as \"syntactic sugar\") built-in procedures second, third, ... (up to some limit), so that (second '(17 24 6)) => 24. The procedure name second is generally easier for humans to process than cadr.\n", "\n", "The names car and cdr reflect the assembly language opcodes from the early implementation of LISP on an IBM computer.\n", "\n", "- CAR => contents of the address register\n", "- CDR => contents of the decrement register" ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [ { "data": { "text/html": [ "24" ], "text/plain": [ "24" ] }, "execution_count": 28, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(cadr '(17 24 6))" ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [ { "data": { "text/html": [ "24" ], "text/plain": [ "24" ] }, "execution_count": 29, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(second '(17 24 6))" ] }, { "cell_type": "code", "execution_count": 30, "metadata": {}, "outputs": [ { "data": { "text/html": [ "6" ], "text/plain": [ "6" ] }, "execution_count": 30, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(caddr '(17 24 6))" ] }, { "cell_type": "code", "execution_count": 31, "metadata": {}, "outputs": [ { "data": { "text/html": [ "6" ], "text/plain": [ "6" ] }, "execution_count": 31, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(third '(17 24 6))" ] }, { "cell_type": "code", "execution_count": 157, "metadata": {}, "outputs": [ { "data": { "text/html": [ "17" ], "text/plain": [ "17" ] }, "execution_count": 157, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(caar '((17) (24) (6)))" ] }, { "cell_type": "code", "execution_count": 32, "metadata": {}, "outputs": [ { "data": { "text/html": [ "17" ], "text/plain": [ "17" ] }, "execution_count": 32, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(first (first '((17) (24) (6))))" ] }, { "cell_type": "code", "execution_count": 33, "metadata": {}, "outputs": [ { "data": { "text/html": [ "17" ], "text/plain": [ "17" ] }, "execution_count": 33, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(caar '((17) (24) (6)))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Further discussion: Constructors.\n", "\n", "In the case of lists, there is one basic constructor, whose name is cons. Its syntax is\n", "
\n",
    "    (cons item lst)\n",
    "
\n", "If lst is a list and item is any value, then (cons item lst) returns a list equal to lst with item inserted as the first element. As examples, we have the following." ] }, { "cell_type": "code", "execution_count": 34, "metadata": {}, "outputs": [ { "data": { "text/html": [ "'(17 24 6)" ], "text/plain": [ "'(17 24 6)" ] }, "execution_count": 34, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(cons 17 '(24 6)) " ] }, { "cell_type": "code", "execution_count": 35, "metadata": {}, "outputs": [ { "data": { "text/html": [ "'(6)" ], "text/plain": [ "'(6)" ] }, "execution_count": 35, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(cons 6 '())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This allows us to construct the list '(17 24 6) using three applications of cons, as follows." ] }, { "cell_type": "code", "execution_count": 36, "metadata": {}, "outputs": [ { "data": { "text/html": [ "'(17 24 6)" ], "text/plain": [ "'(17 24 6)" ] }, "execution_count": 36, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(cons 17 (cons 24 (cons 6 '())))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The innermost cons expression evaluates to the list '(6), the middle cons expression adds 24 at the front of that list, to get '(24 6), and the outermost cons expression adds 17 to that list, to get '(17 24 6).\n", "\n", "Note that cons does not require its second argument to be a list, though that will cover essentially all our uses of cons. If you cons together two numbers, you get a \"dotted pair\", as in the following example." ] }, { "cell_type": "code", "execution_count": 37, "metadata": {}, "outputs": [ { "data": { "text/html": [ "'(3 . 8)" ], "text/plain": [ "'(3 . 8)" ] }, "execution_count": 37, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(cons 3 8)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This looks a bit like a list, but the dot (.) is an indicator that it is not -- sometimes such structures are called \"improper lists\". When it occurs in your program's output, it is generally an indication that you cons'ed an element onto something that is not a list, which is generally an error to be corrected. There is a built-in predicate (list? exp), which returns #t if exp is a list, and #f otherwise. (pair? exp), which returns #t if exp is a list - even an improper one. As examples, we have the following." ] }, { "cell_type": "code", "execution_count": 38, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#t" ], "text/plain": [ "#t" ] }, "execution_count": 38, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(list? '())" ] }, { "cell_type": "code", "execution_count": 39, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#t" ], "text/plain": [ "#t" ] }, "execution_count": 39, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(list? '(17 24 6))" ] }, { "cell_type": "code", "execution_count": 40, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#f" ], "text/plain": [ "#f" ] }, "execution_count": 40, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(list? 17)" ] }, { "cell_type": "code", "execution_count": 166, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#t" ], "text/plain": [ "#t" ] }, "execution_count": 166, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(list? (cons 3 '())) " ] }, { "cell_type": "code", "execution_count": 41, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#f" ], "text/plain": [ "#f" ] }, "execution_count": 41, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(list? \"hi!\") " ] }, { "cell_type": "code", "execution_count": 42, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#f" ], "text/plain": [ "#f" ] }, "execution_count": 42, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(list? (cons 3 8)) " ] }, { "cell_type": "code", "execution_count": 43, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#t" ], "text/plain": [ "#t" ] }, "execution_count": 43, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(pair? (cons 3 8))" ] }, { "cell_type": "code", "execution_count": 44, "metadata": {}, "outputs": [ { "data": { "text/html": [ "#t" ], "text/plain": [ "#t" ] }, "execution_count": 44, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(pair? '(3 4))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As another example, we will construct the list '((1 2) (3) 4) using cons, numbers, and the empty list '(). Because cons adds elements at the front of the list, we construct a list by working backwards. To get the list '(4), we cons 4 to the empty list:" ] }, { "cell_type": "code", "execution_count": 45, "metadata": {}, "outputs": [ { "data": { "text/html": [ "'(4)" ], "text/plain": [ "'(4)" ] }, "execution_count": 45, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(cons 4 '()) " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We'd like to add the element '(3) to the front of this list. However, this is itself a list, so we need to construct it similarly to '(4), namely, (cons 3 '()). Thus, we can get a list of the 2nd and 3rd elements of our target as follows." ] }, { "cell_type": "code", "execution_count": 46, "metadata": {}, "outputs": [ { "data": { "text/html": [ "'((3) 4)" ], "text/plain": [ "'((3) 4)" ] }, "execution_count": 46, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(cons (cons 3 '()) (cons 4 '())) " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We just need to cons the element '(1 2) onto the front of this list, but we need to construct the list '(1 2) in order to do this. To construct the list '(1 2) we use the expression (cons 1 (cons 2 '())). Putting all this together, we have" ] }, { "cell_type": "code", "execution_count": 47, "metadata": {}, "outputs": [ { "data": { "text/html": [ "'((1 2) (3) 4)" ], "text/plain": [ "'((1 2) (3) 4)" ] }, "execution_count": 47, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(cons (cons 1 (cons 2 '())) (cons (cons 3 '()) (cons 4 '())))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Next we look at various forms of recursive procedures dealing with lists as arguments and return values." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "End of Racket notebook." ] } ], "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": 4 }