Programming for Architecture
1 Preface
This book was born in 2007, after an invitation to teach an introductory programming course to the students of Architecture at Instituto Superior Técnico (IST). The original motivation for the introduction of this course was the same as for several other courses: similar to Mathematics and Physics, Programming is now one of the fundamental courses that constitute the basic education of any IST student.
With this premise, it did not seem to be a subject that would entice the student’s interest, particularly since it was not very clear the contribution it could have to the course. To contradict that first impression, I decided to include in the course’s syllabus some applications of Programming in Architecture. In that sense, I had a conversation with several architectural students and teachers and asked them to explain to me what they did and how they did it. What I heard and saw was revealing.
Despite the enormous progresses that ComputerAidedDesign (CAD) brought to the profession, the truth is that its use continuous to be manual, laborious, repetitive and boring. The creation of a digital model in a CAD tool requires extreme attention to detail, distracting from what is fundamental: the idea. Frequently, the obstacles found end up forcing the Architect to simplify the original idea. Unfortunately, those obstacles do not end with the creation of the model. On the contrary, they become aggravated when the inevitable changes need to be made to the model.
In general, CAD tools are conceived to make the most common tasks easier, in detriment of other less common or sophisticated tasks. In fact, to an Architect interested in modelling more complex shapes, the CAD tool used can raise several limitations. However, those limitations are only deceptions since they can be overcome with the aid of programming. Programming allows a CAD tool to be amplified with new capabilities, thus eliminating the obstacles that restrict the work of the Architect.
The programming practice is intellectually very stimulating but it is also a challenge. It implies the need to master a new language, it implies a new way of thinking. Frequently, that effort makes several people give up but to the ones that prevail in overcoming the initial difficulties, they acquire the skill to go further in the creation of innovative architectural solutions.
This books aims to meet those Architects.
2 Introduction
Knowledge transmission is one of the issues that has worried mankind throughout the ages. As man is able to accumulate knowledge throughout his life, it would be unfortunate if all that knowledge disappeared with his death.
To avoid this loss, mankind invented a series of mechanisms to preserve knowledge. Firstly, oral transmission, consisting in the transmission of knowledge from one person to a small group of people, in a way transferring the problem of knowledge preservation to the next generation. Secondly, written transmission, consisting in documenting knowledge. On one hand, this approach has the great advantage of reaching out to a much larger group of people. On the other hand, it significantly reduces the loss of knowledge due to transmission problems. In fact, the written word allows to preserve knowledge for long periods of time and without the inevitable changes that occur on a long chain of oral transmissions.
It is thanks to the written word that mankind can understand and accumulate vast amounts of knowledge, some of it dating back to thousands of years. Unfortunately, the written word has not always been able to accurately transmit what the author had in mind: the natural language is ambiguous and it evolves with time, making the interpretation of written texts a subjective task. Whether when we write a text or when we read, or interpret one, there are omissions, imprecisions, errors, and ambiguities which can turn the knowledge transmission fallible. If the transmitted knowledge is simple, the receptor will most likely have enough culture and imagination to understand it. For the transmission of more complex knowledge, however,that might be much more difficult to do.
When rigour in the transmission of knowledge is needed, relying on the receptor’s abilities to understand it can have disastrous outcomes and, in fact, throughout history we can find many catastrophic events caused solely by insufficient or incorrect transmission of knowledge.
To avoid these problems, more accurate languages were developed. Mathematics, in particular, has for the past millenia obsessively sought to construct a language that shines for its absolute rigour. This allows knowledge transmission to be much more accurate than in other areas, reducing to the bare minimum the need for imagination in order to understand that knowledge.
To better understand this problem, let us consider one concrete example of knowledge transmission: the calculus of the factorial of a number. If we assume that the person, to whom we want to transmit that knowledge, knows beforehand about numbers and arithmetic operations, we could tell him that, to calculate the factorial of any number, one must multiply every number from one until that number. Unfortunately, that description is too long, and worse yet, inaccurate, because it does not state that only integer numbers are to be multiplied. To avoid these imprecisions and simultaneously make the information more compact, Mathematics developed a set of symbols and concepts that should be understood by everyone. For example, to define the integer sequence of numbers between \(1\) and \(9\), mathematics allows us to write \(1,2,3,\ldots,9\). In the same manner, instead of referring to "any number" mathematics invented the concept of "variable": a name that refers to some "thing" that can be used in several parts of a mathematical statement, always representing the same "thing". That way, Mathematics allows us to more accurately express the factorial computation as follows:
\[n! = 1\times 2\times 3\times \cdots{} \times n\]
But is this definition rigorous enough? Is it possible to interpret it without requiring imagination to figure the author’s intention? Apparently, it is but there is a detail in this definition that requires imagination: the ellipses. The ellipses indicate that the the reader must imagine what should be in its place. Although most readers will correctly understand that the author meant the multiplication of the sequential numbers, some might think to replace the ellipsis with something else.
Even if we exclude this last group of people from our target audience, there are still other problems with the previous definition. Let us imagine, for example, the factorial of \(2\). What is its value? If we use \[n = 2\] in the formula, we get:
\[2! = 1\times 2\times 3\times \cdots{} \times 2\]
In this case, the computation makes no sense, which shows that, in fact, imagination is need for more than just on how to fill in the ellipsis: the number of terms to consider depends on the number to which we want to calculate the factorial.
Assuming that our reader has enough imagination to figure out this particular detail, he would easily calculate that \(2!=1\times 2=2\). But even then, there will be cases where it is not that clear. For example, what is the factorial of zero? The answer does not appear to be obvious. What about the factorial of \(1\)? Again, it is not clear. And the factorial of \(4.5\)?. Once again the formula says nothing regarding these situations and our imagination can not guess the correct procedure.
Would it be possible to find a way of transmitting the knowledge required to compute the factorial function that minimizes imprecisions, gaps, and ambiguities? Let us try the following variation of the definition of the factorial function:
\[n!= \begin{cases} 1, & \text{if $n=0$}\\ n \cdot (n1)!, & \text{if $n\in \mathbb{N}$.} \end{cases}\]
Have we reached the necessary rigour that requires no imagination on the reader’s part? One way to find out is with the previous cases that gave us problems. Fist of all, there are no ellipsis, which is positive. Secondly, for the factorial of the number \(2\) we will have that:
\[2!=2\times 1!=2\times (1\times 0!)=2\times (1\times 1)=2\times 1=2\]
which means that there is no ambiguity. Finally, we can see that it makes no sense trying to determine the factorial value of \(1\) or \(4.5\) because this function can only be applied to \(\mathbb{N}_0\) members}.
This example shows that, even in mathematics, there are different levels of rigour in the different ways that is possible to transmit knowledge. Some require more imagination than others but in general they have been enough for Mankind to preserve knowledge throughout history.
It so happens that nowadays Mankind has a partner that has been giving a huge contribution to its progress: the computer. This machine has the extraordinary capability of instructed on how to execute a complex set of tasks. Programming is essentially all about transmitting to a computer the knowledge needed to solve a specific problem. This knowledge is called a program. Because they are programable, computers have been used for the most diversified ends, and in the last decades they have radically changed the way we work. Unfortunately, the computer’s extraordinary ability to learn comes with an equal extraordinary lack of imagination. A computer does assume or imagine, it just rigorously interprets the knowledge transmitted in the form of a program.
Since it has no imagination the computer depends critically on the way we present it the knowledge that we wish to transmit: that knowledge must be described in such a way that no ambiguity, gaps or imprecision. A language with these characteristics is generally called a programming language.
2.1 Programming Languages
For a computer to be able to solve a problem it is necessary to describe the process of solving a problem in a language that it understands. Unfortunately, the language that a computer "innately" understands is extremely poor, making the description of how to solve a nontrivial problem a very exhausting, tedious and complex one. The countless programming languages that have been invented aim at lighting the programmer’s burden, by introducing linguistic elements capable of simplifying those descriptions. For example the concept of function, sum, matrix or rational number do not exist natively in computers but many programming languages allow their usage in order to simplify the description of scientific calculus. Naturally, there must be a process that is able to transform the programmer’s descriptions into instructions that computers can understand. Although this process is relatively complex what matters is that it allows us to have programming languages that operate closer to human thinking process rather than the computer’s.
This last fact is of extreme importance because it allows us to use programming languages not only to instruct a computer on how to solve a problem, but also to explain that process accurately to another human being. This way, programming languages become a way of transmitting knowledge as mathematics has been for the last thousands of years.
There is a huge amount of programming languages, some better equipped than others to solve specific problems. The choice of a programming language should therefore depend heavily on the type of the problems we wish to solve but it should not be a total commitment. For a programmer it is much more important to understand the fundamentals and techniques of programming than to master this or that language. However, to better understand these fundamentals, it’s convenient to exemplify them in a concrete programming language.
As the focus of this document will be on programming for Architecture, we will use a programming language that is geared towards solving geometrical problems. There are many languages that serve this purpose, most commonly associated with computed aided design tools  Computer Aided Design (CAD). ArchiCAD, for instance, offers a programming language called GDL, an acronym for Geometric Description Language that enables users to program multiple geometric forms. In the case of AutoCAD that language used is called AutoLisp, a dialect of a famous programming language called Lisp. A third option will be the RhinoScript language, available for Rhinoceros. Despite these languages seeming very different from each other, the concepts behind them are very similar. It is on these concepts that we will be leaning on, although for pedagogical reasons, it is convenient to particularize them in a single language.
Unfortunately GDL, AutoLisp, and RhinoScript were developed a long time ago and they have not been updated, possessing many archaic characteristics that makes them harder to learn and use. In order to make the learning process easier and, simultaneously allowing our programs to run in different CAD environments, we are going to use a new language called Racket, that has was purposely adapted for programming in Architecture. In this text we will explain the fundamentals of programming using Racket, not just because its easier to learn, but also for it’s practical applicability. However, once learned, the reader should be able to apply these fundamentals to any other programming language.
In order to facilitate the programmer’s task, Racket is equipped with a programming environment called DrRacket, that offers a text editor adapted to edit Racket’s programs, as well as a set of additional tools for error detection and debugging. This programming environment is shared with a freeware license and it is available at:
2.1.1 Exercises 1
2.1.1.1 Question 1
Exponentiation \(b^n\)is an operation between two numbers \(b\) and \(n\). When \(n\) is a positive integer, exponentiation is defined as:
\[b^n = \underbrace{b \times b \times \cdots \times b}_n\]
To a reader not familiarized with exponentiation, the previous definition raises several questions that may not be evident: how many multiplications should actually be done?,\(n\)?, \(n1\)? What if \(b=1\)? or \(b=0\)? Propose a definition for the exponentiation function in order to clear any doubts.
2.1.1.2 Question 2
What is a program and what purpose does it serve?
2.1.1.3 Question 3
What is a programming language and what purpose does it serve?
2.2 The Racket Language
In this section we will learn about Racket programming language, which we will use throughout this text. But first, we are going to examine some aspects that are common to other languages.
2.2.1 Syntax, Semantics and Pragmatics
Every language has syntax, semantics, and pragmatics.
In simple terms, syntax is a set of rules that dictate the kind of sentence that can be written in that language. Without it, any concatenation of words could be a sentence. For example, given the words “John”, “cake”, “ate”, “the” the syntax rules of the English language tell us that  “John ate the cake” is a correct sentence, and that  “ate the Jonh cake” is not. Note that according to the English syntax, "The cake ate John" is also syntactically correct.
Syntax dictates how a sentence is constructed but says nothing in regards to its meaning. Semantics are what attributes meaning to a sentence, thus telling us that “The cake ate John” makes no sense.
Finally, pragmatics sets the way sentences are commonly expressed. In a language, pragmatic changes depending on the context: the way two close friends talk with each other is different from the way two strangers talk.
These three aspects of a language are also present when we discuss programming languages. Unlike the natural languages we use to communicate between us, programming languages are characterized as being formal, obeying a set of simple and restrictive rules that can be mechanically processed.
In this document we will describe Racket’s syntax and semantics and, although there are mathematical formalisms to describe rigorously those two aspects, they require a mathematical sophistication that, given the nature of this work, is inappropriate. So we will only use informal descriptions. Afterwards, as we introduce language elements, we will discuss language pragmatics’.
2.2.2 Syntax and Semantics of Racket
When compared to other programming languages, Racket’s syntax is extraordinary simple and is based on the concept of expressions.
An expression in Racket can be formed using primitive elements such as numbers; or by the combination of those elements such as the sum of two numbers. This simple definition allows us to build expressions of arbitrary complexity. However, it is important to remember that syntax restricts what can be written: the fact that we can combine expressions to create more complex ones, that does not mean we can write any combination of subexpressions. These combinations are restricted by syntactic rules that we will describe throughout this text.
Much like the syntax, Racket’s semantics is also very simple when compared to other programming languages. As we will see, semantics is determined by the operators that are used in our expressions. For instance, the sum operator, is used to add two numbers. An expression that combines this operator with, for example, the numbers \(3\) and \(4\) will have as its meaning the sum between \(3\) and \(4\), i.e., \(7\). In a programming language, the semantics’ of an expression is given by the computer that will evaluate the expression.
2.2.3 The Evaluator
Every expression in Racket has a value. This concept is so important that Racket provides an evaluator, i.e., a program designed to interact with the user in order to evaluate expressions defined by the user.
In Racket, the evaluator is shown as soon as we start working the DrRacket environment, and it is possible to easily change between the editor and the evaluator at any time. Once DrRacket is running, the user is presented with the character > (called prompt), meaning that Racket is waiting for the user to input an expression.
The character ">" is Racket’s "prompt", in front of which the user’s expressions will be shown. Racket interacts with the user by executing a cycle that reads an expressions, determines its value and writes the result. This cycle is traditionally called readevalprintloop (abbreviated to REPL).
During the read phase, Racket reads an expression and creates an internal object that represents it. In the evaluation phase, that object is analysed in order to produce a value. This analysis uses rules that dictate, for each case, the object’s value. Finally, the result is given back in text form to the user in the print phase.
Given the existence of the readevalprintloop process, in Racket it is not necessary to instruct that computer to explicitly print the result of an expression, meaning that testing and debugging is significantly easy. The advantage of Racket being an interactive language is that it allows programs to be quickly developed by writing, testing and correcting small fragments at a time.
2.3 Language Elements
In every programming language we have to deal with two sets of objects: data and procedures. Data comprise all the entities that we wish to manipulate. Procedures designate the rules on how to manipulate that data.
In Mathematics, we can look at numbers as the data and algebraic operations as the procedures. These operations allows us to combine numbers. For example, \(2\times 2\) is a combination. Another combination involving more data is \(2\times 2\times 2\), and using even more data \(2\times 2\times 2\times 2\). However, unless we want to spend time solving problems of elementary arithmetic, we should consider more elaborate operations that represent calculation patterns. In the previous sequence of combinations shown, it is clear that the pattern that is emerging is the definition of exponentiation, which has been defined in Mathematics a long time ago. Exponentiation is therefore an abstraction of a succession of multiplications.
As in Mathematics, a programming language should contain primitive data and procedures, it should be capable of combining data and procedures to create more complex data and procedures and it should be able to abstract calculation patterns and allow them to be used as simple operations, defining new operations that represent those patterns.
Further ahead we are going to see how it is possible to define these abstractions in Racket. But for now, let us take a closer look at the primitive elements of the language, i.e., the most simple entities that the language deals with.
2.3.1 Numbers
As said previously, Racket executes a readevalprint cycle. This implies that everything we write in Racket must be evaluated, i.e., everything must have a value that Racket displays on the screen.
That way, if we give the evaluator a number it will return the value of that number. How much is the value of a number? At best we can say it has its own value. For example, the value of 1 is 1.
> 1 1
> 12345 12345
> 1/2 1/2
> 1+2i 1+2i
> 4.5 4.5
In Racket, numbers can be exact or inexact. Exact numbers include integers, fractions and complex numbers with integer parts. Inexact numbers are all others, being typically written in decimal or scientific notation).
2.4 Combinations
A combination is an expression that describes the application of an operator to its operands. In Mathematics, numbers can be combined using operations like the sum or multiplication; e.g. \(1 + 2\) and \(1 + 2 \times 3\). The sum and multiplication of numbers are but two of the extremely primitive procedures provided by Racket.
In Racket, a combination can be created by writing a sequence of expressions inside a pair of parentheses. An expression is a primitive element or another combination. The expression (+ 1 2) is a combination of two primitive elements 1 and 2 through the primitive procedure +. In the case of (+ 1 (* 2 3)) the combination is between \(1\) and (* 2 3) (this last expression is also a combination). Note that each expression must be separated from the rest using at least one space. Despite the combination (* 2 3) having three expressions  *, 2 and 3, the combination (*2 3) only has two  *2 and 3, in which the first expression has no predefined meaning.
For now, the only useful combinations are those in which expressions have meaning as operators and operands. Conventionally, Racket considers the first element of the combination the operator and the rest its operands.
The notation Racket uses to build expressions (the operator first and then the operands) is called prefix notation. This form of notation can cause some perplexity to new users of this language since most of them expect a notation closer to that taught in arithmetic and which is usually used in other programming languages. The expression (+ 1 (* 2 3)) is normally written 1 + 2 * 3 (designated infix notation, operator between operands), and usually this is easier for a human being to read. However, the prefix notation used by Racket has advantages over the infix notation:
It is very easy to use variadic operators, that is, operators that have an variable number of operands, such as (+ 1 2 3) or (+ 1 2 3 4 5 6 7 8 9). Most of other programming languages use only unary or binary operators and it is necessary to write the binary operators between each two operands: 1 + 2 + 3 or 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9. If it is intended to apply a ternary operator, you cannot write it the same way.
There are no precedences between operators. In languages that make use of infix notation, the expression 1 + 2 * 3, must be calculated as if it was written 1 + (2 * 3) instead of (1 + 2) * 3, because precedences were created in order to remove ambiguities. These precedences can be altered by parentheses use. In Racket, expressions like (+ 1 (* 2 3)) or (* (+ 1 2) 3) would be necessarily different, which avoids any kind of ambiguity.
The operators we define are used exactly the same way as the operators provided by the language: operator first and then the operands. In most other languages the operators are infix (between operands) and the procedures created by the user are prefix (first the procedure’s name and then the operands). This compromises the extension of the language in a coherent way. To exemplify this last case, let’s consider the exponentiation operation in a language with infix notation. To be coherent with the rest of the language there should be an operator, for example **, that lets us write 3**4 to calculate the 3 to the fourth power. As this operator usually does not exist, we are forced to create a procedure that implements it, but in this case the syntax changes radically because, in general, the user can not create infix operators. As a consequence, this new operand would have to be used in a prefix way and thus not remain coherent the other operators of the language, such as the sum or multiplication. On the contrary, in Racket we can either write (* 3 3 3 3) or create a function that allows us to write (** 3 4).
Besides the infix and prefix notations, there is also the postfix notation in which the operator comes after the operands. This notation has the same properties as the prefix notation but it is, in general, more difficult to read since it is necessary to read every operand before we are able to understand what should be done with them.
2.4.1 Indentation
The disadvantage of the prefix notation is the writing of complex combinations. The expression 1+2*34/5*6 is easy to read but when written using Racket’s syntax, ( (+ 1 (* 2 3)) (* (/ 4 5) 6)), it has a form that for those not yet accustomed to this syntax can be harder to read due to the large number of parentheses.
To make the expression easier to read, we can (and we should) use indentation. This technique is based on using different alignments in the textual disposition of programs in order to make them easier to read. This way, instead of writing our expressions in a single line or with an arbitrary line arrangement, we write them throughout several lines and with an alignment between lines that shows how the subexpressions are related to the expression that contains them.
The rule for indentation in Racket is extremely simple: in one line we have the operator and the first operand, the remaining operands are placed immediately below the first on, with enough blank spaces on the left so they are correctly aligned. If the case of a short expression, we can write it in a single line, with the operands immediately after the operator, using a single blank space to separate them. Using these two rules, we can rewrite the previous expression in the following manner:
( (+ 1 (* 2 3)) (* (/ 4 5) 6))
Note that arranging a combination in several lines does not affect the way Racket reads it. The correct delimitation of elements in a combination is done only by the visual separation and the correct usage of stacked parenthesis.
When the indentation rule is not enough to produce an aesthetically pleasing disposition of text lines, some minor variations may be used, such as placing the operator in a line and the operands underneath it, like in the following example:
(anoperatorwithaveryveryverybigname 1 2 3 4)
Generally, we might need to apply several rules simultaneously:
(anoperator (anoperatorwithaveryveryverybigname 1 2 3 4) (anotheroperator 5 6 (andanother 7 8)) (andthelastone 9 10))
Some operators have a proper indentation rule but this will be explained as we introduce them.
Indentation is crucial in Racket because it makes it easier to write and read complex code. Most editors that recognize Racket’s language automatically format the programs as we write them, also showing the correct matching between parenthesis. Eventually with practice, it becomes every easy to write and read programs, regardless how complex their structure is.
2.4.2 Exercises 2
2.4.2.1 Question 4
Define REPL?
2.4.2.2 Question 5
What is the difference between prefix an infix notation?
2.4.2.3 Question 6
In Mathematics it is usual to use simultaneously the prefix, infix and postfix notations. Write some mathematical examples of expressions that make use of those different notations.
2.4.2.4 Question 7
\(1 + 2  3\)
\(1  2 \times 3\)
\(1 \times 2  3\)
\(1 \times 2 \times 3\)
\((1  2) \times 3\)
\((1  2) + 3\)
\(1  (2 + 3)\)
\(2 \times 2 + 3 \times 3 \times 3\)
2.4.2.5 Question 8
2.4.2.6 Question 9
Use indentation to rewrite the following expression, so that there is only a single operand per line.
(* (+ (/ 3 2) ( (* (/ 5 2) 3) 1) ( 3 2)) 2)
2.4.3 Evaluating Combinations
As we have seen, Racket considers the first element of a combination its operator and the rest the operands.
The evaluator determines the combination’s value by applying the procedure specified by the user to the value of the operands. The value of an operand is designated as the argument of the procedure. The value of the combination (+ 1 (* 2 3)) is the result of adding the value of 1 to (* 2 3). As we have already seen, the value of 1 is \(1\) and (* 2 3) is a combination whose value is the result of multiplying 2 by 3, which is \(6\). Finally, by summing \(1\) with \(6\) we get \(7\).
> (* 2 3) 6
> (+ 1 (* 2 3)) 7
2.4.3.1 Question 10
Calculate the value of the following Racket expressions:
2.4.4 Strings
Chains of characters (also called Strings) are another type of primitive data. A character is a letter, a digit or any kind of graphic symbols, including nonvisible graphic symbols like blank spaces, tabs and others. A string is specified by a character sequence between quotations marks. Just like with numbers, the value of a string is the string itself:
> "Hi" "Hi"
> "I am my own value" "I am my own value"
Since a string is delimited by quotation marks, one could ask how can we create a string that contains quotation marks. For this and other special characters, there is a special character that Racket interprets differently: when in a strings the character \ appears it tells Racket that the next characters must be evaluated in a special way. For example, to create the following string:
John said "Good morning!" to Peter.
We must write:
"John said \"Good morning!\" to Peter."
Some valid escape characters in Racket.
The character \ is called an escape character and allows the inclusion of characters in strings that would otherwise be difficult to input. this table) shows examples of other escape characters.
As it happens with numbers, there are countless operators to manipulate strings. For example, to concatenate multiple strings there is the stringappend operator. The concatenation of several strings produces a single string with all characters of those strings, in the same order:
> (stringappend "1" "2") "12"
> (stringappend "one" "two" "three" "four") "onetwothreefour"
> (stringappend "I" " " "am" " " "a" " " "string") "I am a string"
> (stringappend "And I" " am " "another") "And I am another"
To know how many characters there are in a string we have the stringlength operator:
> (stringlength "I am string") 11
> (stringlength "") 0
Note that quotation marks define strings’ boundaries and are not consider characters. Besides strings and numbers, Racket has other kinds of primitive elements that will be addressed later.
2.5 Defining Functions
In addition to basic arithmetic operations, Mathematics offers a large set of operations that are defined based on those basic ones. For example, the square of a number is an operation (also designated as a function) that, given a number, calculates the multiplication of that number by itself. This function has the following mathematical definition \(x^2 = x \cdot x\).
Like in Mathematics, it is possible to define the square function in a programming language. In Racket, to obtain the square of a number, for example 5, we write the combination (* 5 5). In general, given a number x, we know to obtain the square value by writing (* x x) combination. All that remains now is to associate a name indicating that, given a number x, we obtain its square by evaluating (* x x). Racket allows us to do that by using the define operation:
(define (square x) (* x x))
As you can see from the square function definition, in order to define a function in Racket, we need to combine three elements. The first element is the word define, that informs the evaluator that we are defining a function. The second element is a combination with the function’s name and its parameters. The third element is the expression that will compute the function value for those parameters. In generic terms we could say that the definition of functions is done in the following manner:
(define (name parameter1 ... parametern) body)
The function’s parameters are called formal parameters and they are used in the body of an expression to refer to the correspondent arguments. When we write in the evaluator (square 5), the number 5 is the formal parameter. During the calculation this argument is associated with the number x. The arguments of a function are also called actual parameters.
The definition of the square function declares that in order to determine the square of a number x, we should multiply that number by itself (* x x). This definition associates the word square with a procedure, i.e., a description on how to obtain the desired result. Note that this procedure has parameters allowing it to use different arguments. For example, let’s evaluate the following expressions:
> (square 5) 25
> (square 6) 36
The rule to evaluate combinations stated above is also valid for functions defined by the user. The evaluation of the expression (square (+ 1 2)) first evaluates (+ 1 2) operand. This value of this operand, \(3\), is used by the function in place of x. The function’s body is then evaluated and all occurrences of x will be replaced by the value 3, i.e., the final value will b e the combination (* 3 3).
Formally, in order to invoke a function, is necessary to construct a combination in which the first element is an expression that evaluates for the function itself, and the remaining elements are expressions that evaluate for the arguments that the function is supposed to use. The result of the combination’s evaluation is the value calculated by the function for those arguments.
The process of evaluating a combination is done by the following steps:
All elements in a combination are evaluated, with the value of the first one being necessarily a function.
The formal parameters are associated with the function’s arguments, i.e., the value of the remaining elements of that combination. Each parameter is associated to an argument, according to the order of parameters and arguments. An error occurs when the number of parameters and arguments is different.
The function’s body is evaluated keeping in mind these associations between parameters and arguments.
To better understand this process, it is useful to decompose it in its most elementary steps. The following example shows the process of evaluating the expression \(((1+2)^2)^2\) step by step:
\(\downarrow\)(square (square 3))\(\downarrow\)\(\downarrow\)(square 9)\(\downarrow\)\(\downarrow\)81
Every function created by the user is evaluated by Racket in equal terms as other predefined function. This allows them to be used to create new functions. For example, after defining the square function, we can define the function that calculates the area of a circle with radius \(r\), using the formula \(\pi * r^2\):
(define (circlearea radius) (* pi (square radius)))
Naturally, during the evaluation of the expression used to compute the area of a circle, the square function will be invoked. This is visible in the following evaluation sequence:
(circlearea 2)\(\downarrow\)\(\downarrow\)\(\downarrow\)\(\downarrow\)\(\downarrow\)12.5664
Since defining functions allows for associations to be established between a procedure and a name, that means Racket needs to have memory in which to store those associations. This memory is called environment.
Note that this environment exists only while we are working. When the program is shut down, that environment is lost. In order to avoid losing those definitions, they should be saved as in a file. This is the reason why the process of working in Racket is based on writing the definitions in files although still needing to test them using the evaluator to ensure the proper behaviour of the created definitions.
2.5.1 Exercises 3
2.5.1.1 Question 11
Define the function double, that calculates the double of a given number.
2.6 Names
The definition of functions in Racket involves assigning names: names for functions and names for its parameters.
The blank space concept includes tabs and changing line.
In Racket the only characters that cannot be used in names are the parentheses (, apostrophe ’, quotation marks " and semicolons ;. All other characters can be used in names, but, in practice, the creation of names should take some rules in consideration:
You should only use letters of the alphabet, digits, arithmetic symbols and some punctuation characters like exclamation and interrogation marks. For portability reasons, accented characters should be avoided.
If the function’s name has more than one word, those words should be separated by an hyphen . For example, a function that computes the are of a circle can have the name circlearea. However, circlearea, circle_area and circle+area are less appropriate.
If the name is associated with a question it should end with an interrogation mark  ?. For example, a function that tests if a number is even could be called even?.
If the function makes a conversion between two types of values, the name could include the name of both types and an arrow between them, indicating the direction of the conversion. For example, a function that converts euros into pounds could be called euros>pound or better yet pound<euro.
The choice of names will have a significant impact on the program’s legibility. Let us consider for example the area \(A\) of a triangle with a base \(b\) and a height \(c\) which can be defined mathematically by:
\[A(b,c) = \frac{b \cdot c}{2}\]
In Racket’s language we will have:
(define (A b c) (/ (* b c) 2))
Note that the Racket definition is identical to the corresponding mathematical expression, apart from the prefix notation and the \(=\) symbol being define. However, if we did not know beforehand what the function does, we will hardly understand it. Therefore, and contrary to Mathematics, the names that we assign in Racket should have a clear meaning. Instead of writing "A" it is preferable that we write "trianglearea" and instead of writing "b" and "c" we should write "base" and "height" respectively. Taking these aspects in consideration we can present a more meaningful definition:
(define (trianglearea base height) (/ (* base height) 2))
As the number of definitions grow, names become particularly important for the reader to quickly understand the written program, so it is crucial that names are carefully chosen.
2.6.1 Exercises 4
2.6.1.1 Question 12
Function that calculates the volume of a sphere;
Function that tests if a number is a primenumber;
Function that converts a measurement in centimetres into inches.
2.6.1.2 Question 13
Define the function radians<degrees that receives an angle in degrees and computes the corresponding value in radians. Note that \(180\) degrees are \(pi\) radians.
2.6.1.3 Question 14
Define the function degrees<radians that receives an angle in radians and computes the corresponding value in degrees.
2.6.1.4 Question 15
Define a function that calculates the perimeter of a circle given the radius \(r\).
2.6.1.5 Question 16
Define a function that calculates the volume of a parallelepiped from its length, width and height.
2.6.1.6 Question 17
Define a function that calculates the volume of a cylinder a length and radius. The volume corresponds to multiplying the base radius with the cylinder’s length.
2.6.1.7 Question 18
Define a function average that calculates the average value between two numbers. For example: (average 2 3) \(\rightarrow\) 2.5.
2.7 Predefined Functions
The possibility of defining new functions is fundamental for increasing the language’s flexibility and its ability to adapt to the problems we want to solve. The new functions, however, must be defined in terms of others that were either defined by the user or, at most, were already predefined in the language.
As we will see, Racket has a reasonable set of predefined functions that in most cases they are enough for what we want to do, but we should not try to avoid defining new functions whenever we deem it necessary.
In this table we see a set of mathematical functions predefined in Racket. Note that, due to syntax limitations (that are also present in other languages), it is common that some Racket functions have a notation that is different when compared to the mathematical definition. For example, the square root function, \(\sqrt{x}\) is written as (sqrt x). The name sqrt is a contraction of the words square root and similar contractions are used for several other functions. The absolute value function is written as \(x\) and the exponentiation function \(x^y\) as (expt x y). This table shows some equivalents between the invocations of Rackets functions and the correspondent Mathematics invocations.
Some math predefined functions in Racket.
Function
Arguments
Result
Multiple Numbers
The sum of all arguments. With no arguments, zero.
Multiple Numbers
With only one argument, the symmetric value. For more than one argument, the subtraction of the first argument by all the remaining arguments. With no arguments, zero.
Multiple Numbers
The multiplication of all arguments. With no arguments, zero.
Multiple Numbers
The dividision of the first argument by all the remaining arguments. With no arguments, zero.
One number
The sum of the argument with 1.
One number
The subtraction of the argument with 1.
One number
The absolute value of the argument.
One number
The sine of the argument (in radians).
One number
The cosine of the argument (in radians).
One or two numbers
With only one argument, the inverse tangent of the argument (in radians). With two arguments,the arc tangent of the division between the first and the second. The argument’s sign is used to determine the quadrant.
One number
The square of the argument.
One number
The square root of the argument.
One number
The exponential value with \(e\) base of the argument.
Two numbers
The first argument raised to the second one.
One number
The logarithmic value of the argument.
Multiple Numbers
The highest argument.
Multiple Numbers
the lowest argument.
Two numbers
With two arguments, the remainder of the division between the first and the second argument.
One number
The argument without the fractional part.
Racket’s predefined math functions.
Racket
Mathematics
\(x_0 + x_1 + \ldots + x_n\)
(+ x)
\(x\)
(+)
\(0\)
\(x_0  x_1  \ldots  x_n\)
( x)
\(x\)
()
Error
\(x_0 \times x_1 \times \ldots \times x_n\)
(* x)
\(x\)
(*)
\(1\)
\(x_0 / x_1 / \ldots / x_n\)
(/ x)
\(x\)
(/)
Error
(add1 x)
\(x+1\)
(sub1 x)
\(x1\)
(abs x)
\(x\)
(sin x)
\(\sin x\)
(cos x)
\(\cos x\)
(atan x)
\(\arctan x\)
(atan y x)
\(\arctan \frac{y}{x}\)
(sqr x)
\(x^2\)
(sqrt x)
\(\sqrt{x}\)
(exp x)
\(e^x\)
(expt x y)
\(x^y\)
(log x)
\(\log x\)
(floor x)
\(\lfloor x\rfloor\)
2.7.1 Exercises 5
2.7.1.1 Question 19
\(\sqrt{\frac{1}{\log 2^{\left(39\log 25)\right}}}\)
\(\frac{\cos^4 \frac{2}{\sqrt 5}}{\arctan 3}\)
\(\frac{1}{2} + \sqrt 3 + \sin^{\frac{5}{2}} 2\)
2.7.1.2 Question 20
2.7.1.3 Question 21
Define the function odd? that, for a given number evaluates if it is odd, i.e., if the remainder of that number when divided by two is one. In order to do so, you can use the predefined function remainder
2.7.1.4 Question 22
The area \(A\) of a pentagon inscribed in a circle of radius \(r\) is given by the following expression: \[A = \frac{5}{8}r^2\sqrt{10 + 2\sqrt{5}}\] Define a function to calculate that same area.
2.7.1.5 Question 23
Define a function to calculate the volume of an ellipsoid with semiaxis \(a\), \(b\) and \(c\). That volume can be calculated using the formula: \(V=\frac{4}{3}\pi a b c\)
2.8 Arithmetic in Racket
We saw previously that Racket is capable of dealing with several types of numbers, from integers to complex numbers and also fractions. Some of those numbers like \(\sqrt 2\) or \(pi\), do not have a rigorous representation based in numerals and for that reason, Racket classifies them as inexact numbers, in order to emphasize that they are dealing with an approximated value. When an inexact number is used in an arithmetic operation, the result will also be inexact, so the inexactness is said to be contagious.
Finitude is another feature of the inexact numbers. Unlike exact numbers that theoretically have no limits, inexact numbers cannot surpass a certain limit, above which every number is represented by infinity, as you can see by the following example:
> (expt 10.0 10) 10000000000.0
> (expt 10.0 100) 1e+100
> (expt 10.0 1000) +inf.0
Note that +inf.0 (or inf.0) is Racket’s way of saying that a number exceeds the inexact numbers representation capacity. The number is not infinite, as one might think, but merely a value excessively big for Racket’s capacity.
There is also another problem regarding inexact numbers: roundoff errors. As an example, let us consider the obvious equality \((4/31)*31=0\) and let us compare the results that we get by using exact or inexact numbers in Racket:
> ( (* ( (/ 4 3) 1) 3) 1) 0
> ( (* ( (/ 4.0 3.0) 1.0) 3.0) 1.0) 2.220446049250313e16
As we can see, by using inexact numbers we cannot obtain a correct result and the problem is caused by the use of roundoff errors: 3/4 can’t be represented with a finite number of digits. This roundoff error is then propagated to the remaining operations which will produce a value that, even though not zero, is relatively close enough.
2.8.1 Exercises 6
2.8.1.1 Question 24
Translate the following definition into Racket: \[f(x)=x0.1\cdot{}(10\cdot{}x10)\]
2.8.1.2 Question 25
In Mathematical terms, whatever the argument used in the previous function, the result should always be \(1\) because \[f(x)=x0.1\cdot{}(10\cdot{}x10)=x(x1)=1\] Using the created function calculate the result of the following expressions and explain them:
(f 5.1) (f 51367.7) (f 176498634.7) (f 1209983553611.9) (f 19843566622234756.0) (f 5.5377455871102e+20)
2.8.1.3 Question 26
We wish to create a flight of stairs with \(n\) treads, capable of covering a height \(a\) in meters. Considering that each step as a tread height \(h\) and a width \(d\) that obey to the following proportion: \[2h+d=0.64\]
define a function that, from a given height to cover and the number of treads, computes the length of the flight of stairs.
2.9 Name Evaluation
The primitive elements presented so far, such as numbers and strings, evaluate to themselves, i.e., the value of an expression composed only by a primitive element is the primitive element itself. With names this is no longer true.
Names have a special meaning in Racket. Note that when we define a function, it has a name. And its formal parameters have names as well. When a combination is written, the evaluator uses the function definition associated with the name that is the first element of the combination. This means that the value of the first element in a combination is the associated function. If we had defined the function square, as suggested previously in section Defining Functions, we could test this behaviour in the following expressions:
> (square 3) 9
> square #<procedure:square>
As we can see, the value of the name square is an entity that Racket describes using a special notation. This entity is, as shown, a function. The same behaviour happens to any other predefined function:
> + #<procedure:+>
> * #<procedure:*>
As we have seen, the sum + and multiplication * signs are some of the predefined names of the language. For example, the symbol pi is also predefined and associated with an approximated value of \(pi\):
> pi 3.141592653589793
However, when the body of the expression is evaluated, the value of a name assigned to a parameter in a function is the corresponding argument during the function call. For example, in the combination (square 3), after the evaluator knows that the square value is a function by us defined and that 3 has the value \(3\), it evaluates the body of the function but associating the name x, whenever is necessary, to the same \(3\) that was previously associated with x.
2.10 Conditional Expressions
There are many operations in which the result depends on a test. For example, the mathematical function \(x\), that estimates the absolute value of \(x\) is equivalent to the inverse of a number if it is negative or the number itself otherwise. Using the mathematical notation we have that:
\[x= \begin{cases} x, & \text{if $x<0$}\\ x, & \text{otherwise.} \end{cases}\]
This function needs to test if the argument is negative and choose one of two alternatives: it either evaluates for the number itself or for its symmetrical value.
These kind of expressions that depend on making one or more tests, are called conditional expressions.
2.10.1 Logical Expressions
A conditional expression follows the structure ”if expression then ..., otherwise ...”. The expression that determines whether to use the branch ”if” or the branch ”otherwise”, is called the logical expression and is characterized for having its value interpreted as either true or false. For example, the logical expression (< x 0) tests if the value of x is less than zero. If it is, the expression’s evaluation will return true, otherwise it will return false.
2.10.2 Logical Values
After George Boole, the English mathematician that invented the algebra of logic.
In Racket, conditional expressions consider only one of its values as false, represented as #f. Any other value that is different than #f is considered to be true. From the conditional expression point of view, the expression \(123\) is considered to be true. However, it makes little sense to a human user that a number is considered as true or false so a constant that represents true was introduced the language. This constant is represented by #t. If #f is the only value that represents falsehood and if #t is different than #f then #t necessarily represents truth.
2.11 Predicates
In the most usual case, a logical expression is a function applied to some arguments. In this case, the function used as the test case is known as a predicate. The test value is interpreted as true or false. So the predicate is a function that produces only true or false.
Despite the use of #t and #f it is important to know that not every predicate returns #t and #f exclusively. There are predicates produce different values from #t and #f.
2.11.1 Arithmetic Predicates
The mathematical relational operators \(<\),\(>\),\(=\),\(\leq\) and \(\geq\) are some of the most simple predicates. These operators compare numbers between each other. Their use in Racket follows the prefix notation and are written respectively <,>,=, <= and >=. Some examples are:
> (> 4 3) #t
> (< 4 3) #f
> (<= (+ 2 3) ( 6 1)) #t
2.12 Logical Operators
In order to combine logical expressions together we have the and, or and not operators. The and and the or operators accept any number of arguments. The not only accepts one. The value of such combinations is determined according to the following rules:
The and evaluates arguments from left to right until one of them is false, returning that value. If none of the arguments are false, the and operator returns true.
The or evaluates arguments from left to right until one of them is true, returning that value. If none of the arguments are true, the or operator returns false.
The not operator evaluates for true, if the argument is false, and evaluates false if the argument is true.
Note that although the meaning of false is clear, it necessarily corresponds to the value of #f, the meaning of true is not so clear because everything different than #f is considered to be true.
2.12.1 Exercises 7
2.12.1.1 Question 27
2.13 Predicates with a Variable Number of Arguments
> (< 1 2 3) #t
> (< 1 2 2) #f
2.14 Recognizers
Apart from relational operators, there are many other predicates in Racket, like zero?, that tests if the number is zero:
> (zero? 1) #f
> (zero? 0) #t
Note that zero? ends with an question mark because when a predicate is called, a question is being asked. For historical reasons, not all predicates in Racket follow this convention. However, when we define new predicates we should be mindful to use them.
Note that the operator zero? is used to recognize a particular element (zero) in a data type (numbers). These type of predicates are known as recognizers.
Another important set of predicates are the universal recognizers. These do not recognize one but all elements of a particular type of data. An universal recognizer accepts any kind of data as an argument and returns true if that elements belongs to the that same kind.
For example, to determine if a certain element is a number, we can use the number? predicate:
> (number? 1) #t
> (number? "Two") #f
And the string? predicate determines if entities are strings:
> (string? "Two") #t
> (string? 3) #f
Finally, there are predicates that recognize certain sub types of data, like the predicate integer? that recognizes integer values:
> (integer? 1) #t
> (integer? 1.0) #t
> (integer? 1.1) #f
> (integer? 2/3) #f
> (integer? "four") #f
Note that similar to the number 1, the number 1.0 is also an integer. The first is an exact number and the second one is an inexact number which implies that operations involving 1.0 will produce inexact results. To distinguish between both types of numbers we have the predicates exact? and the inexact?:
> (exact? 1) #t
> (exact? 1.0) #f
> (inexact? 1) #f
> (exact? 2/3) #t
2.14.1 Exercises 8
2.14.1.1 Question 28
What is a conditional expression? What is a logical expression?
2.14.1.2 Question 29
What is a logical value? Which logic values does Racket incorporate?
2.14.1.3 Question 30
What is a predicate? Give examples of predicates used in Racket.
2.14.1.4 Question 31
What is a relational operator? Give examples of relational operators used in Racket.
2.14.1.5 Question 32
What is a logical operator? Give examples of logical operators used in Racket.
2.14.1.6 Question 33
What is a recognizer? What is an universal recognizer? Give examples in Racket.
2.14.1.7 Question 34
\(x<y\)
\(x\leq y\)
\(x<y\wedge y<z\)
\(x<y\wedge x<z\)
\(x\leq y \leq z\)
\(x\leq y < z\)
\(x< y \leq z\)
2.15 Selection
If we look at the mathematical definition of absolute value:
\[x= \begin{cases} x, & \text{if $x<0$}\\ x, & \text{otherwise} \end{cases}\]
we notice that it uses a conditional expression in the form of
\[\begin{cases} \text{consequent expression}, & \text{if logical expression}\\ \text{alternative expression}, & \text{otherwise} \end{cases}\]
that translates to common language as “if logical expression then consequent expression, otherwise alternative expression”.
The evaluation of a conditional expression is made through the evaluation of the logical expression and if it is true, the consequent expression is applied, if it is false then the alternative expression is applied.
The use of conditional expressions in Racket is even easier than in mathematics because it is based on a simple operator, the if operator, called a selection operator since it allows to choice between two alternatives. The syntax of the if operator is as follows:
(if logicalexpression consequentexpression alternativeexpression)
The value of a conditional expression that uses the operator if is computed in the following way:
The logicalexpression is evaluated;
If the previous evaluation is true, then the combination value is the consequentexpression;
Otherwise, if the logical expression turns out to be false, the combination value is the alternativeexpression.
Such behaviour, identical to what we would have in Mathematics, can be verified by the following examples:
> (if (> 3 2) 1 2) 1
> (if (> 3 4) 1 2) 2
Using the if operator, we can now define the absolute value function by doing a simple translation from the mathematical definition to Racket:
(define (abs x) (if (< x 0) ( x) x))
The purpose of using the if operator is to define functions whose behaviour depends on one or more conditions. For example, if we consider the max function that receives two numbers as arguments and returns the highest. To define such function we only need to test if the first number is higher than the second one. If it is, the function returns the first argument, otherwise it returns the second one. Based on this logic we can write:
(define (max x y) (if (> x y) x y))
Another far more interesting example is the mathematical function sign \(\operatorname{sgn}\), also known as signum (latim for "sign"). This function could be interpreted as the dual function of the absolute value function because we will have that \(x=\operatorname{sgn}(x) x\). The sign function is defined as:
\[\operatorname{sgn} x = \begin{cases} 1 & \text{if $x<0$} \\ 0 & \text{if $x = 0$} \\ 1 & \text{otherwise}\end{cases}\]
In common language, we would say that if \(x\) is negative, the \(\operatorname{sgn} x\) value is \(1\), otherwise, if \(x\) is \(0\), the value is \(0\), otherwise the value is \(1\). That shows that the above expression uses two conditional expressions stacked in the following way:
\[\operatorname{sgn} x = \begin{cases} 1 & \text{se $x<0$} \\ \begin{cases} 0 & \text{se $x = 0$} \\ 1 & \text{otherwise}\end{cases} & \text{otherwise}\end{cases}\]
To define this function in Racket, two ifs must be used:
(define (signum x) (if (< x 0) 1 (if (= x 0) 0 1)))
2.16 Multiple Selection
When a conditional expression requires more than one if, it is possible that the code becomes increasingly harder to read. In this case there is an alternative called cond which makes the function’s definition easier to read. The syntax of cond is as follows:
(cond (expr0,0 expr0,1 ... expr0,n) (expr1,0 expr1,1 ... expr1,m) ... (exprk,0 exprk,1 ... exprk,p))
A cond receives any number of arguments. Each argument is called a clause and is made up of a list of expressions. The semantics of a cond is based on evaluating sequentially the first expression in each clause until one of them turns out to be true. In that case the consequent expressions are evaluated and the value of the last one is returned. If none of the clauses has a first expression that is true, the cond returns #<void> meaning there is no relevant result to be returned. If the clause, in which the first expression is true, has no other expressions then cond returns the value of that first expression.
It is important to note that the parenthesis around the clauses do not mean the clauses are combinations: they are simply part of the cond syntax and are necessary to separate clauses from each other.
The usual pragmatic used in a cond (especially when the clause only has two expressions) consists in aligning the expressions one under the other.
Using cond the sign function can be much easily defined:
(define (signum x) (cond ((< x 0) 1) ((= x 0) 0) (#t 1)))
Note that in the previous example the last cond clause has, as logical expression, the #t symbol. As we have seen, this last symbol represents true so its presence ensures that it will be evaluated in case none of the previous ones are. This way, a clause in the form of (#t ...) represents a "for everything else....". To make the reading task easier, Racket accepts a special form for this last situation: else. Using that syntax, the example can now be written as:
(define (signum x) (cond ((< x 0) 1) ((= x 0) 0) (else 1)))
2.16.1 Exercises 9
2.16.1.1 Question 35
Define the function sumhighest that given 3 numbers as arguments calculates the sum of the 2 with the highest value.
2.16.1.2 Question 36
Define the function max3 that given 3 numbers as arguments returns the highest value.
2.16.1.3 Question 37
Define the function secondhighest that given 3 numbers as arguments and returns the second highest number: the number between the maximum and minimum value.
2.17 Local Variables
Let us consider the following triangle:
and try to define a function in Racket to calculate the triangle’s area from the parameters \(a\), \(b\) and \(c\).
Heron of Alexandria was an important Greek mathematician and engineer of the 1st century A.D. to whom numerous discoveries and inventions were credited to, including the steam engine and the syringe.
\[A=\sqrt{s\left(sa\right)\left(sb\right)\left(sc\right)}\]
in which \(s\) is the triangle’s semiperimeter:
\[s=\frac{a+b+c}{2}\]
When trying to use the Heron’s formula to write the equivalent in Racket we come across a small problem: the formula is (also) written in terms of the semiperimeter \(s\), but \(s\) is not a parameter but rather a value that is derived from other parameters of the triangle.
One way of solving this problem is to replace \(s\) with its meaning:
\[A=\sqrt{\frac{a+b+c}{2}\left(\frac{a+b+c}{2}a\right)\left(\frac{a+b+c}{2}b\right)\left(\frac{a+b+c}{2}c\right)}\]
From this formula it is now possible to define the function in Racket:
(define (areatriangulo a b c) (sqrt (* (/ (+ a b c) 2) ( (/ (+ a b c) 2) a) ( (/ (+ a b c) 2) b) ( (/ (+ a b c) 2) c))))
Unfortunately, this definition has two problems. The first one is the loss of correspondence between the original formula and the function definition, making it harder to recognize as the Heron’s formula. The second one is that the function is repeatedly using (/ (+ a b c) 2) which is a waste of human effort, because we had to write it four times, and a waste of computational effort, because the expression needs to be calculated four times, even though we know it always has the same value.
In order to solve this problem, Racket allows the use of local variables. A local variable only has meaning in the context of a function and is used to calculate intermediate values such as the semiperimeter s. Using a local variable we can rewrite the trianglearea function:
(define (trianglearea a b c) (define s (/ (+ a b c) 2)) (sqrt (* s ( s a) ( s b) ( s c))))
The semantics used when definition local variables is the same as the definition of regular ones, with the added subtlety that its context, i.e. the part of the program in which the defined name can be used, is confined to the function that it contains.
When calling the function trianglearea, giving it the arguments for the corresponding parameters a, b and c, it starts by introducing an additional name  s  associated to the value that comes from the expression (/ (+ a b c) 2) and, in the context of than new name, evaluates the remaining expressions in the function’s body. In practice it is as if the function was stating: "Knowing that \(s=\frac{a+b+c}{2}\), let us calculate \(\sqrt{s\left(sa\right)\left(sb\right)\left(sc\right)}\)."
There is another way of defining local variables that, although semantically similar to the previous one, has the advantage of being usable in any part where an expression is expected. We can do that by using the let form. The redefinition of the previous function using the let form is as follows:
(define (trianglearea a b c) (let ((s (/ (+ a b c) 2))) (sqrt (* s ( s a) ( s b) ( s c)))))
Let uses the following syntax:
(let ((name0 expr0) (name1 expr1) ... (namen exprn)) exprn+1 exprn+2 ... exprn+m)
The semantic of the let form consists of associating each name (#,(lispemphi name "i") to the corresponding expression expri and, in the context established by that association, evaluate the let’s body, i.e., evaluate the expressions from exprn+1 to exprn+m and return the value of the last one.
One important characteristic of the let form is the fact that the association between expressions and names does not depend on other associated names. This implies that the context for evaluating the body of let is established just once, and not incrementally. This can be explained in the following example:
> (let ((pi 3) (prevpi pi)) (+ pi prevpi)) 6.141592653589793
However, if we want the context to be established incrementally, thus allowing expressions to associate names that can depend on previous established associations, Racket provides the form let*: form:
> (let* ((pi 3) (prevpi pi)) (+ pi prevpi)) 6
The Let* form is formally equivalent to a cascade of let forms, as can be seen in the following example:
> (let ((pi 3)) (let ((prevpi pi)) (+ pi prevpi))) 6
2.18 Global Variables
Contrary to local names that have a limited context, a global name, is a name that can be seen in any context of our programs. Its context is therefore the entire program. The name pi represents the constant \(\pi=3.14159...\) and can be used in any part of our program. For that reason, pi is a global name.
Also known as gold proportion and divine proportion among other names, and abbreviated to \(\phi\) in honour of Fineas, a Greek sculptor responsible for building the Parthenon where, supposedly, this golden proportion was used. The golden ration was first introduced by Euclid when solving the problem of dividing a line segment into two parts such that the ratio between the line segment and the longest part was equal to the ratio between the longest part and the shortest part. If \(a\) is the length of the longest part and \(b\) the shortest part, the Euclid’s problem is equivalent to \(\frac{a+b}{a}=\frac{a}{b}\). As a result, \(a^2abb^2=0\) or \(a=\frac{b\pm\sqrt{b^2+4b^2}}{2}=b\frac{1\pm\sqrt{5}}{2}\). What makes sense then is: \(a=b\frac{1+\sqrt{5}}{2}\). The expression for calculating the golden ratio is thus: \(\phi=\frac{a}{b}=\frac{1+\sqrt{5}}{2}.\)
\[\phi=\frac{1+\sqrt{5}}{2}\approx 1.6180339887\]
We simply need to write:
(define goldenratio (/ (+ 1 (sqrt 5)) 2))
From this moment on, the goldenration can be referenced in any part of our program.
It should be warned that global names should be limited, when possible, to defining just constants, like 2pi. Other useful examples may be pi/2, 4pi and pi/4, as well their symmetric values, that are defined in terms of:
(define 2pi (* 2 pi))
(define pi/2 (/ pi 2))
(define 4pi (* 4 pi))
(define pi/4 (/ pi 4))
(define pi ( pi))
(define 2pi ( 2pi))
(define pi/2 ( pi/2))
(define 4pi ( 4pi))
(define pi/4 ( pi/4))
2.19 Modules
Every functionality of Racket is stored and organized modules. Every module is a unit containing a set of definitions. The Racket language is nothing more than an aggregation of modules that provide often required functionalities. There are many other functionalities in modules that we can only have access to if we specifically ask for it.
Take for example the following program:
#lang racket 
(define (square x) 
(* x x)) 

(define (circlearea r) 
(* pi (square r))) 
The first line indicates the language in which the program is written, in this case, Racket. The following lines are the definitions of our program that, naturally, make use of Racket’s functionalities. For this program that includes the define function, the arithmetic operation * and the pi value.
If it was necessary to have additional functionalities we would have to require it, using the form require. For example, if we want to visualize the \(\sin\) function graph, we could use the plot module, like so:
#lang racket 
(require plot) 

(plot (function sin ( pi) pi)) 
The plot module used above provides the means to visualize graphs, namely the functions plot and function, with the remaining names being provided by Racket’s module.
The require form can also be used in other ways, the most useful being to access Racket’s central modules repository, called planet (http://planet.racketlang.org/). For example, for accessing the rosetta module, whose author is aml we should write:
#lang racket 
(require (planet aml/rosetta)) 
2.19.1 Exercises 10
2.19.1.1 Question 38
Define a module called hyperbolic with the three following functions: hyperbolic sin (sinh), hyperbolic cosin (cosh) and hyperbolic tangent (tanh), based on the mathematical definitions: \[\sinh x = \frac{e^xe^{x}}{2}\] \[\cosh x = \frac{e^x+e^{x}}{2}\] \[\tanh x = \frac{e^xe^{x}}{e^x+e^{x}}\]
2.19.1.2 Question 39
In the same module, define the inverse hyperbolic functions: asinh, acosh, and atanh, whose mathematical definitions are:
\[\sinh^{1} x=\ln(x+\sqrt{x^2+1})\] \[\cosh^{1} x=\pm\ln(x+\sqrt{x^21})\] \[\tanh^{1} x=\begin{cases} \frac{1}{2}\ln(\frac{1+x}{1x}), & \text{se $x<1$}\\ \frac{1}{2}\ln(\frac{x+1}{x1}), & \text{se $x>1$} \end{cases}\]
2.19.1.3 Question 40
Set the defined names, from the previous exercises, as available for other modules. Hint: See Racket’s documentation on provide.
3 Modelling
(require rosetta/tikz)  package: rosetta 
We saw in previous sections some types of predefined data in Racket. In many cases these are enough to build our programs. But in other cases it will become necessary to introduce new types of data. In this section we will study a new type of data that will become particularly useful to model geometric entities: coordinates.
3.1 Coordinates
Descartes was a 19th Century French philosopher, author of the famous quote: "Cogito ergo sum", (I think, therefore I am) and of countless contributions in the fields of Mathematics and Physics.
Cartesian coordinates of a point in space.
There are other types of useful operations that we can make using coordinates. For example, we can calculate the distance between two positions in space \(P_0=(x_0,y_0,z_0)\) and \(P_1=(x_1,y_1,z_1)\). That distance can be expressed using the formula:
\[d = \sqrt{(x_1x_0)^2+(y_1y_0)^2+(z_1z_0)^2}\]
and a first draft of this definition in Racket would be:
(define (dist x0 y0 z0 x1 y1 z1) (sqrt (+ (sqr ( x1 x0)) (sqr ( y1 y0)) (sqr ( z1 z0)))))
The distance between \((2,1,3)\) and \((5,6,4)\) would then be:
> (dist 2 1 3 5 6 4) 5.916079783099616
Unfortunately, by treating coordinates as a set of three independent numbers the use of functions becomes unclear. This can already be seen in the previous example, where the function dist calls upon six parameters forcing the reader to know where the coordinates of one point start and where they end. This problem becomes even worse when a function must return, not a number as it so happens with the function dist, but rather a position in space, as it happens, for example, with the function that computes the mid position \(P_m\) between \(P_0=(x_0,y_0,z_0)\) and \(P_1=(x_1,y_1,z_1)\). That position can be calculated using the formula:
\[P_m=(\frac{x_0+x_1}{2},\frac{y_0+y_1}{2},\frac{z_0+z_1}{2})\]
But it is difficult to conceive a function that implements this because, apparently, it would have to calculate three different result simultaneously, one for each of the coordinates \(X\), \(Y\) and \(Z\).
To deal with this kind of problems, mathematicians came up with the concept of \(tuple\): a tuple is nothing more than group of values but, in certain cases, this grouping has a specific name. For example, a rational number is tuple since it groups a number and a denominator, the same way that a position in space is also a tuple since it groups three coordinates.
All programming languages have mechanisms to create and manipulate tuples. In Racket, in addition to the predefined tuples, like rational and complex numbers, various other tuples have already been defined in the module rosetta which will be very useful to us when modelling geometry.
To use them, we must first require the module rosetta
#lang racket 
(require (planet aml/rosetta)) 
To set the coordinates \((x,y,z)\), Rosetta provides the function xyz:
> (xyz 1 2 3) #<xyz:1 2 3>
> (xyz (* 2 3) (+ 4 1) ( 6 2)) #<xyz:6 5 4>
Note that the result from evaluating the expression (xyz 1 2 3) is a value that represents a position in the threedimensional Cartesian space. That value is expressed as #<xyz:x y z> in which x, y, and z are the coordinate values.
3.2 Operations with Coordinates
Now that we know how to create coordinates, we can rethink the functions that manipulate them. Let us begin with the function that calculates the distance between two points \(P_0=(x_0,y_0,z_0)\) and \(P_1=(x_1,y_1,z_1)\), which, as we saw, can be calculated using the formula:
\[\sqrt{(x_1x_0)^2+(y_1y_0)^2+(z_1z_0)^2}\]
A first draft of this definition translated into Racket would be:
(define (dist p0 p1) (sqrt (+ (sqr ( ? ?)) (sqr ( ? ?)) (sqr ( ? ?)))))
In order to complete the function we need to know how to get the \(x\), \(y\), and \(z\) coordinates of a certain position \(P\). For that purpose, Rosetta provides the functions cx, cy and cz, abreviations for ccoordinate x, ccoordinate y and ccoordinate z respectively.
Using these functions we can now write:
(define (dist p0 p1) (sqrt (+ (sqr ( (cx p1) (cx p0))) (sqr ( (cy p1) (cy p0))) (sqr ( (cz p1) (cz p0))))))
The dist is predefined in Rosetta under the name distance.
> (dist (xyz 2 1 3) (xyz 5 6 4)) 5.916079783099616
Let us look at another example. Suppose we wanted to define a function that calculates the position of a point after a translation, expressed in terms of its orthogonal components \(\Delta_x\), \(\Delta_y\) and \(\Delta_z\), as can be seen in this figure. For \(P=(x,y,z)\) we will have \(P'=(x+\Delta_x,y+\Delta_y,z+\Delta_z)\). To make the use of this function easier we shall call it +xyz. Naturally, it needs as inputs a starting point \(P\) and the increments \(\Delta_x\), \(\Delta_y\) and \(\Delta_z\) that we will call dx, dy, and dz, respectively.
The point \(P'\) as a result of the translation of point \(P=(x,y,z)\) of \(\Delta_x\) in the \(X\) axis, of the \(\Delta_y\) in \(Y\) axis and \(\Delta_z\) in the \(Z\) axis.
The definition of this function is as follows:
(define (+xyz p dx dy dz) (xyz (+ (cx p) dx) (+ (cy p) dy) (+ (cz p) dz)))
Naturally we ca now use +xyz to define new functions, as for example, the horizontal and vertical translation:
(define (+x p dx) (+xyz p dx 0 0)) (define (+y p dy) (+xyz p 0 dy 0)) (define (+z p dz) (+xyz p 0 0 dz))
Equally useful would be the functions that compute the diagonal translations through the orthogonal planes XY, XZ and YZ:
(define (+xy p dx dy) (+xyz p dx dy 0)) (define (+xz p dx dz) (+xyz p dx 0 dz)) (define (+yz p dy dz) (+xyz p 0 dy dz))
The effect of this function can be seen in this figure.
Note that every function defined in this section is based on the xyz, cx, cy and cz operations, which we can consider to be the fundamental operations on coordinates. The first one allows us to construct a position given three numbers and the others to know which numbers determine a position. For this reason, the first operation is said to be a constructor of coordinates and the others selectors of coordinates.
Although we are unaware on how these functions operate internally, we know they are consistent with each other, ensured by the following expressions:
3.2.1 Exercises 11
3.2.1.1 Question 41
Define the function midpoint that calculates the threedimensional coordinates of the midpoint between two points \(P_0\) and \(P_1\), given their threedimensional coordinates.
3.2.1.2 Question 42
Define the function =c that compares two points coordinates and returns true if they are the coincident. Note that two points are coincident when their \(x\), \(y\), and \(z\) coordinates are equal.
3.2.2 Bidimensional Coordinates
Just as threedimensional coordinates locate points in space, bidimensional coordinates locate points in a plane. The question asked is: which plane? From a mathematical point of view, this question is not relevant since it is perfectly possible to think about geometry in a plane without needing to visualize the plane itself. But when we try to visualize that geometry in a 3D modelling program we must inevitably think where that plane is located. If omitted, CAD applications will by default consider the bidimensional plane to be the \(XY\) plane with the height \(Z\) being zero. So let us consider the bidimensional coordinate \((x,y)\) as a simplified notation for the threedimensional coordinate \((x,y,0)\).
Based on this simplification, we can define a bidimensional coordinates constructor in terms of the threedimensional coordinates constructor:
(define (xy x y) (xyz x y 0))
One of the advantages of defining bidimensional coordinates as a particular case of threedimensional coordinates is that the selectors cx, cy and cz are automatically applicable to bidimensional as well, as are the other functions we created, such as distance and the translations  +x, +y, +z, +xy, +xz, +yz and +xyz.
3.2.3 Exercises 12
3.2.3.1 Question 43
Given the point \(P_0=(x_0,y_0)\) and a line defined by two points \(P_1=(x_1,y_1)\) and \(P_2=(x_2,y_2)\), the minimum distance \(d\) between \(P_0\) and the line is given by:
\[d=\frac{\left\vert(x_2x_1)(y_1y_0)(x_1x_0)(y_2y_1)\right\vert}{\sqrt{(x_2x_1)^2+(y_2y_1)^2}}\]
Define a function pointlinedistance that given the coordinates of \(P_0\), \(P_1\) and \(P_2\) returns the minimum distance between \(P_0\) and the line defined by \(P_1\) and \(P_2\).
3.2.3.2 Question 44
3.2.4 Polar Coordinates
Although the Cartesian coordinate system is largely used, there are other coordinate systems that can be more useful in certain situations. As an example, suppose we wanted to set \(n\) elements, equally spaced between them and with distance \(d\) from the origin point, as is partially represented in this figure. Logically, the elements will eventually form a circle and it is easy to see that the angle between them would have to be \(\frac{2\pi}{n}\).
Positions along a circle.
Taking the \(X\) axis as reference, we can say that the first element will be positioned at \(d\) distance from the origin point, the second element will have the same distance but on a different axis that makes a \(\frac{2\pi}{n}\) angle with the \(X\) axis. The third element will have the same distance but in a different axis, making an angle of \(\frac{2\pi}{n}\) wit the \(X\) axis, and so on. However, when trying to define those positions using Cartesian coordinates we would find that the regularity expressed with the "and so on" is immediately lost. This should make us consider a different system of coordinates, namely the polar coordinate system.
As represented in this figure, a position in a bidimensional plane is expressed, in rectangular coordinates, by the numbers \(x\), and \(y\)  respectively the xaxis and yaxis, while in polar coordinates it is expressed by \(\rho\) and \(\phi\)  respectively the radius vector (also called module) and the polar angle (also called argument). With the help of trigonometry and the Pythagorean theorem it is easy to convert polar coordinates into rectangular coordinates:
\[\left\{\begin{aligned} x&=\rho \cos \phi\\ y&=\rho \sin \phi \end{aligned}\right.\]
or from rectangular coordinates to polar coordinates:
\[\left\{\begin{aligned} \rho&=\sqrt{x^2 + y^2}\\ \phi&=\arctan \frac{y}{x} \end{aligned}\right.\]
Rectangular and polar coordinates.
Based on the above equations we can define the constructor of polar coordinates pol (abbreviation of "polar") that will construct coordinates from its polar representation, by simply converting them into the equivalent rectangular ones:
(define (pol ro fi) (xy (* ro (cos fi)) (* ro (sin fi))))
These functions are already predefined in Rosetta.
(define (polrho c) (sqrt (+ (sqr (cx c)) (sqr (cy c))))) (define (polphi c) (atan (cy c) (cx c)))
Note that in some cases the coordinates numbers are not zero or one as we would expect, but values very close to them. This is due to rounding errors. And also note that since we are using bidimensional coordinates the \(z\) coordinate is always zero.
> (pol 1 0) #<xyz:1 0 0>
> (pol (sqrt 2) (/ pi 4)) #<xyz:1.0000000000000002 1.0 0>
> (pol 1 (/ pi 2)) #<xyz:6.123233995736766e17 1.0 0>
> (pol 1 pi) #<xyz:1.0 1.2246467991473532e16 0>
Another very useful operation is the one that, given a point \(P=(x,y)\) and a ""vector"" with its origin in \(P\) and specified in polar coordinates by a distance \(\rho\) and an angle \(\phi\), returns the the point located at the end of the vector, as shown in this figure. Using trigonometry it is easy to calculate that this position given by \(P'=(x+\rho\cos\phi, y+\rho\sin\phi)\).
A point displacement in polar coordinates
This translated into Racket becomes:
(define (+pol p ro fi) (+xy p (* ro (cos fi)) (* ro (sin fi))))
Some examples of its use:
> (+pol (xy 1 2) (sqrt 2) (/ pi 4)) #<xyz:2.0 3.0 0>
> (+pol (xy 1 2) 1 0) #<xyz:2 2 0>
> (+pol (xy 1 2) 1 (/ pi 2)) #<xyz:1.0 3.0 0>
3.2.5 Exercises 13
3.2.5.1 Question 45
The function =c defined in Question 42 compares the coordinates of two points, returning true if they are coincidental. However, taking into account that numeric operations can produce rounding errors it is possible that two coordinates, which in theory should be the same, in practice are not considered as such. For example, the point \((1,0)\) in rectangular coordinates can be expressed in polar coordinates as \(\rho=1, \phi=\pi\) but Racket will not consider them equal, which can be seen in the example below:
> (=c (xy 1 0) (pol 1 pi)) #f
> (xy 1 0) #<xyz:1 0 0>
> (pol 1 pi) #<xyz:1.0 1.2246467991473532e16 0>
As you can see, although the coordinates are not the same they are very close, i.e., the distance between them is very close to zero. Propose a new definition for the function =c based on the concept of distance between coordinates.
3.3 Bidimensional Geometric Modelling
In this section we will be introducing some bidimensional geometric modelling operations.
In order to visualize the shapes we create we need to have a CAD application, like AutoCAD or Rhino. The choice of which CAD application we wish to use is made using the backend function together with the argument (autocad or rhino). Therefore, a program that uses Rosetta usually starts with:
#lang racket 
(require (planet aml/rosetta)) 
(backend autocad) 
or with:
#lang racket 
(require (planet aml/rosetta)) 
(backend rhino) 
depending on the user’s preference for AutoCAD or Rhino, respectively.
From here onwards, we will omit the header that requires Rosetta and chooses the CAD application and, instead, we shall focus our attention on the operations for geometric modelling.
#lang racket 
(require (planet aml/rosetta)) 
(backend autocad) 

(circle (pol 0 0) 4) 
(circle (pol 4 (/ pi 4)) 2) 
(circle (pol 6 (/ pi 4)) 1) 
A series of circles.
The swastika is a mythical symbol, used by many cultures since the neolithic period
(line (xy 1 1) (xy 1 0) (xy 1 0) (xy 1 1)) (line (xy 1 1) (xy 0 1) (xy 0 1) (xy 1 1))
A set of line segments.
In case we wish to draw closed polygonal lines it is preferable that we use the polygon function, very similar to the function line but with the difference that it creates an additional segment connecting the last position with the first. This figure shows the result of the following code:
(polygon (pol 1 (* 2 pi 0)) (pol 1 (* 2 pi 1/5)) (pol 1 (* 2 pi 2/5)) (pol 1 (* 2 pi 3/5)) (pol 1 (* 2 pi 4/5)))
A polygon.
And for drawing regular polygons, i.e., polygons that have equal edges and angles, as shown in this figure, it is preferable to use the function regularpolygon. This function receives as arguments the number of sides, its centre point, a radius, a rotation angle and a boolean to indicate if the radius refers to an inscribed circle (i.e. the radius is the distance from the edges to the centre) or a circumscribed circle (i.e. the radius is the distance from the vertexes to the centre). If omitted, the centre point will be considered the origin, the radius will have one unit of measurement, the angle will be considered zero and the circle will be circumscribed.
Using regularpolygon, this figure can be obtained by:
(regularpolygon 5)
More interesting examples can be obtained by changing the rotation angle. For example, the following expressions will produce the image shown in this figure:
(regularpolygon 3 (xy 0 0) 1 0 #t) (regularpolygon 3 (xy 0 0) 1 (/ pi 3) #t)
(regularpolygon 4 (xy 3 0) 1 0 #t) (regularpolygon 4 (xy 3 0) 1 (/ pi 4) #t)
(regularpolygon 5 (xy 6 0) 1 0 #t) (regularpolygon 5 (xy 6 0) 1 (/ pi 5) #t)
Overlapping triangles, squares and pentagons with different rotation angles.
For four sided polygons aligned with the \(X\) and \(Y\) axis there is a very simple function: rectangle. This function can either be used with the position of its bottom left corner and upper right corner or the with the position of its bottom left corner and the two rectangle dimensions, as exemplified below and represented in this figure:
(rectangle (xy 0 1) (xy 3 2)) (rectangle (xy 3 2) 1 2)
A set of rectangles
In the following sections we will introduce the remaining functions available in Rosetta.
3.3.1 Exercises 14
3.3.1.1 Question 46
Recreate the drawing presented in this figure but this time using rectangular coordinates.
3.3.1.2 Question 47
We wish to place two circles with unit radius and around an origin so that the circles are tangent to each other as shown in the following drawing:
Write a sequence of expressions that when evaluated produce the above image.
3.3.1.3 Question 48
We wish to place four circles with unit radius and around an origin so that the circles are tangent to each other as shown in the following drawing:
Write a sequence of expressions that when evaluated produce the above image.
3.3.1.4 Question 49
We wish to place three circles with unit radius and around an origin so that the circles are tangent to each other as shown in the following drawing:
Write a sequence of expressions that when evaluated produce the above image.
3.4 Side Effects
In Racket, as we have previously seen, every expression has a value. We can see that by evaluating an arithmetic expression but also when evaluating a geometric expression:
> (+ 1 2) 3
> (circle (xy 1 2) 3) #<circle 0>
In the last example, the result of evaluating the second expression is a geometric entity. When Racket writes a result that is a geometric entity it uses a notation based on the name of that entity and an integer to distinguish between them. Most of the times we are not only interested in seeing a geometric entity as a piece of text but we are also interested in visualizing that entities in space. For that purpose the evaluation of geometric expressions also has a side effect: all created geometrical forms are automatically added to the chosen CAD application using the backend function.
That way, evaluating the following program:
#lang racket 
(require (planet aml/rosetta)) 
(backend autocad) 

(circle (xy 1 2) 3) 
produces as a result an abstract value that represents a circle with a radius of \(3\) units, centred at the point \(1,2\) and, as a side effect, that circle becomes visible in AutoCAD.
This behaviour of geometric functions like circle, line, rectangle, etc., is fundamentally different from the ones we have seen so far because previously these functions were used to compute something, i.e., to produce a value, and now it is not that value that interests us the most but rather its side effect (also called collateral effect) that allows us to visualize the geometrical shape in the CAD application.
One important aspect of using side effects is the possibility of their composition. The composition of side effects is accomplished through sequencing, i.e., the sequential computation of the different effects. In the next section we will discuss sequencing of side effects.
3.5 Sequencing
So far, we have combined mathematical expressions using mathematical operators. For example, from the expressions (sin x) and (cos x) we can calculate their division by (/ (sin x) (cos x)). This is possible because the evaluation of the subexpressions (sin x) and (cos x) will produce two values that can then be used in the division.
With geometrical functions these kinds of combinations must be done differently since, as we saw, their evaluation also produces side effects. Seeing as these side effects are precisely what we want, Racket provides a way of producing them sequentially, i.e., one after the other. That feature is called begin and is used for sequencing side effects. For example, consider a function that draws a circle with a radius \(r\), centred on \(P\) with an inscribed or circumscribed square, depending on the user’s specification, as we show in this figure.
A circle with a square inscribed (left) and a square circumscribed (right).
The function’s definition could start as something like:
(define (circlesquare p r inscribed?) ...)
The drawing produced by the function will naturally depend on the logic value of inscribed?, that is, this function’s definition could be something like:
(define (circlesquare p r inscribed?) (if inscribed? "create a circle and a square inscribed in that circle" "create a circle and a square circumscribed in that circle"))
The problem now is that, for each case of if, the function must generate two side effects, namely create a circle and create a square. But the if only admits one expression, making us wonder how we would be able to combine two side effects in a single expression. For this purpose Racket provides the form Sbegin. This operator will take any number of expressions and will sequentially evaluate them, i.e., one after the other, returning the value of the last. Logically, if only the value of the last expression is used, then all other values from the other expressions are discarded and these are only relevant for the side effect they may have produced.
Using the begin operator we can further detail our function:
(define (circlesquare p r inscribed?) (if inscribed? (begin "create a circle" "create a square inscribed in the circle") (begin "create a circle" "create a square circumscribed in the circle")))
All we need to do now is translate each expressions into the corresponding functions. In the case of the inscribed square we will use polar coordinates because we know its vertices will be set in a circle, one with \(\frac{\pi}{4}\) and another at \(\pi+\frac{\pi}{4}\).
(define (circlesquare p r inscribed?) (if inscribed? (begin (circle p r) (rectangle (+pol p r (* 5/4 pi)) (+pol p r (* 1/4 pi)))) (begin (circle p r) (rectangle (+xy p ( r) ( r)) (+xy p r r)))))
Note that for the if operator, both the consequence and the alternative are one single expression even though each of those expressions is the result of two other more elemental expressions. The begin operator can therefore be seen as a mechanism for grouping expressions.
Even though sequencing expressions requires the use of the operator begin, it is usually possible to minimize its use in these expressions. An attentive look at the previous function circlesquare shows that a circle is always created independently of the square being inscribed or circumscribed. That way we can redefine the function so that the circle is created outside the if:
(define (circlesquare p r inscribed?) (begin (circle p r) (if inscribed? (begin (rectangle (+pol p r (* 5/4 pi)) (+pol p r (* 1/4 pi)))) (begin (rectangle (+xy p ( r) ( r)) (+xy p r r))))))
It is now clear that the two begins of the if form only have one expression each so its use is unnecessary. We can therefore simplify the expression to:
(define (circlesquare p r inscribed?) (begin (circle p r) (if inscribed? (rectangle (+pol p r (* 5/4 pi)) (+pol p r (* 1/4 pi))) (rectangle (+xy p ( r) ( r)) (+xy p r r)))))
Even tough the operator begin is necessary every time we wish to evaluate more than one expression, there are some situations where Racket uses an implicit begin, such as in a function’s body. In fact, when a function is invoked every expression in the function’s body is evaluated sequentially and the function’s value is only determined by the last expression. This fact allows us to simplify the circlesquare function even more:
(define (circlesquare p r inscribed?) (circle p r) (if inscribed? (rectangle (+pol p r (* 5/4 pi)) (+pol p r (* 1/4 pi))) (rectangle (+xy p ( r) ( r)) (+xy p r r))))
3.5.1 Exercises 15
3.5.1.1 Question 50
Define a function called circleandradius that given the coordinates of its centre point and the radius creates the specified circle and, as shown in this figure, places the text describing the radius on the circle’s right side. The text should be proportional to the circle’s size.
3.5.1.2 Question 51
Using the previously defined function circleandradius, recreate this figure.
3.6 Doric Order
The Doric Order exemplified in the Greek Temple of Segesta. This temple was never ended Photography by Enzo De Martino.
These columns also present an intentional deformation called entasis. The entasis is a small curvature give to columns and is believed to be a way a creating a optical illusion in order to misguide the eye to see the column curved instead of straight.
Even though we are particularly interested in three.dimensional modelling, for pedagogic reasons, we are going to start by sketching a Doric column (without flutes) in two dimensions. In the following sections we will extend this process to create a threedimensional model.
The same way a Doric columns can be decomposed into its basic components  shaft, Echinus and abacus  the drawing of one can too be decomposed in components. Therefore, we will create functions to draw the shaft, Echinus and abacus. this figure shows a reference model. Let us start by defining a function for the shaft.
The Doric column for reference
(define (shaft) (line (xy 0.8 10) (xy 1 0) (xy 1 0) (xy 0.8 10) (xy 0.8 10)))
In this example, we used the line function that, given a sequence of positions, will create a line with vertices on those positions. Another possibility, probably more appropriate, would be to create a closed polygonal line, something we can do with the polygon function, omitting the repeated position, i.e.:
(define (shaft) (polygon (xy 0.8 10) (xy 1 0) (xy 1 0) (xy 0.8 10)))
To complete the figure we also need a function for the Echinus and another for the abacus. The reasoning for the Echinus is similar:
(define (echinus) (polygon (xy 0.8 10) (xy 1 10.5) (xy 1 10.5) (xy 0.8 10)))
For the abacus we could use a similar strategy or explore a function in Rosetta that creates rectangles. This function requires two points to define a rectangle:
(define (abacus) (rectangle (xy 1 10.5) (xy 1 11)))
Finally, the expression that creates a Doric column:
(define (doriccolumn) (shaft) (echinus) (abacus))
Note that the doriccolumn function sequentially calls the functions shaft, Echinus and finally abacus.
This figure shows the result of invoking the doriccolumn function.
A Doric column.
3.7 Parametrization of Geometric Figures
Unfortunately, the Doric column we created has fixed position and size, making it difficult to use this function in different contexts. Naturally, this function would be much more useful if its creating was parametrized, i.e., if its creation depended on the parameters that describe a column, as for example, the column’s base coordinates, the height of the Echinus, shaft and abacus, the base and top Echinus radius, etc.
In order to better understand this parametrization of these functions, let us start by considering the shaft represented in this figure.
Sketch of a column’s shaft.
The first step in parametrizing a geometrical drawing is to correctly identify the relevant parameters. In the case of the shaft one of the obvious parameters would be its spatial location, i.e., the coordinates of a reference point in relation to which the shaft will be drawn. Let us then consider that the shaft will be placed with its base’s centre point at an imaginary point \(P\) of arbitrary coordinates \((x,y)\). In addition to this parameter we also need know the height of the shaft \(a\) and both base \(r_b\) and top radius \(r_t\).
To make the drawing process easier it is convenient to consider additional reference points in our sketch. For the shaft, since it’s shaped essentially like a trapeze, we can look at its shape as the succession of line segments along a sequence of points \(P_1\), \(P_2\), \(P_3\) and \(P_4\), points which we can easily calculate from \(P\).
We now have all we need to define a function that draws the column’s shaft. To make the program clearer we will use the names ashaft for the height \(a\), the base radius rbase and top radius rtop respectively. The definition will now be:
(define (shaft p ashaft rbase rtop) (polygon (+xy p ( rtop) ashaft) (+xy p ( rbase) 0) (+xy p (+ rbase) 0) (+xy p (+ rtop) ashaft)))
Next we need to draw out the Echinus. It is convenient to consider once more a geometrical sketch, as shown in this figure.
Sketch of an Echinus.
As similar to the shaft, from the bases’ centre point \(P\) we can compute the coordinates that define the extremities of the lines that draw an Echinus. Using those points, the function will be:
(define (echinus p aechinus rbase rtop) (polygon (+xy p ( rbase) 0) (+xy p ( rtop) aechinus) (+xy p (+ rtop) aechinus) (+xy p (+ rbase) 0)))
Having done the shaft and Echinus all that is left now is to draw the Abacus. For that we can consider yet another geometric sketch, shown in this figure.
Sketch of an Abacus.
Once more we will consider \(P\) as the starting point in the Abacus’s base. From this point we can easily calculate the points \(P_1\) and \(P_2\), that are the two opposite corners of the rectangle that defines the Abacus. That way, we have:
(define (abacus p aabacus labacus) (rectangle (+xy p (/ labacus 2) 0) (+xy p (/ labacus 2) aabacus)))
Finally, to create the entire column we must combine the functions that draw the shaft, the Echinus and the Abacus. We need only to take into account that, as this figure shows, the shaft’s top radius is coincidental with the Echinus’ base radius and the Echinus top radius is half the width of the Abacus. The figure also shows that the coordinates of the Abacus’ base is the result of adding the shaft’s height to the the coordinates of the the shaft’s base and that the Abacus’s base coordinates are the result of adding the combined heights of the shaft and Echinus to the shaft’s base coordinates.
Composition of the shaft, Echinus and Abacus.
As we did before, let us give more appropriate names to the parameters in this figure. Using the names p, ashaft, rbaseshaft, aEchinus, rbaseEchinus, aabacus and labacus instead of the corresponding, \(P\), \(a_f\), \(r_{bf}\), \(a_c\), \(r_{bc}\), \(a_a\) and \(l_a\), we obtain:
(define (column p ashaft rbaseshaft aechinus rbaseechinus aabacus labacus) (shaft p ashaft rbaseshaft rbaseechinus) (Echinus (+xy p 0 ashaft) aechinus rbaseechinus (/ labacus 2)) (abacus (+xy p 0 (+ ashaft aechinus)) aabacus labacus))
Using this function we can easily explore different variations of columns. The following examples reproduce what is shown in this figure.
(column (xy 0 0) 9 0.5 0.4 0.3 0.3 1.0) (column (xy 3 0) 7 0.5 0.4 0.6 0.6 1.6) (column (xy 6 0) 9 0.7 0.5 0.3 0.2 1.2) (column (xy 9 0) 8 0.4 0.3 0.2 0.3 1.0) (column (xy 12 0) 5 0.5 0.4 0.3 0.1 1.0) (column (xy 15 0) 6 0.8 0.3 0.2 0.4 1.4)
Multiple Doric columns.
As we can see, not all columns obey the proportion’s canon of the Doric order. Further ahead we are going to see which modifications are needed to avoid this problem.
3.8 Documentation
In the column function, ashaft is the shaft’s height, rbaseshaft is the shaft’s base radius, rtopshaft is the shaft’s top radius, aechinus is the Echinus’ height and, finally, rabacus is the Abacus’ radius. Because the function already employs many parameters and its meaning may not be clear to someone that reads the function’s definition for the first time, it is convenient to document the function. For that, Racket provides a special syntax: each time a semicolon ; is used Racket will ignore everything that is in front of it until the end of that line. That allows text to be written between the code without Racket trying to interpret its meaning.
The program is so simple in fact that it should not require so much documentation.
;;;;Drawing Doric Columns 

;;;The drawing of a Doric column is divided into three parts: 
;;;shaft, Echinus and abacus. Each of those parts has an 
;;;independent function. 

;Draws the shaft of a Doric column. 
;p: column's centre base coordinate, 
;ashaft: shaft's height, 
;rbase: shaft's base radius, 
;rtop: shaft's top radius. 
(define (shaft p ashaft rbase rtop) 
(polygon (+xy p ( rtop) ashaft) 
(+xy p ( rbase) 0) 
(+xy p (+ rbase) 0) 
(+xy p (+ rtop) ashaft))) 

;Draws the Echinus of a Doric column. 
;p: Echinus centre base coordinate, 
;aechinus: Echinus height, 
;rbase: Echinus base radius, 
;rtop: Echinus top radius. 
(define (Echinus p aEchinus rbase rtop) 
(polygon (+xy p ( rbase) 0) 
(+xy p ( rtop) aechinus) 
(+xy p (+ rtop) aechinus) 
(+xy p (+ rbase) 0))) 

;Draws the Abacus of a Doric column. 
;p: column's centre base coordinate, 
;aabacus: abacus height, 
;labacus: abacus width. 
(define (abacus p aabacus labacus) 
(rectangle (+xy p (/ labacus 2) 0) 
(+xy p (/ labacus +2) aabacus))) 

;Draws a Doric column composed of a shaft, an Echinus and an Abacus. 
;p: column's centre base coordinate, 
;ashaft: shaft's height, 
;rbaseshaft: shaft's base radius, 
;rbaseechinus: Echinus base radius = shaft's top radius, 
;aechinus: Echinus height, 
;aabacus: abacus height, 
;labacus: abacus width = 2*Echinus top radius. 
(define (column p 
ashaft rbaseshaft 
aechinus rbaseechinus 
aabacus labacus) 
;;We draw the shaft at the base point p 
(shaft p ashaft rbaseshaft rbaseechinus) 
;;We place the Echinus on top of the shaft 
(Echinus (+xy p 0 ashaft) aechinus rbaseechinus (/ labacus 2)) 
;;and the Abacus on top of the Echinus 
(abacus (+xy p 0 (+ ashaft aechinus)) aabacus labacus)) 
When a program is documented, is easier to understand what the program ~does, without having to study the functions’ body. As we can see in the following example, it is common pragmatic in Racket to use a different number of semicolons to indicate the importance of the comment:
;;;;
They should be placed on the left margin and are used to divide the program into sections and to give a title to each one.;;;
They also should be placed on the left margin and are used for general commenting of the program that will follow. These should not be used inside functions.;;
Should be aligned with the correspondent piece of code, which appears immediately below.;
Should be placed in a same column to the right, referencing the piece of code immediately to the left.
It is important that we get used to documenting our programs but it is important to note that excessive documentation can also have disadvantages:
The code in Racket should be clear enough for a human being to understand. It is better to spend more time writing clearer code than to write documentation that explains it.
Having documentation that does not correspond to the program is worse than having no documentation at all.
Frequently, we need to modify our programs in order to adapt them to different purposes. The more documentation we have, the more documentation we will have to modify to make it relevant with the changes made to the program.
For these reasons, we should try to write the code as clear as possible and at the same time provide short and useful documentation: the documentation should not say what is already obvious from reading the program.
3.8.1 Exercises 16
3.8.1.1 Question 52
Consider an arrow with an origin in \(P\), a length of \(\rho\), an inclination \(\alpha\) , an opening angle \(\beta\) and a tip \(\sigma\), as shown in the following figure:
Define a function arrow that, given the parameters \(P\), \(\rho\), \(\alpha\), \(\beta\) and \(\sigma\) creates the corresponding arrow.
3.8.1.2 Question 53
Based on the previous exercise define a function that given the point \(P\), the length \(\rho\) and the angle \(\alpha\) draws "the North" as shown in the following figure:
The opening angle \(\beta\) must be \(45\,^{\circ}.\)
The arrow’s tip length \(\sigma\) must be \(\frac{\rho}{2}\).
The "N" should be distanced \(\frac{\rho}{10}\) from the arrow’s tip in the arrow’s direction
The size of "N" should be half the length \(\rho\).
3.8.1.3 Question 54
Using the function arrow function, define a new function called arrowfromto that given two points, creates an arrow that goes from the first point to the second. coordinate. The tips should have one unit as measurement and the angle should be \(\frac{\pi}{8}\).
3.8.1.4 Question 55
Consider a house with only rectangular rooms. Define the function rectangulardivisions that receives as parameters the bottom left corner of a room, the length and width of the room and a text describing that room’s type of use. With those values, that function should build a rectangle with two text lines, one with the rooms type of use and the other with the room’s floor area. For example, the following code lines:
(rectangulardivision (xy 0 0) 4 3 "kitchen") (rectangulardivision (xy 4 0) 2 3 "pantry") (rectangulardivision (xy 6 0) 5 3 "room") (rectangulardivision (xy 0 5) 5 4 "livingroom") (rectangulardivision (xy 5 5) 3 4 "toilet") (rectangulardivision (xy 8 3) 3 6 "room")
They produce as a result the following drawing:
3.9 Debugging
As we all know Errare humanum est. Making mistakes is part of our daytoday life and we mostly learn how to deal with them. The same does not apply to programming languages. Any mistake made in a computer program will result in it having a different behaviour than that which was to be expected.
Seeing how easy it is to make errors, it should also be easy to detect and correct them. The process of detecting and correcting errors is called debugging. Different programming languages provide different mechanisms to do that. As we will see Racket is well covered for these mechanisms.
Generally, errors in programs can be classified as syntactic errors or semantic errors.
3.9.1 Syntactic Errors
Syntactic errors occur each time we write a line of code which does not follow the language’s grammar rules. As an example let us suppose we wish to create a function to create a single Doric column, which we will refer as standard, and that always has the same dimensions, dispensing the need for any other parameters. One possible definition is:
(define (standardcolumn (column (xy 0 0) 9 0.5 0.4 0.3 0.3 0.5)))
However, if we test this definition, Racket will produce an error, telling us that something is wrong:
define: not an identifier,identifier with default,or keyword for procedure argument in: (column (xy 0 0) 9 0.5 0.4 0.3 0.3 0.5)
The error Racket is warning us about is that the form define does not follow the required syntax, and in fact a careful look at it shows we have not followed the correct syntax that, as we saw in section Defining Functions, should obey the following structure:
(define (name parameter1 ... parametern) body)
Our mistake is now obvious, we forgot to close the parentheses in the list of parameters. That makes Racket use the following expression as part of the parameters list and, being unable to do so, Racket reports a syntactic error, i.e., a "sentence" which does not obey the language syntax.
There are several other types of errors that Racket is capable of detecting and these will be discussed as we go along. The important thing is not that what kind of syntactic errors Racket is capable of detecting but rather that Racket is capable of checking the expressions we write and detect syntactic errors in them before evaluating them.
3.9.2 Semantic Errors
Semantic errors are very different from syntactic ones. A semantic error is not the misspelling of a "sentence" but rather a mistake in its meaning. In other terms, a semantic error occurs when we write a sentence which we think has a certain meaning but in reality it has a different one.
Generally semantic errors can only be detected during the evaluation of the functions that contain them. Part of the semantic errors are detectable by Racket’s evaluator but countless others can only be detected by the programmer.
As an example of a semantic error consider a meaningless operation such as the sum of a number with a string:
> (+ 1 "two") +: contract violation
expected: number?
given: "two"
argument position: 2nd
other arguments...:
1
As we can see the mistake is explained in the given message, which says that the second argument should be a number. In this example the mistake is obvious enough for us to detect it immediately. However, with more complex programs the process of finding mistakes can often be slow and frustrating. It is also a fact that the more experienced we get at detecting errors the faster we will be at doing it..
3.10 Threedimensional Modelling
As you have seen in the previous section, Rosetta provides multiple drawing tools (lines, rectangles, circles, etc.) that allows us to easily draw bidimensional representations of objects such as floor sections, side sections, etc.
Although to this moment we have only used Rosetta’s bidimensional drawing capacities it is possible to go further and start exploring threedimensional modelling. This type of modelling represents lines, surfaces and volumes in the threedimensional space.
In this section we will study Rosetta’s functions the allow the creating of such threedimensional objects.
3.10.1 Predefined Solids
Rosetta provides a set of predefined functions that create solids from their threedimensional coordinates. Although these predefined functions only allow the creation of a limited number of solids they are enough to create sophisticated models.
The predefined operations can create boxes (function box), cylinders (function cylinder), cones (function cone), spheres (function sphere), toruses (function torus) and pyramids (function regularpyramid). Each of these functions accepts various optional arguments for creating solids in these solids in different ways. this figure shows a set of solids built from the following expressions:
(box (xyz 2 1 1) (xyz 3 4 5)) (cone (xyz 6 0 0) 1 (xyz 8 1 5)) (conefrustum (xyz 11 1 0) 2 (xyz 10 0 5) 1) (sphere (xyz 8 4 5) 2) (cylinder (xyz 8 7 0) 1 (xyz 6 8 7)) (regularpyramid 5 (xyz 2 1 0) 1 0 (xyz 2 7 7)) (torus (xyz 14 6 5) 2 1)
Primitive solids in Rosetta.
Great Pyramid of Giza. Photography by Nina Aldin Thune.
It is interesting to notice that some of the most important works in architectural history could be modelled directly using these operations. One example is the Great Pyramid of Giza, this figure, built over forty five centuries ago and an almost perfect example of a square pyramid. In its original form, the Great Pyramid had a square base, with sides measuring 230 meters, and a height of 147 meters which made it the tallest manmade structure up until the construction of the Eiffel Tower. Many other Egyptian pyramids have similar proportions which makes them easy to model with the following function:
(define (egyptianpyramid p side height) (regularpyramid 4 p (/ side 2) 0 height #f))
The case of the Great Pyramid of Giza would be:
(egyptianpyramid (xyz 0 0 0) 230 147)
Contrary to the Great Pyramid of Giza, there are not many examples that can be modelled with a single geometric primitive. It is however possible to create more complex structures by combining these primitives.
Take for example the cross in this figure, built using six identical truncated cones, with their base positioned at the same point and the top vertices positioned on orthogonal axis.
Cross, overlapping truncated cones.
To model this solid we will parametrize the base point \(p\) and the other cone’s dimensions : the base radius \(r_b\), top radius \(r_t\) and length \(c\):
(define (cross p rb rt c) (conefrustum p rb (+x p c) rt) (conefrustum p rb (+y p c) rt) (conefrustum p rb (+z p c) rt) (conefrustum p rb (+x p ( c)) rt) (conefrustum p rb (+y p ( c)) rt) (conefrustum p rb (+z p ( c)) rt))
3.10.2 Exercises 17
3.10.2.1 Question 56
Using the function cross determine approximate values for the parameters as to create the following models:
3.10.2.2 Question 57
Using the function conefrustum define the function hourglass function that given the base centre point, the base radius, the neck radius and the height, creates the model of a hourglass similar to the following example:
Similar to the truncated cone, but with a polygonal base, we have the truncated pyramid. A truncated pyramid can be created using the function regularpyramidfrustum. For that we must specify the number of sides, the base centre point, the radius of the circle that circumscribes the base, the base rotation angle, the top centre point and the radius of the circle that circumscribes the top.
this figure you can see the rotation’s angle effect given by the following expressions:
Three truncated pyramids with different rotation angles. From left to right the angles are: \(0\), \(\frac{\pi}{4}\) e \(\frac{\pi}{2}\).
(regularpyramidfrustum 3 (xyz 0 0 0) 2 0 (xyz 0 0 1) 1) (regularpyramidfrustum 3 (xyz 8 0 0) 2 pi/4 (xyz 8 0 1) 1) (regularpyramidfrustum 3 (xyz 16 0 0) 2 pi/2 (xyz 16 0 1) 1)
The Dahshur pyramid. Photography by Ivrienen.
For a more architectural example let us consider the Dahshur rhomboidal pyramid, illustrated in this figure, characterized for having sloped edges that abruptly change from \(43^{\circ}\) to \(55^{\circ}\), presumably to avoid its collapse due to the original slope. This pyramid is \(188.6\) meters wide and \(101.1\) meters tall and it can be decomposed into two geometrical forms, an initial truncated pyramid onto of which stands a quadrangular pyramid.
In order to generalise the modeling of this type of pyramid we can consider the schematic model in This figure where a section of this pyramid is shown. From there it is easy to see that \(h_0 + h_1 = h\) and that \(l_0+l_1=l\). Moreover we have the trigonometric functions
\[\tan\alpha_0=\frac{h_0}{l_0}\qquad\qquad \tan\alpha_1=\frac{h_1}{l_1}\]
As a result, we have
\[l_0=\frac{hl\tan\alpha_1}{\tan\alpha_0\tan\alpha_1}\]
Section of a rhomboidal pyramid.
Transcribing these functions into Racket we have:
(define (rhomboidalpyramid p l h a0 a1) (define l0 (/ ( h (* l (tan a1))) ( (tan a0) (tan a1)))) (define l1 ( l l0)) (define h0 (* l0 (tan a0))) (define h1 ( h h0)) (regularpyramidfrustum 4 p l 0 h0 l1) (regularpyramid 4 (+z p h0) l1 0 h1))
The rightmost model in This figure shows a Dahshur pyramid created by the following expression:
(rhomboidalpyramid (xyz 0 0 0) (/ 186.6 2) 101.1 (radians<degrees 55) (radians<degrees 43))
In the same image, to the left, two other pyramids were created with the following expressions:
(rhomboidalpyramid (xyz 300 0 0) (/ 186.6 2) 101.1 (radians<degrees 75) (radians<degrees 40)) (rhomboidalpyramid (xyz 600 0 0) (/ 186.6 2) 101.1 (radians<degrees 95) (radians<degrees 37))
Three different rhomboidal pyramids.
Washington Monument. Photography by David Iliff.
3.10.3 Exercises 18
3.10.3.1 Question 58
An obelisk is a monument shaped like a rhomboidal pyramid. The Washington Monument, this figure, is a modern example of an obelisk of enormous size, which can be defined, (relative to this figure) with a truncated pyramid \(2l=16.8\) meters wide on the bottom and \(2l_1=10.5\) meters wide on top, a total height of \(169.3\) meters and an upper pyramid top height of \(h_1=16.9\) meters.
Define the obelisk function that given the base centre point, base width, base height, top width and total height, creates an obelisk.
3.10.3.2 Question 59
A perfect obelisk follows a set of proportions in which its height is equal to the pyramid’s base width, which in turn is onetenth of the total height. The top width has to be two thirds of the base width. With these proportions in mind, define the perfectobelisk function, that given a base centre point and the total height, creates a perfect obelisk.
3.10.3.3 Question 60
Using the regularpyramidfrustum function, define a function called prism that creates a regular prismatic solid. The function should receive as parameters the number of sides, the threedimensional coordinates of the base’s centre point, the distance between that centre point to each vertices, the base rotation angle and the threedimensional coordinates of the top’s centre point. As examples consider the following expressions:
(prism 3 (xyz 0 0 0) 0.4 0 (xyz 0 0 5)) (prism 5 (xyz 2 0 0) 0.4 0 (xyz 1 1 5)) (prism 4 (xyz 0 2 0) 0.4 0 (xyz 1 1 5)) (prism 6 (xyz 2 0 0) 0.4 0 (xyz 1 1 5)) (prism 7 (xyz 0 2 0) 0.4 0 (xyz 1 1 5))
3.10.3.4 Question 61
The Sears Tower shown in This figure (today called Willis Tower was for many years the tallest building in the world. This tower consists of nine square prisms, with different heights \(h_{i,j}\) connected to each other.
The Sears Tower, em Chicago.
From a top view these nine blocks define a square with a side \(l\), as shown in the following sketch:
Using the function prism defined in the previous exercise, define a function called searstower capable of creating buildings similar to the Sears Tower. The function should have as parameters the threedimensional coordinates of the base corner \(P\), the base width \(l\) and nine more parameters relative to each height value \(h_{0,0}, h_{0,1},\ldots,h_{2,2}\).
The real Sears Tower has the following parameters: \(l=68.7 m\), \(h_{0,1}=h_{1,1}=442 m\), \(h_{1,0}=h_{2,1}=h_{1,2}=368 m\), \(h_{0,0}=h_{2,2}=270 m\) and \(h_{0,2}=h_{2,0}=205 m\) as shown in the following image:
Besides the already presented geometrical primitives there is another that allows the creation of cuboid solids, i.e., solids with six faces but with a shape that is not necessarily cubic. A cuboid is defined by its eight vertices divided into two sets of four, respectively the base and top vertices (in counterclockwise order. The following expressions produce the three cuboids presented in this figure:
Three cuboid solids with different vertexes
(cuboid (xyz 0 0 0) (xyz 2 0 0) (xyz 2 2 0) (xyz 0 2 0) (xyz 0 0 2) (xyz 2 0 2) (xyz 2 2 2) (xyz 0 2 2)) (cuboid (xyz 4 0 0) (xyz 5 0 0) (xyz 5 2 0) (xyz 4 2 0) (xyz 3 1 2) (xyz 5 1 2) (xyz 5 2 2) (xyz 3 2 2)) (cuboid (xyz 7 2 0) (xyz 8 0 0) (xyz 8 3 0) (xyz 6 3 0) (xyz 7 2 2) (xyz 8 0 2) (xyz 8 3 2) (xyz 6 3 2))
The John Hancock Center, in Chicaco. %Fotografia de User:Cacophony
The John Hancock Center, in this figure, is a good example of a building with a geometry that can be modelled with a cuboid. In fact this building has a truncated shape with regular base and top. To model it we can can start by defining the regularcuboidgeometry function parametrized by the base centre point \(P\), the base length \(c_b\) and width \(l_b\), the top base length \(c_t\) and width \(l_t\) and finally by the height \(h\). For creating the solid the cuboid function becomes particularly useful, leaving us only with determining the position each vertex relative to the base point \(P\).
(define (regularcuboidgeometry p cb lb ct lt h) (cuboid (+xyz p (/ cb 2) (/ lb 2) 0) (+xyz p (/ cb 2) (/ lb 2) 0) (+xyz p (/ cb 2) (/ lb 2) 0) (+xyz p (/ cb 2) (/ lb 2) 0) (+xyz p (/ ct 2) (/ lt 2) h) (+xyz p (/ ct 2) (/ lt 2) h) (+xyz p (/ ct 2) (/ lt 2) h) (+xyz p (/ ct 2) (/ lt 2) h)))
Using this function, it becomes trivial to create buildings inspired by the shape of the John Hancock Center, as presented in this figure.
Buildings inspired by John Hancock Center’s cuboid geometry.
Pylons of the Karnak Temple. Illustration by Albert Henry Payne.
3.10.3.5 Question 62
A pylon is a distinctive Egyptian architectural element, illustrated in this figure. It is a monumental gateway enclosed by two identical tapering towers on both sides. Each tower is cuboid shaped, with a rectangular base and top and the remaining sides as trapeziums. Define a conveniently parametrized function capable of creating a simplified version of a pylon, similar to the one shown in the following image:
3.11 Cylindrical Coordinates
We have seen in previous sections some examples of the use of rectangular and polar coordinate systems. It also became clear that a rightful choice of coordinate system can simplify a geometric problem greatly.
For threedimensional modelling, besides the rectangular and polar, it is also common to use two other coordinate systems: cylindrical coordinates and spherical coordinates.
As we can see in figure this figure, a point in cylindrical coordinates is defined by a radius \(\rho\) radius on the \(z=0\) plan, an angle \(\phi\) with the \(x\) axis and a height \(z\). It is easy to see that the radius and the angle match the polar coordinates of a point’s projection on the \(z\) plan.
Cylindrical coordinates.
From this figure we can easily see that given a point \((\rho, \phi, z)\) in cylindrical coordinates, that same point in rectangular coordinates would be \[(\rho \cos \phi, \rho \sin \phi, z)\].
Likewise, a point in rectangular coordinates \((x,y,z)\) would be represented in in cylindrical coordinates as \[(\sqrt{x^2+y^2},\arctan\frac{y}{x}, z)\]
These equivalences are assured by the constructor of cylindrical coordinates cyl. Although this function is predefined in Rosetta it is not difficult it is defined as
(define (cyl ro fi z) (xyz (* ro (cos fi)) (* ro (sin fi)) z))
If we simply want to add to a point a translation in cylindrical coordinates, we can use the +cyl function, which makes use of the +xyz function:
(define (+cyl p ro fi z) (+xyz p (* ro (cos fi)) (* ro (sin fi)) z))
3.11.1 Exercises 19
3.11.1.1 Question 63
Define the selectors cylrho, cylphi and cylz which return, respectively, the components \(\rho\), \(\phi\) and \(z\) of a point built with the constructor cyl.
3.11.1.2 Question 64
Define a function stairs capable of building a helix staircase. The following image shows three different examples of helix staircases:
It can be seen that a staircase is formed by a central cylinder onto which \(10\) cylindrical steps are connected. For easier testing consider the central cylinder as a radius of \(r\). Each step is equal to the central cylinder, they measure \(10\) times the centre pole radius and are placed at incremental heights. Each is distanced \(h\) from the previous one and, seen from a top view, makes with it an angle \(\alpha\).
The function stairs should have as parameters the coordinates of the central cylinder’s base, the radius \(r\), the height \(h\) and the the angle \(\alpha\). As an example, consider that the stairs in the previous figure were created with the following expressions:
(stairs (xyz 0 0 0) 1.0 3 (/ pi 6)) (stairs (xyz 0 40 0) 1.5 5 (/ pi 9)) (stairs (xyz 0 80 0) 0.5 6 (/ pi 8))
3.12 Spherical Coordinates
Colatitude is the complementary angle to the latitude, i.e., the the difference between the pole (\(\frac{\pi}{2}\)) and the latitude.
Spherical Coordinates
Given a point \((\rho, \phi, \psi)\), that same point in spherical coordinates is \[(\rho \sin \psi \cos \phi, \rho \sin \psi \sin \phi, \rho \cos \psi)\]
Likewise a point \((x,y,z)\) in Cartesian coordinates, that same point in spherical coordinates is \[(\sqrt{x^2+y^2+z^2},\arctan\frac{y}{x}, \arctan\frac{\sqrt{x^2+y^2}}{z})\]
As it happens with cylindrical coordinates, the constructors of spherical coordinates sph and +sph are already predefined but it is not difficult to deduce their definitions:
(define (sph ro fi psi) (xyz (* ro (sin psi) (cos fi)) (* ro (sin psi) (sin fi)) (* ro (cos psi)))) (define (+sph p ro fi psi) (+xyz p (* ro (sin psi) (cos fi)) (* ro (sin psi) (sin fi)) (* ro (cos psi))))
3.12.1 Exercises 20
3.12.1.1 Question 65
Define the selectors sphrho, sphphi and sphpsi which return, respectively, the components \(\rho\), \(\phi\) and \(\psi\) of a point built with the constructor sph.
3.12.1.2 Question 66
The Mohawk hairstyle was widely used during the punk period. It is defined by styling the hair in the shape of a crest, as shown below:
Define the function mohawk, with parameters \(P\), \(r\), \(c\), \(\phi\) and \(\Delta_\psi\), which creates 9 cones of length \(c\) and base radius \(r\), all centred at point \(P\), leaning with an angle \(\Delta_\psi\) between them and placed along a plane with an angle \(\phi\) with the \(XZ\) plane.
An example is presented below as a result of the following expressions:
(mohawk (xyz 0 0 0) 1.0 10 (/ pi 2) (/ pi 6)) (mohawk (xyz 0 15 0) 0.5 15 (/ pi 3) (/ pi 9)) (mohawk (xyz 0 30 0) 1.5 6 (/ pi 4) (/ pi 8))
3.13 Modelling Doric Columns
The threedimensional modelling has the virtue of allowing us to create geometrical entities that are more realistic than mere agglomerates of lines representing views of that entity. As an example, reconsider the Doric column we introduced back in Doric Order. In that section we developed a series of functions capable of creating a front view of each of that column’s components. Even though those views are useful it is even more useful to model a column directly as a threedimensional entity.
In this section we are going to employ some of the most relevant operations for threedimensional modelling of columns, in particular truncated cones for shaping the shaft and the Echinus and rectangular box to shape the Abacus.
Before, our "columns" were laid out in the \(XY\) axis with them "growing" along the \(Y\) axis. Now, only the column’s base will be set on the \(XY\) plane: the column’s body will grow along the \(Z\) axis. Although it would be trivial to employ a different arrangement of axes, this is the one closest to reality.
Similarly to many other functions in Rosetta, each of the operations to model solids has different ways of being called upon. For the case of modelling truncated cones  conefrustum  the method to us more convenient is that one that receives the base centre coordinates, the base radius, height and finally the top radius.
With this in mind we can redefine the operation for creating the column’s shaft:
(define (shaft p hshaft rbase rtop) (conefrustum p rbase hshaft rtop))
Likewise, the operation for creating the Echinus will become:
(define (echinus p hechinus rbase rtop) (conefrustum p rbase aechinus rtop))
Finally, to build the Abacus  the rectangular box at the column’s top  we have many ways of specifying it. One way is to specify the two corners of this box. The other is to specify one of these corners followed by the box’s dimensions. For this example we will employ the second alternative.
(define (abacus p habacus labacus) (box (+xyz p (/ labacus 2) (/ labacus 2) 0) labacus labacus habacus))
3.13.1 Exercises 21
3.13.1.1 Question 67
Implement the abacus function but using the other option for creating a rectangular box whit the two corners.
Finally all there is left is to implement the column function that, similar to what happened in the bidimensional case, successively invokes the functions shaft, Echinus and abacus but now progressively increasing the \(Z\) coordinate:
(define (column p hshaft rbaseshaft hechinus rbaseechinus habacus labacus) (shaft p hshaft rbaseshaft rbaseechinus) (echinus (+z p hshaft) aechinus rbaseechinus (/ labacus 2)) (abacus (+z p (+ hshaft hechinus)) habacus labacus))
With these redefinitions we can now repeat the columns drawn in section Doric Order, and shown in this figure, but now creating a threedimensional image as presented in this figure:
(column (xyz 0 0 0) 9 0.5 0.4 0.3 0.3 1.0) (column (xyz 3 0 0) 7 0.5 0.4 0.6 0.6 1.6) (column (xyz 6 0 0) 9 0.7 0.5 0.3 0.2 1.2) (column (xyz 9 0 0) 8 0.4 0.3 0.2 0.3 1.0) (column (xyz 12 0 0) 5 0.5 0.4 0.3 0.1 1.0) (column (xyz 15 0 0) 6 0.8 0.3 0.2 0.4 1.4)
Multiple threedimensional columns.
3.14 Vitruvian Proportions
The method for modelling Doric columns that we developed in the previous section allows us to easily build columns, for which we need only indicate the values for the relevant parameters, such as the shaft’s height and base radius, the Echinus’ height and base radius and the Abacus’ height and width. Each of these parameters represents a degree of freedom that we can freely vary.
Even though it is logic to think that the more degrees of freedom we have the more flexible the modelling is, the truth is an excessive number of parameters will often lead to unrealistic models. That phenomenon can be seen in this figure where we show a a set of columns with randomly chosen parameters.
Threedimensional columns with randomly chosen parameters. Only one of these columns obeys the canon of the Doric order.
Vitruvius was a Roman writer, architect and engineer during the 1st century b.C., and the author of the only architectural treatise that has survived from Ancient times.
Because they wanted to raise a temple with columns but did not know the adequate proportions, [...], they measured a man’s foot and saw that is was one sixth of his height, so they gave the column a similar proportion, i.e., they made its height, including the capital, six times the column’s width, measured from the base. Thus the Doric order obtained its proportion and its beauty from the male human figure.
Vitruvius characterizes the Doric order in terms of modules:
The columns’ width, at the base, shall be two modules and their height, including the capitals, shall be fourteen.
From this we deduce that a module is equal to the column’s base radius and that the column’s height should be 14 times that radius. In other terms, the column’s base radius should be \(\frac{1}{14}\) the column’s height.
The capital’s height shall be one module and its width two and one sixth modules.
This implies that the Echinus’ height added to the Abacus’ height shall be one module, that is, the column’s base radius and the Abacus’ width shall be \(2\frac{1}{6}\) modules or \(\frac{13}{6}\) of the radius. Together with the fact that the column is 14 modules high that implies that the shaft’s height shall be 13 times the radius.
Let the capital’s height be divided into three parts, of which one will form the abacus with its cymatium, the second the Echinus with its annulets, and the third the neck.
This means that the abacus has the height of one third of a module, that is \(\frac{1}{3}\) of the base radius and the Echinus will have the remaining two thirds, which means \(\frac{2}{3}\) of the base radius.
These considerations lead us to determine the values of some of the parameters to draw a column, in terms of the shaft’s base radius. In terms of implementing this, that means that the function’s parameters will now be local variables whose value is attributed by applying the proportions established by Vitruvius to the the parameter rbaseshaft. The function is thus defined:
(define (doriccolumn p rbaseshaft rbaseechinus) (define hshaft (* 13 rbaseshaft)) (define hechinus (* 2/3 rbaseshaft)) (define habacus (* 1/3 rbaseshaft)) (define labacus (* 13/6 rbaseshaft)) (shaft p hshaft rbaseshaft rbaseechinus) (echinus (+z p hshaft) hechinus rbaseechinus (/ labacus 2)) (abacus (+z p (+ hshaft hechinus)) habacus labacus))
or, alternatively:
(define (doriccolumn p rbaseshaft rbaseechinus) (let ((hshaft (* 13 rbaseshaft)) (hechinus (* 2/3 rbaseshaft)) (habacus (* 1/3 rbaseshaft)) (labacus (* 13/6 rbaseshaft))) (shaft p hshaft rbaseshaft rbaseechinus) (echinus (+z p hshaft) hechinus rbaseechinus (/ labacus 2)) (abacus (+z p (+ hshaft aechinus)) habacus labacus)))
Using this function it is now possible to create columns closer to the Doric proportions (as set by Vitruvius). this figure shows the result of evaluating the following expressions:
(doriccolumn (xyz 0 0 0) 0.3 0.2) (doriccolumn (xyz 3 0 0) 0.5 0.3) (doriccolumn (xyz 6 0 0) 0.4 0.2) (doriccolumn (xyz 9 0 0) 0.5 0.4) (doriccolumn (xyz 12 0 0) 0.5 0.5) (doriccolumn (xyz 15 0 0) 0.4 0.7)
Variations of Doric columns according to Vitruvius’ proportions.
Vitruvius’ proportions allowed us to reduce the number of independent parameters for a Doric column to only two: the shaft’s base radius and Echinus’ base radius. However, it does not seem right for these parameters to be completely independent since that allows bizarre columns to be constructed, where the shaft’s top is larger than the base, as it happens in the rightmost column in this figure.
In truth, the characterization of the Doric Order that we presented is incomplete since, for the column’s proportions, Vitruvius also added:
The reduction at the top of a column seems to be regulated according to the following principles: if a column is less than fifteen feet, let the base width be divided into six parts and use five of those and let five of those parts be used to form the top’s width. If the column has between fifteen and twenty feet, let base width be divided into six and a half parts, and let five and a half of those parts be used for the upper width of the column. If a column has between twenty to thirty feet let the base width be divided into seven parts and let the reduced top measure six of them. A column of thirty to forty feet should be divided at the base into seven and a half parts, and, at the beginning of the reduction, it should have six and a half of these parts at the top. Columns of forty to fifty feet should be divided into eight parts, and reduced to seven of those at the top of the column under the capital. In the case of higher columns, let the reduction be determined proportionally, on the same principles. (In Vitruvius, The Ten Books on Architecture, III book, Chapter 3.1)
A Foot was the fundamental unit of measurement during for several centuries but its value has changed along the years. The measurement of the international foot is \(304.8\) millimetres and was established in 1958. Before that, many other measurements were used, as the Doric foot of \(324\), the Roman and Ionic feet of \(296\) millimetres, the Athenian foot of \(315\) millimetres, the Egyptian and Phoenician feet of \(300\) millimetres, etc.
Let us then consider a function, which we will call shafttopradius, that receives as parameters the column’s base width and the column’s height and returns as the result the width for the column’s top width.
A literal translation of Vitruvius’ considerations allows us to write:
(define (shafttopradius baseradius height) (cond ((< height 15) (* (/ 5.0 6.0) baseradius)) ...))
The previous fragment obviously corresponds to the statement: "if a column is less than fifteen feet, let the base width be into six parts and use five of those and let five of those parts be used to form the top’s width." In case the column has less than fifteen feet we skip to the next statement: "If the column has between fifteen and twenty feet, let base width be divided into six and a half parts, and let five and a half of those parts be used for the upper width of the column". Translating this last statement we have:
(define (shafttopradius baseradius height) (cond ((< height 15) (* (/ 5.0 6.0) baseradius)) ((and (>= height 15) (< height 20)) (* (/ 5.5 6.5) baseradius)) ...))
A careful analysis of the two previous statements shows that, in reality, we are making excessive tests on the second clause. In fact, if we can get to the second clause that means the first one is false, i.e., a height is not less than 15 and, therefore, it is higher or equal to 15. In that case it is useless to test if the height is higher of equal to 15 again. That way we can simplify the function and write instead:
(define (shafttopradius baseradius height) (cond ((< height 15) (* (/ 5.0 6.0) baseradius)) ((< height 20) (* (/ 5.5 6.5) baseradius)) ...))
Moving on with the translation, leads us to:
(define (shafttopradius baseradius height) (cond ((< height 15) (* (/ 5.0 6.0) baseradius)) ((< height 20) (* (/ 5.5 6.5) baseradius)) ((< height 30) (* (/ 6.0 7.0) baseradius)) ((< height 40) (* (/ 6.5 7.5) baseradius)) ((< height 50) (* (/ 7.0 8.0) baseradius)) ...))
The problem now is that Vitruvius as let the door open to arbitrarily high columns, simply saying: "In the case of higher columns, let the reduction be determined proportionally, on the same principles". To clearly understand the principles we speak of let us consider the evolution of the relation between the top and base of the columns which is visible on the side image.
The ration between the column’s top radius and the base radius is as shown in the side image (something which was also already clear in the shafttopradius function), a sequence like
\[\frac{5}{6},\, \frac{5\frac{1}{2}}{6\frac{1}{2}},\, \frac{6}{7},\, \frac{6\frac{1}{2}}{7\frac{1}{2}},\, \frac{7}{8},\, \cdots{}\]
It now becomes obvious that, for higher columns, "the same principles" Vitruvius speaks of come down to, for each \(10\) additional feet, adding \(\frac{1}{2}\) to both the numerator and the denominator. However, it is important to notice that this principle should only be applied if the height exceeds \(15\) feet, since the first interval is bigger than the other ones. Thus, we have to handle columns up to \(15\) feet differently and, from there onward, simply subtract \(20\) feet from the height and determine integer division by \(10\) in order to know how many times we need to add \(\frac{1}{2}\) to both the numerator and the denominator of \(\frac{6}{7}\).
It is the "handle differently" for one case and the other which suggests the need to come up with a selection mechanism: it is necessary to distinguish between two cases and react to each accordingly. For Vitruvius’ column, if the column has a height \(a\) up to \(15\) feet, the ration between the top and the base is \(r=\frac{5}{6}\); if the height \(a\) is not less than \(15\) feet, the ration between the top and the base shall be:
\[r=\frac{6 + \lfloor\frac{a20}{10}\rfloor\cdot\frac{1}{2}}{7 + \lfloor\frac{a20}{10}\rfloor\cdot\frac{1}{2}}\]
As an example, let us consider a column with \(43\) feet. The integer division of \(4320\) by \(10\) is \(2\) so we must add \(2\cdot{}\frac{1}{2}=1\) to the numerator and \(\frac{6}{7}\) to the denominator, and we will get \(\frac{7}{8}=0.875\).
As as for a second example let us consider the proposal made by Adolf Loos for the headquarters of the Chicago Tribune journal, a \(122\) metertall building with the shape of a doric column on top of a large base. The column alone would be \(85\) meters hight. Taking into account that a foot, in the Doric Order, measured \(324\) millimetres, the column would have \(85/0.324\approx 262\) feet. The integer division of \(26220\) by \(10\) is \(24\). So the ratio between the top and the base of this hypothetical would then be \(\frac{6+24/2}{7+24/2}=\frac{18}{19}=0.95\). Because the value is close to the unit it shows that the column would be practically cylindrical.
Based on these considerations we can now define a function that, given an integer representing the column’s height in feet, computes the ration between the top and the base. Beforehand, however, it is convenient to simplify the formula for columns with heights not inferior to \(15\) feet. So:
\[r=\frac{6 + \lfloor\frac{a20}{10}\rfloor\cdot\frac{1}{2}}{7 + \lfloor\frac{a20}{10}\rfloor\cdot\frac{1}{2}}= \frac{12 + \lfloor\frac{a20}{10}\rfloor}{14 + \lfloor\frac{a20}{10}\rfloor}= \frac{12 + \lfloor\frac{a}{10}\rfloor  2}{14 + \lfloor\frac{a}{10}\rfloor  2}= \frac{10 + \lfloor\frac{a}{10}\rfloor}{12 + \lfloor\frac{a}{10}\rfloor}\]
The function’s definition would then be:
(define (shafttopradius baseradius height) (if (< height 15) (* 5/6 baseradius) (let ((divisions (quotient height 10))) (* (/ (+ 10 divisions) (+ 12 divisions)) baseradius))))
This was the last expression that was missing in order to completely specify the drawing of a Doric column according to Vitruvius in his architectural treatise. Let us consider that we will supply the coordinates for the column’s base centre point and its height. All remaining parameters will be calculated in terms of these ones. The definition will be:
(define (doriccolumn p height) (define rbaseshaft (/ height 14)) (define rbaseechinus (shafttopradius rbaseshaft height)) (define ashaft (* 13 rbaseshaft)) (define aechinus (* 2/3 rbaseshaft)) (define aabacus (* 1/3 rbaseshaft)) (define labacus (* 13/6 rbaseshaft)) (shaft p ashaft rbaseshaft rbaseechinus) (echinus (+z p ashaft) aechinus rbaseechinus (/ labacus 2)) (abacus (+z p (+ ashaft aechinus)) aabacus labacus))
Note that the column’s height should be given specified in feet.
(doriccolumn (xy 0 0) 10) (doriccolumn (xy 10 0) 15) (doriccolumn (xy 20 0) 20) (doriccolumn (xy 30 0) 25) (doriccolumn (xy 40 0) 30) (doriccolumn (xy 50 0) 35)
Column variations according to Vitruvius proportions.
Finally it is worth mentioning that the functions column and doriccolumn represent two extreme cases: the first one models a column with many degrees of freedom, from the position to the measurements of the shaft, echinus and abacus, whereas the second only allows the position and height to be given. The function doriccolumn is in fact a particular case of function column so it can be defined in terms of it:
(define (doriccolumn p height) (define rbaseshaft (/ height 14)) (define rbaseechinus (shafttopradius rbaseshaft height)) (define ashaft (* 13 rbaseshaft)) (define aechinus (* 2/3 rbaseshaft)) (define aabacus (* 1/3 rbaseshaft)) (define labacus (* 13/6 rbaseshaft)) (column p ashaft rbaseshaft aechinus rbaseechinus aabacus labacus))
The functions column and doriccolumn are also a good example of a modelling strategy. Whenever possible, we should begin by defining the most general and unconstrained case, contemplating the highest number of degrees of freedom that are reasonable, and only then should we consider the particular cases, modelled with specific functions but which can naturally resort to the definition of the general case.
3.14.1 Exercises 22
3.14.1.1 Question 68
A careful look at the shafttopradius function shows there is a repeated fragment of code, namely the multiplication by the baseradius parameter. This suggests that it should be possible to come up with an even more compact version of this function. Define it.
4 Recursion
4.1 Introduction
We have seen previously that our functions, in order to do something useful, must call other functions. For example, if we already have the function that computes the square of a number and if we want to define the function that computes the cube of a number, we can easily do it by using the square and an additional multiplication, i.e.:
(define (cube x) (* (square x) x))
Similarly, we can define the function fourthpower in terms of the cube function and an additional multiplication:
(define fourthpower (x) (* (cube x) x))
Obviously, we can continue to define new functions to compute larger powers but this is a lengthy process and, moreover, it will always be limited. It would be much more useful if we were able to generalize this process and simply define the exponentiation function which, from two numbers (base and exponent) computes the first one raised to the power of the second one.
However, what we did for the fourthpower, the cube and the square gives us an important clue: if we have a function that computes the exponentiation with the immediately lower exponent, then we only need one additional multiplication to compute the exponentiation with the next exponent.
In other words, we have:
(define (power x n) (* (inferiorpower x n) x))
Although we were able to simplify our power calculation problem, there is still one unanswered question: how can we calculate the power immediately below? The answer may not be obvious but once understood, it is trivial: the exponentiation immediately lower to the exponentiation of exponent n is the exponentiation to the power of \(n1\). That implies that (inferiorpower x n) is exactly the same as (power x ( n 1)). Based on this idea, we can rewrite the previous definition:
(define (power x n) (* (power x ( n 1)) x))
In spite of our ingenious solution this definition has a problem: regardless of the exponentiation we try to compute, we will never be able to get the a final result. In order to understand this problem it is simpler to consider a real case: let us try to calculate the third power of the number \(4\), i.e., (power 4 3).
For this, according to the power function definition, we will need to evaluate the following expression:
\(\downarrow\)\(\downarrow\)\(\downarrow\)\(\downarrow\)\(\downarrow\)
It is obvious that this process will never finish. The problem is due to the fact that we reduced the power calculation of a number raised to an exponent to the power calculation of this number raised to an exponent immediately below it, but we have not said in which situation we already have a simple enough exponent to which the solution is immediate. Which is the situation where this happens? We have seen that when the exponent is \(2\) the square function returns the correct answer so the case \(n = 2\) is sufficiently simple. However, it is possible to have an even simpler case: when the exponent is \(1\), the result is simply the base value. Finally, the simplest case of all: when the exponent is \(0\), the result is \(1\), regardless of the base value. This last case is easy to understand when we see that the evaluation of (power 4 2) (i.e., the forth power of four) is reduced to (* (* (power 4 0) 4) 4). For this expression to be similar to (* 4 4) it is necessary that the evalauation of (power 4 0) produces 1.
We are now capable of defining the power function correctly:
When the exponent is \(0\) the result is \(1\);
Otherwise, we calculate the power with the exponent immediately before and we multiply it to the base.
(define (power x n) (if (zero? n) 1 (* (power x ( n 1)) x)))
The previous example is an example of a recursive function, i.e., it is a function which is defined in terms of itself. In other words, a recursive function is a function that calls itself inside its own definition. This use is obvious when we "unwrap" the evaluation process for the (power 4 3):
(power 4 3)\(\downarrow\)\(\downarrow\)\(\downarrow\)\(\downarrow\)\(\downarrow\)\(\downarrow\)\(\downarrow\)64
The recursion is the mechanism that allows a function to call upon itself during its own evaluation process. Recursion is one of the most important programming tools, so it is important we understand it well. Many apparently complex problems usually have surprisingly simple recursive solutions.
There are countless examples of recursive functions. One of the simplest is the factorial function that is defined mathematically as:
\[n!= \begin{cases} 1, & \text{if $n=0$}\\ n \cdot (n1)!, & \text{otherwise} \end{cases}\]
The translation of this formula into Racket is straightforward:
(define (factorial n) (if (zero? n) 1 (* n (factorial ( n 1)))))
It is important to notice that for all recursive functions there is:
The basic case (also called stopping criterion) to which the result is immediately known.
A non basic case (also called recursive case) in which an original problem is decomposed into a simpler subproblem.
If we analyse the factorial function, the stopping criterion is the equality test to zero (zero? n), the immediate result is 1, and the recursive case is obviously (* n (factorial ( n 1))).
Usually, a recursive function is only correct if it has a conditional statement which identifies the basic case but this needs not be mandatory. Invoking a recursive function consists of successively solving simpler subproblems until the simplest case of all is reached, for which the result is immediate. This way, the most common pattern to write a recursive function is:
Start by testing the basic cases.
Make a recursive call with a subproblem increasingly closer to a basic case.
Use the result of the recursive call to produce the result of the original call.
Given this pattern, the most common errors associated with recursive functions are:
Not detecting the basic case.
Recursion not reducing the complexity of the initial problem, i.e., not moving on to a simpler problem.
Not properly using the result of the recursion to produce the originally intended result.
Note that a recursive function that works perfectly for the cases for which it was created can be completely wrong for other cases. The factorial function is an example: when the argument is negative, the problem’s complexity increases and becomes further away from the basic case:
(factorial 1)\(\downarrow\)\(\downarrow\)\(\downarrow\)\(\downarrow\)\(\downarrow\)\(\downarrow\)
The most frequent error in a recursive function is when it never stops, either because the basic case in not correctly detected or, because the recursion does not decrease the problem’s complexity. In this case, the number of recursive calls grows indefinitely until the computer’s memory is exhausted. At this point, the program generates an error message. In Racket’s case this error is not entirely obvious because the evaluator only interrupts the evaluation, showing no results. Here is an example:
> (factorial 3) 6 > (factorial 1) ERROR
It is very important to correctly understand the concept of recursion. Although, at first, it may be difficult to embrace fully its implications, recursion allows solving with great simplicity, apparently very complex problems.
4.1.1 Exercises 23
4.1.1.1 Question 69
The Ackermann function is set to nonnegative numbers as follows: \[A(m, n) = \begin{cases} n+1 & \text{if $m = 0$} \\ A(m1, 1) & \text{if $m > 0$ and $n = 0$} \\ A(m1, A(m, n1)) & \text{if $m > 0$ and $n > 0$}\end{cases}\] Define the Ackermann function in Racket.
4.1.1.2 Question 70
(ackermann 0 8)
(ackermann 1 8)
(ackermann 2 8)
(ackermann 3 8)
(ackermann 4 8)
4.2 Recursion in Architecture
As we will see, in architecture recursion is also a fundamental concept. As an example, let us consider a ladder profile, as outlined in this figure and let us imagine that we intend to define a function called ladder that, given the point \(P\), the length \(c\) of the thread and the \(e\) height of each riser, and finally the number of steps \(n\), creates the stairs with the first riser starting at \(P\). Given these parameters, the definition of the function should start as:
(define (ladder p c e n) ...)
Profile of a ladder with \(n\) steps, with the fist step starting at the point \(P\), each step containing a thread \(c\) and a riser \(e\).
To implement this function we have to be able to decompose the problem in less complex sub problems and this is where recursion provides a great help: it allows us to decompose the drawing of a ladder with \(n\) steps into the drawing of a step followed by the drawing of a ladder with \(n1\) steps, as presented in the diagram of this figure.
Decomposition of the design of a ladder of \(n\) steps into the design of a ladder of \(n1\) steps. steps.
This means the function will be something like:
(define (ladder p c e n) ... (step p c e) (ladder (+xy p c e) c e ( n 1)))
To draw a step we can define the following function that creates the segments for the thread and riser:
(define (step p c e) (line p (+y p e) (+xy p c e)))
The problem now is that the ladder function needs to stop creating steps at some point. It is easy that when successively reducing the number of steps, that moment comes when we reach a point where that number is zero. Thus, when asked to draw a ladder with zero steps, the ladder function no longer needs to do anything. This means that the function should have the following form:
(define (ladder p c e n) (if (= n 0) ... (begin (step c e) (ladder (+xy p c e) c e ( n 1)))))
What is left to decide is what does the function produces as output when it reaches the stopping condition. As, in this case, what interests us is the side effect resulting from invoking the function, it is less relevant what it produces as a result so we will stipulate that it will produce #t to indicate all worked out well:
(define (ladder p c e n) (if (= n 0) #t (begin (step p c e) (ladder (+xy p c e) c e ( n 1)))))
To see a more interesting example of recursion in Architecture let us consider the Saqqara Pyramid, illustrated in this figure, built by the architect Imhotep in XXVII century b.C.. This step pyramid is considered to be the first pyramid in Egypt and the oldest monumental stone construction in the world, being composed of six progressively smaller mastabas, stacked on top of each other. Another way of looking at this step pyramid is a mastaba on top of which stands another smaller step pyramid.
The Saqqara steps Pyramid. Photography by Charles J. Sharp.
Formally, we can define a pyramid of \(n\) steps as a mastaba on top of which stands a pyramid of \(n1\) steps. To complete this definition it must be said that when the last mastababa is created, the pyramid of \(0\) steps at the top is, actually, nonexistent.
Thus, considering the illustration in this figure, if we consider that the centre of the base of the pyramid is the \(p\) position and the mastabas are several pyramid frustums, we can write:
Schematic step pyramid.
(define (steppyramid p b t h d n) (if (= n 0) #t (begin (regularpyramidfrustum 4 p b 0 h t) (steppyramid (+z p h) ( b d) ( t d) h d ( n 1)))))
An approximate example of the pyramid of Saqqara would then be:
(steppyramid (xyz 0 0 0) 120 115 20 15 6)
4.2.1 Exercises 24
4.2.1.1 Question 71
The above definition does not accurately reflect the geometry of the step pyramid of Saqqara because it has sloped surfaces between each mastaba, as can be seen in the following diagram where we compare the sections of the pyramid we defined (left) and to the actual pyramid of Saqqara (right):
Define a more rigorous version of the steppyramid function which receives, in addition to the above parameters, the height of each slope. Experiment with different parameters for you to produce a model similar to the following one:
4.2.1.2 Question 72
The false arch is the oldest form of arch, formed by parallelepipeds arranged horizontally like steps, forming an opening that narrows towards the top, ending with a horizontal beam, as shown in the following diagram:
Assuming that the parallelepipeds have a square section measuring \(l\), the opening reduction is \(\Delta_e\), equal in every step, and that the point \(p\) is the arc’s centre point, define the falsearc function that, with the parameters \(p\), \(c\), \(e\), \(\Delta_e\) and \(l\), creates a false arc.
4.2.1.3 Question 73
Define the balancedcircles function that allows you to create any of the following illustrations:
(balancedcircles (xy 0 0) 1.2 0.3) (balancedcircles (xy 3 0) 1.2 0.5) (balancedcircles (xy 6 0) 0.9 0.6) (balancedcircles (xy 9 0) 0.5 0.8)
Note that the circles have radius that are in a geometrical progression ratio of \(f\), with \(0 <f < 1\). That way, each circle (except the first one) has a radius that is the product of \(f\) by the radius of the largest circle in which it stands. The smallest circle has a radius that is greater or equal to \(1\). The function should have as parameters the centre point and the radius of the larger circle, and also the reduction factor \(f\).
4.2.1.4 Question 74
Define a radialcircles function that given the coordinates the rotation centre \(p\), the number of circles \(n\), the \(r_0\) translation radius, the circle radius \(r_1\), the initial angle \(\phi\) and the angle increment \(\Delta\phi\), draws the circles as shown in the previous figure.
Test your function with the following expressions:
(radialcircles (xy 0 0) 10 1.5 0.3 0 (/ pi 5)) (radialcircles (xy 4 0) 20 1.5 0.3 0 (/ pi 10)) (radialcircles (xy 8 0) 40 1.5 0.3 0 (/ pi 20))
whose evaluation should generate the following image:
4.2.1.5 Question 75
Consider the design of symbolic flowers composed of an inner circle around which radial circles are arranged corresponding to the petals. These circles should be tangent to each other and to the inner circle, as shown in the following image:
(flower (xy 0 0) 1 10)
Define a flower function that receives only the flower’s centre point, the radius of the inner circle and the number of petals.
Test your function with the following expressions:
(flower (x 0.0) 1.0 10) (flower (x 3.6) 0.4 10) (flower (x 8.0) 2.0 20)
Their evaluation should generate the following image:
4.3 Debugging Recursive Programs
There are some semantic errors that can be detected before the program’s execution, but this detection depends strongly on the quality of the language implementation and its ability to anticipate the program’s consequences.
> (factorial 5) 120 > (factorial "five") zero?: contract violation expected: number? given: "five"
Obviously this last error has nothing to do with Racket’s grammar rules: the "sentence" on how to call the factorial function is correct. The problem is that it does not make sense to calculate the factorial of a string, because calculating factorials involves arithmetic operations and these do not apply to strings. Therefore, the error is related to the meaning of the written "sentence", i.e., with the semantics. It is then, a semantic error.
An infinite recursion is another example of a semantic error. We saw that if the factorial function is called with a negative argument, infinite recursion occurs. Consequently, if we use a negative argument, we will be making a semantic error.
Racket provides several mechanisms for detecting errors. One of the simplest ones is the trace form (provided by the racket/trace module) that allows the visualization of a function’s invocations. The trace receives the name of the functions to be analysed and changes these functions so as to write the successive calls with the respective arguments, as well as the result of the its invocation. The information given as the trace result is generally extremely useful for debugging functions.
For example, to visualize the call of the factorial function, consider the following definition:
#lang racket 
(require racket/trace) 

(define (factorial n) 
(if (zero? n) 
1 
(* n (factorial ( n 1))))) 

(trace factorial) 
and the following call:
> (factorial 5) 
>(factorial 5) 
> (factorial 4) 
> >(factorial 3) 
> > (factorial 2) 
> > >(factorial 1) 
> > > (factorial 0) 
< < < 1 
< < <1 
< < 2 
< <6 
< 24 
<120 
120 
Note that, in the previous example as a consequence of trace, the call of the factorial function appears aligned to the left side. Each recursive call appears slightly to the right, allowing the display of the recursion’s "depth", i.e., the number of recursive calls. The result returned by each call appears aligned in the same column of that call.
4.3.1 Exercises 25
4.3.1.1 Question 76
Trace the power function. What is the resulting trace of the (power 2 10) evaluation?
4.3.1.2 Question 77
Define a circles function capable of creating the illustration presented below:
Note that, the circles have radius that are in geometrical progression ration of \(\frac{1}{2}\). In other words, the smaller circles have half the radius of the adjacent larger circle. The smallest circles of all have a radius greater or equal to \(0.1\). The function should only have as parameters the centre and radius of the larger circle.
4.3.1.3 Question 78
Define the saw function that, given a point \(P\), a number of teeth, the length \(c\) of each tooth and the height \(a\) of each tooth, draws a saw, with the first tooth starting at \(P\) as presented in the following image:
4.3.1.4 Question 79
Define the lozenges function capable of creating the illustration presented below:
(lozenges (xy 0 0) 1.6 0.1)
Note that the dimensions of the lozenges have a geometrical progression ration of \(\frac{1}{2}\). In other words, the smaller lozenges have half the size of the largest lozenges at the tips of which they are centred. The smallest lozenges have a width greater or equal to \(1\). This function should only have as parameters the centre and width of the largest lozenge.
4.3.1.5 Question 80
Consider the stair outlined in the figure below, designed to overcome an slope \(\alpha\).
Define the stairslope function that receives the point \(p\), the angle \(\alpha\), the length \(c\) and the number of steps \(n\) and builds the ladder described in the previous schema.
4.3.1.6 Question 81
Consider the ladder outlined in the figure below, designed to overcome a slope \(\alpha\).
Note that the steps’ dimensions have a geometric progression ration of \(f\), i.e., given a step with a length of \(c\), the step immediately above has a length of \(f \cdot c\). Define the geometricprogressionladder function that receives the point \(P\), the angle \(\alpha\), the length \(c\), the number of steps \(n\) and the ration \(f\) and creates the ladder described in the previous schema.
4.4 Doric Temples
With Vitruvius we have seen that the Greeks created an elaborate proportion system for columns. These columns were used to form porticos, wherein a succession of columns crowned with a roof served as the entrance for buildings and, in particular, for temples. When this arrangement of columns was projected from the building it was called prostyle, and it was classified according to the number of columns in its composition as Diastyle, Tristyle, Tetrastyle, Pentastyle, Hexastyle, etc. When the prostyle was extended to the whole building, placing columns all around it, it was called peristyle.
In addition to describing the proportions of columns, Vitruvius also explained in his famous treaty the rules that the construction of temples had to follow, in particular, regarding their orientation, which should be from east to west. and regarding the spacing between columns, distinguishing several cases of temples from those with a very reduced spacing (pycnostyle) to temples with excessively large columns spacing (araeostilo), including the style which he considered to have the best proportion (eustilo) in which the spacing between columns is variable, being larger in the central columns.
To simplify our implementation we will ignore these details and,instead of distinguishing each style, we will simply consider consider the placement of columns distributed linearly in a particular direction, as sketched in this figure.
Temple’s floor plan with an arbitrary orientation
To this moment, we have considered coordinates as mere positions in space. Now, in order to model this temple, it will be useful to adopt a vectorial perspective of the concept of coordinates. In this perspective, coordinates are seen as the tips of vectors positioned at the origin. This can be seen in this figure where we have marked the position of two columns with the vectors \(P\) and \(Q\).
With all vectors being positioned at the origin it becomes irrelevant to mention it, which allows us to characterize vectors as having only a magnitude and a direction. It is easy to see that this magnitude and direction are precisely the polar coordinates of the vector’s end, i.e., the distance from the origin to the tip of the vector and the angle that the vector makes with the \(X\) axis.
The great advantage of adopting the vectorial perspective is that it enables us to conceive an algebra for operating with vectors. For example, the sum of vector is a vector whose components are the sum of corresponding components, i.e., \[P + Q = (P_x + Q_x, P_y + Q_y, P_z + Q_z)\]
Similarly, we can define the subtraction of vectors as \[P  Q = (P_x  Q_x, P_y  Q_y, P_z  Q_z)\] and the multiplication of a vector with a scalar \(\alpha\): \[P\cdot\alpha=(P_x\cdot\alpha,P_y\cdot\alpha,P_z\cdot \alpha)\]
Finally, the division of a vector by a scalar can be defined in terms of the multiplication by the scalar’s inverse value, i.e., \[\frac{P}{\alpha} = P\frac{1}{\alpha}\]
These operations are presented in this figure for the bidimensional case.
Algebraic operations using vectors.
These operations are predefined in Racket.
(define (+c p0 p1) (xyz (+ (cx p0) (cx p1)) (+ (cy p0) (cy p1)) (+ (cz p0) (cz p1)))) (define (c p0 p1) (xyz ( (cx p0) (cx p1)) ( (cy p0) (cy p1)) ( (cz p0) (cz p1)))) (define (*c p a) (xyz (* (cx p) a) (* (cy p) a) (* (cz p) a))) (define (/c p a) (*c p (/ 1 a)))
4.4.1 Exercises 26
4.4.1.1 Question 82
A unit vector is a vector of unitary magnitude. Define the unitvector operation that given any vector calculates the unit vector with the same direction as the given one.
4.4.1.2 Question 83
The symmetric vector of a \(\vec{v}\) vector is the vector \(\vec{v}^\prime\), such that \(\vec{v}+\vec{v}^\prime=0\). In other words, is the vector with equal magnitude but opposite direction. Define the symmetricvector operation that given any vector calculates the symmetric vector.
Returning to the problem of positioning columns in the temple, illustrated in this figure. Given the orientation and separation vector \(\vec{v}\) of columns, from the position of any column \(P\), we determine the position of the next column through \(P+\vec{v}\). This reasoning allows us to first define a function to create a row of columns. This function will have as parameters the coordinates \(P\) of the base of the first column, the height \(h\) of the column, the vector \(\vec{v}\) separating the axes of the columns and also the number \(n\) of columns that we wish to create. The reasoning behind the definition of this function is, once more, recursive:
If the number of columns to create is zero, then there is nothing to be done, so we can simply return #t.
Otherwise, we place a column at the point \(P\), and recursively create the remaining columns based on the resulting point of adding the vector \(v\) to the point \(P\). As they are two actions that we wish to perform sequentially, we need to use the operator "begin" to group a joint action.
The translation of this process to Racket we have:
(define (doriccolumns p h v n) (if (= n 0) #t (begin (doriccolumn p h) (doriccolumns (+c p v) h v ( n 1)))))
We can test the creating of columns using, for example:
of which result is presented in this figure.
A perspective of a set of eight Doric columns with \(10\) units of height and \(5\) spacing units between columns along the \(x\) axis.
4.4.1.3 Question 84
Although the use of separation vectors between columns is relatively simple, it is possible to simplify that process even further by calculating the vector value based on the start and end points of the row of columns. Using the doriccolumns function, define a function called doriccolumnsbetween that given centre points \(P\) and \(Q\) of the first and final columns, the height \(h\) of the columns and finally the number of columns, creates a row of columns between those two points.
As an example, the following image shows the result of evaluating the following expressions:
(doriccolumnsbetween (pol 10 0.0) (pol 50 0.0) 8 6) (doriccolumnsbetween (pol 10 0.4) (pol 50 0.4) 8 6) (doriccolumnsbetween (pol 10 0.8) (pol 50 0.8) 8 6) (doriccolumnsbetween (pol 10 1.2) (pol 50 1.2) 8 6) (doriccolumnsbetween (pol 10 1.6) (pol 50 1.6) 8 6)
From the moment we know how to create rows of columns, it becomes easy to create the four necessary rows for building peristyle temples. Normally, the description of these temples is done in terms of the number of columns at the front and at the side, assuming that the columns oat the corners count for both rows. This means that, for example, a temple with \(6 \times 12\) columns there are actually only \(4 \times 2 + 10 \times 2 + 4 = 32\) columns. For creating the peristyle, besides the number of columns at the front and side, we need to know the position of the columns at the extremities of the temple and obviously the column’s height.
In terms of the algorithm, we start by creating one of the corners of peristyle:
(define (doricperistylecorner p height v0 n0 v1 n1) (doriccolumns p height v0 n0) (doriccolumns (+c p v1) height v1 ( n1 1)))
Note that, in order to avoid repeating columns, the second row must start at the second column and consequentially one less column must be placed.
To build the complete peristyle we only have to create one corner and then build another corner with one less column on each side, progressing in opposite directions.
(define (doricperistyle p height v0 n0 v1 n1) (doricperistylecorner p height v0 n0 v1 n1) (doricperistylecorner (+c p (+c (*c v0 ( n0 1)) (*c v1 ( n1 1)))) height (*c v0 1) ( n0 1) (*c v1 1) ( n1 1)))
A realistic example is the temple of Segesta, represented in this figure. This temple is of the peristyle type, composed of \(6\) columns (i.e., Hexastyle) at each front and \(14\) columns at the side, a total of \(36\) \(9\) meters high columns. The distance between the columns axes is approximately \(4.8\) meters at the front and \(4.6\) meters at the sides. The expression that creates the peristyle of this temple is then:
The result of evaluating the above expression is shown in this figure.
An overview of the peristyle of the temple of Segesta. The columns were generated by the peristyledoric function using as parameters, \(6\) columns on the front and \(14\) on the side, with a column distance of \(4.8\) meters on the front and \(4.6\) meters on the sides, with columns \(9\) meters high.
Although the vast majority of Greek temples were rectangular, circular temples were also built, called Tholos. The Sanctuary of Athena Pronaia at Delphi has a good example of one such buildings. Although little remains of this temple is not difficult to imagine its original shape based on what is still remains, shown in this figure.
The Temple of Pronaia in Delphi, built in IV century b.C.. Photography by Michelle Kelley.
To simplify the construction of the Tholos temple, we will divide it into two parts. In one of them we will create the base and on the second one we will position the columns.
For designing the base we can consider a set of flattened cylinders, stacked to form the circular steps, as shown in this figure. Thus, the total base height \(a_b\) will be divided in steps of \(\Delta a_b\) and the base radius will also be divided in \(\Delta r_b\) steps.
A Tholos base section. The base is composed by a sequence of stacked cylinders whose base radius \(R_B\) shrinks \(\Delta r_b\) each step and whose height grows in increments of \(\Delta a_b\) in every step.
For each cylinder we have to consider its radius and the dheight of each step. To draw the next cylinder we have also to consider the radius increment \(dradius\) due to the step’s length. These steps will be built using a recursive process:
If the number of steps to create is zero, nothing needs to be done.
Otherwise, we place a step (modelled with a cylinder) with the given radius and height and, recursively, we place the remaining steps on top of this one, i.e., at a height equal to the steps being placed and a radius reduced from the length of the steps being placed.
This process is implemented by the following function:
(define (basetholos p nsteps radius dheight dradius) (if (= nsteps 0) #t (begin (cylinder p radius dheight) (basetholos (+xyz p 0 0</