Introduction to Computing
Take Home Exam 3
Name: Roiz Alessander M. Hipolito Date: October 9, 2018
Course: BSCS 1-3 Reference: computingbook.org
I. Come up for a Research paper for the following topics below related to composition and
transformation of DATA:
a. DATA Types
A data type defines a set (often infinite) of possible values. The Boolean data type
contains the two Boolean values, true and false. The Number type includes the infinite
set of all whole numbers (it also includes negative numbers and rational numbers). We
think of the set of possible Numbers as infinite, even though on any particular computer
there is some limit to the amount of memory available, and hence, some largest number
that can be represented. On any real computer, the number of possible values of any data
type is always finite. But, we can imagine a computer large enough to represent any given
number. The type of a value determines what can be done with it. For example, a Number
can be used as one of the inputs to the primitive procedures +, *, and = . A Boolean can be
used as the first sub-expression of an if expression and as the input to the not procedure
(—not— can also take a Number as its input, but for all Number value inputs the output is
false), but cannot be used as the input to +, *, or = .1
b. Pairs (Making Pairs, Triples to Octuples)
The simplest structured data construct is a Pair. We draw a Pair as two boxes, Pair each
containing a value. We call each box of a Pair a cell. Here is a Pair where the first cell has
the value 37 and the second cell has the value 42.
Scheme provides built-in procedures for constructing a Pair, and for extracting each cell
from a Pair:
cons: Value × Value → Pair
Evaluates to a Pair whose first cell is the first input and second cell is the second input. The
inputs can be of any type.
car: Pair → Value
Evaluates to the first cell of the input, which must be a Pair.
cdr: Pair → Value
Evaluates to the second cell of input, which must be a Pair. These rather unfortunate
names come from the original LISP implementation on the IBM 704. The name cons is
short for “construct”. The name car is short for “Contents of the Address part of the
Register” and the name cdr (pronounced “could-er”) is short for “Contents of the
Decrement part of the Register”. The designers of the original LISP implementation picked
the names because of how pairs could be implemented on the IBM 704 using a single
register to store both parts of a pair, but it is a mistake to name things after details of their
implementation. Unfortunately, the names stuck.
We can construct the Pair shown above by evaluating (cons 37 42). DrRacket displays a
Pair by printing the value of each cell separated by a dot: (37 . 42). The interactions below
show example uses of cons, car, and cdr.
> (define mypair (cons 37 42))
> (car mypair)
37
> (cdr mypair)
42
Although Scheme provides the built-in procedures cons, car, and cdr for creating Pairs and
accessing their cells, there is nothing magical about these procedures. We can define
procedures with the same behavior ourselves using the subset of Scheme introduced in
Chapter 3. Here is one way to define the pair procedures (we prepend an s to the names to
avoid confusion with the built-in procedures):
(define (scons a b) (lambda (w) (if w a b)))
(define (scar pair) (pair true))
(define (scdr pair) (pair false))
The scons procedure takes the two parts of the Pair as inputs, and produces as output a
procedure. The output procedure takes one input, a selector that determines which of the
two cells of the Pair to output. If the selector is true, the value of the if expression is the
value of the first cell; if the selector is false, it is the value of the second cell. The scar and
scdr procedures apply a procedure constructed by scons to either true (to select the first
cell in scar) or false (to select the second cell in scdr).
Pairs are useful for representing data that is composed of two parts such as a calendar
date (composed of a number and month), or a playing card (composed of a rank and suit).
But, what if we want to represent data composed of more than two parts such as a date
(composed of a number, month, and year) or a poker hand consisting of five playing cards?
For more complex data structures, we need data structures that have more than two
components.
A triple has three components. Here is one way to define a triple data type:
(define (make-triple a b c)
(lambda (w) (if (= w 0) a (if (= w 1) b c))))
(define (triple-first t ) (t 0))
(define (triple-second t ) (t 1))
(define (triple-third t ) (t 2))
Since a triple has three components we need three different selector values. Another way
to make a triple would be to combine two Pairs. We do this by making a Pair whose
second cell is itself a Pair:
(define (make-triple a b c) (cons a (cons b c)))
(define (triple-first t ) (car t ))
(define (triple-second t ) (car (cdr t )))
(define (triple-third t ) (cdr (cdr t )))
Similarly, we can define a quadruple as a Pair whose second cell is a triple:
(define (make-quad a b c d) (cons a (make-triple b c d)))
(define (quad-first q) (car q))
(define (quad-second q) (triple-first (cdr q))
(define (quad-third q) (triple-second (cdr q))
(define (quad-fourth q) (triple-third (cdr q))
We could continue in this manner defining increasingly large tuples. A triple is a Pair
whose second cell is a Pair. A quadruple is a Pair whose second cell is a triple. A quintuple
is a Pair whose second cell is a quadruple.
An n + 1-uple is a Pair whose second cell is an n-uple. Building from the simple Pair, we can
construct tuples containing any number of components.
c. Data Lists
For many applications, we want to be able to manage data of any length such as all the
items in a web store, or all the bids on a given item. Since the number of components in
these objects can change, it would be very painful to need to define a new tuple type
every time an item is added. We need a data type that can hold any number of items.
This definition almost provides what we need: An any-uple is a Pair whose second cell is
an any-uple. This seems to allow an any-uple to contain any number of elements. The
problem is we have no stopping point. With only the definition above, there is no way to
construct an any-uple without already having one.
The situation is similar to defining MoreDigits as zero or more digits in Chapter 2, defining
MoreExpressions in the Scheme grammar in Chapter 3 as zero or more Expressions, and
recursive composition in Chapter 4. Recall the grammar rules for MoreExpressions:
MoreExpressions ::) ExpressionMoreExpressions
MoreExpressions ::) e
The rule for constructing an any-uple is analogous to the first MoreExpression
replacement rule. To allow an any-uple to be constructed, we also need a construction rule
similar to the second rule, whereMoreExpression can be replaced with nothing. Since it is
hard to type and read nothing in a program, Scheme has a name for this value: null.
DrRacket will print out the value of null as (). It is also known as the empty list, since it
represents the List containing no elements. The built-in procedure null? takes one input
parameter and evaluates to true if and only if the value of that parameter is null.
Using null, we can now define a List: A List is either (1) null or (2) a Pair whose second cell
is a List. Symbolically, we define a List as:
List ::) null
List ::) (cons Value List)
These two rules define a List as a data structure that can contain any number of
elements. Starting from null, we can create Lists of any length:
• null evaluates to a List containing no elements.
• (cons 1 null) evaluates to a List containing one element.
• (cons 1 (cons 2 null)) evaluates to a List containing two elements.
• (cons 1 (cons 2 (cons 3 null))) evaluates to a 3-element List.
Scheme provides a convenient procedure, list , for constructing a List. The list procedure
takes zero or more inputs, and evaluates to a List containing those inputs in order. The
following expressions are equivalent to the corresponding expressions above: (list ), (list 1),
(list 1 2), and (list 1 2 3).
Lists are just a collection of Pairs, so we can draw a List using the same box and arrow
notation we used to draw structures created with Pairs. Here is the structure resulting
from (list 1 2 3).
d. Data List Procedures (Procedures that Examine Lists, Generic Accumulators, Procedures that
Construct Lists)
Since the List data structure is defined recursively, it is natural to define recursive
procedures to examine and manipulate lists. Whereas most recursive procedures on
inputs that are Numbers usually used 0 as the base case, for lists the most common base
case is null. With numbers, we make progress by subtracting 1; with lists, we make
progress by using cdr to reduce the length of the input List by one element for each
recursive application. This means we often break problems involving Lists into figuring out
what to do with the first element of the List and the result of applying the recursive
procedure to the rest of the List.
We can specialize our general problem solving strategy from Chapter 3 for procedures
involving lists:
1. Be very optimistic! Since lists themselves are recursive data structures, most
problems involving lists can be solved with recursive procedures.
2. Think of the simplest version of the problem, something you can already solve. This
is the base case. For lists, this is usually the empty list.
3. Consider how you would solve a big version of the problem by using the result for a
slightly smaller version of the problem. This is the recursive case. For lists, the smaller
version of the problem is usually the rest (cdr) of the List.
4. Combine the base case and the recursive case to solve the problem. Next we
consider procedures that examine lists by walking through their elements and
producing a scalar value.
All of the example procedures in this section take a single List as input and produce a
scalar value that depends on the elements of the List as output. These procedures have
base cases where the List is empty, and recursive cases that apply the recursive procedure
to the cdr of the input List.
The list-length, list-sum, and list-product procedures all have very similar structures. The
base case is when the input is the empty list, and the recursive case involves doing
something with the first element of the List and recursively calling the procedure with the
rest of the List:
(define (Recursive-Procedure p)
(if (null? p)
Base-Case-Result
(Accumulator-Function (car p) (Recursive-Procedure (cdr p)))))
We can define a generic accumulator procedure for lists by making the base case result
and accumulator function inputs:
(define (list-accumulate f base p)
(if (null? p)
base
(f (car p) (list-accumulate f base (cdr p)))))
We can use list-accumulate to define list-sum and list-product:
(define (list-sum p) (list-accumulate + 0 p))
(define (list-product p) (list-accumulate * 1 p))
Defining the list-length procedure is a bit less natural. The recursive case in the original
list-length procedure is (+ 1 (list-length (cdr p))); it does not use the value of the first
element of the List. But, list-accumulate is defined to take a procedure that takes two
inputs—the first input is the first element of the List; the second input is the result of
applying list-accumulate to the rest of the List. We should follow our usual strategy: be
optimistic! Being optimistic as in recursive definitions, the value of the second input
should be the length of the rest of the List. Hence, we need to pass in a procedure that
takes two inputs, ignores the first input, and outputs one more than the value of the
second input:
(define (list-length p)
(list-accumulate (lambda (el length-rest) (+ 1 length-rest)) 0 p))
The procedures in this section take values (including Lists) as input, and produce a new List
as output. As before, the empty list is typically the base case. Since we are producing a List
as output, the result for the base case is also usually null. The recursive case will use cons
to construct a List combining the first element with the result of the recursive application
on the rest of the List.
e. Lists of Lists
The elements of a List can be any data type, including, of course, other Lists. In defining
procedures that operate on Lists of Lists, we often use more than one recursive call when
we need to go inside the inner Lists.
Example 5.9: Summing Nested Lists
Consider the problem of summing all the numbers in a List of Lists. For example,
(nested-list-sum (list (list 1 2 3) (list 4 5 6))) should evaluate to 21. We can define
nested-list-sum using list-sum on each List.
(define (nested-list-sum p)
(if (null? p) 0
(+ (list-sum (car p))
(nested-list-sum (cdr p)))))
This works when we know the input is a List of Lists. But, what if the input can contain
arbitrarily deeply nested Lists? To handle this, we need to recursively sum the inner Lists.
Each element in our deep List is either a List or a Number. If it is a List, we should add the
value of the sum of all elements in the List to the result for the rest of the List. If it is a
Number, we should just add the value of the Number to the result for the rest of the List.
So, our procedure involves two recursive calls: one for the first element in the List when it
is a List, and the other for the rest of the List.
(define (deep-list-sum p)
(if (null? p) 0
(+ (if (list? (car p))
(deep-list-sum (car p))
(car p))
(deep-list-sum (cdr p)))))
f. Data Abstraction
The mechanisms we have for constructing and manipulating complex data structures are
valuable because they enable us to think about programs closer to the level of the
problem we are solving than the low level of how data is stored and manipulated in the
computer. Our goal is to hide unnecessary details about how data is represented so we can
focus on the important aspects of what the data means and what we need to do with it to
solve our problem. The technique of hiding how data abstraction data is represented from
how it is used is known as data abstraction.
The data types we have seen so far are not very abstract. We have data types for
representing Pairs, triples, and Lists, but we want data types for representing objects
closer to the level of the problem we want to solve. A good data abstraction is abstract
enough to be used without worrying about details like which cell of the Pair contains
which datum and how to access the different elements of a List. Instead, we want to
define procedures with meaningful names that manipulate the relevant parts of our data.
The rest of this section is an extended example that illustrates how to solve problems by
first identifying the objects we need to model the problem, and then implementing data
abstractions that represent those objects. Once the appropriate data abstractions are
designed and implemented, the solution to the problem often follows readily.
II. Problem Solving:
a. Define or identify a procedure that has the given type.
i. Number × Number → Boolean
(lambda (x y) if (< x y))
ii. Number → Number
(lambda a (- 1 a))
iii. (Number → Number) × (Number → Number) → (Number → Number)
(lambda (f g) (lambda (x) (g (f x))))
iv. Number → (Number → (Number → Number))
(lambda (x) (lambda (y) (lambda(z) (+ x y z))))
b. Suppose the following definition has been executed:
(define tpair
(cons (cons (cons 1 2) (cons 3 4)) 5))
Draw the structure defined by tpair, and give the value of each of the following expressions.
a. (cdr tpair) = 5
b. (car (car (car tpair))) = 1
c. (cdr (cdr (car tpair))) = 4
d. (car (cdr (cdr tpair))) = error
c. Define a procedure that constructs a quintuple and procedures for selecting the five elements
of a quintuple.
(define (make-quintuple a b c d e) (cons a (make-quadruple b c d e)))
(define (quintuple-first q) (car q))
(define (quintuple-second q) (quad-first (cdr q)))
(define (quintuple-third q) (quad-second (cdr q)))
(define (quintuple-fourth q) (quad-third (cdr q)))
(define (quintuple-fifth q) (quad-fourth (cdr q)))
d. Define a procedure is-list? that takes one input and outputs true if the input is a List, and false
otherwise.
(define (is-list? p)
(if (null? p)
true
(if (pair? p)
(is-list? (cdr p))
false)))
e. Define a procedure list-last-element that takes as input a List and outputs the last element of
the input List. If the input List is empty, list-lastelement should produce an error.
(define (last-list-element p)
(if (null?(cdr p))
(car p)
(last-list-element (cdr p))))
f. Define a procedure list-increment that takes as input a List of numbers, and produces as output
a List containing each element in the input List incremented by one. For example,
(list-increment 1 2 3) evaluates to (2 3 4).
(define (list-increment p)
(if (null? p)
null
(cons (+ 1 (car p)) (list-increment (cdr p)))))
g. Define a procedure list-filter-even that takes as input a List of numbers and produces as output
a List consisting of all the even elements of the input List.
(define (list-filter-even p)
(if (null? p)
null
(if (even? (car p))
(cons (car p) (list-filter-even (cdr p)))
(list-filter-even (cdr p)))))