===================[ Midterm Warm-Up ]===================
These are the warm-up problems for the midterm. The actual midterm
problems will be harder; these problems should give you a taste
of their smaller parts.
1. Forest for the trees.
Assume that you are given trees with numbers, symbols, or strings
at the nodes, one value of the above type at each node. These trees
are represented as LISP lists as follows: (VALUE CHILD_SUBTREE1 CHILD_SUBTREE2 ...)
for nodes that have subtrees under them, and (VALUE) for nodes that
do not (the leaf nodes), where a CHILD_SUBTREE is in turn a list
formatted as above.
Write recursive functions that, for a given tree, return:
- the sum of all numbers in it
- the list of all numbers in it (in the depth-first traversal (DFT) order)
- the list of all strings in it (in DFT)
- the tree in which all strings are converted to symbols
(assume all strings are legal symbol names in your LISP). Do not
change the original tree.
- the _original_ tree in which all strings are converted to symbols
(assume all strings are legal symbol names in your LISP). This is
"destructive" w.r.t. the original tree.
- the tree in which at each level the nodes are reordered
so that those with numbers come first, then those with strings,
then those with symbols
- the string that is a concatenation of all strings in it, in *breadth-first order*
Reuse code when possible---e.g., when two tasks are almost similar,
such as the 2nd and 3rd above, write one function for the traversal
and another to test a value at a node.
Do the above with tail-recursion, if possible. Use the accumulator
trick (see OnLisp, class notes).
2. Unwinding the Golden Spiral
Write a program to compute the 100000th Fibonacci number in Lisp (or Ruby).
(See http://world.mathigon.org/Sequences for the Fibonacci numbers definition
and the spiral)
3. A poor man's counter object
In a Common LISP or in Elisp with _lexical binding_ (Emacs 24 or later),
write a LISP function MAKE-COUNTER that will return a list of
three anonymous functions (lambdas/closures). When called (with FUNCALL),
these functions shall, respectively, return the value of a counter,
increment that counter by 1, and decrement the counter by 1. The next
invocations should show the updated value.
Each invocation of the MAKE-COUNTER should give you a triple of
functions that operate on a new counter.
Have MAKE-COUNTER take optional values for the starting counter value
and the increment/decrement value other that the default 1.
How will the above code evaluate under dynamic binding (Elisp's default)?
4. From Ruby to LISP and Back Again, via Sexps.
Ruby (starting with 2.0) can output the S-expressions (sexps)
that its parser produces for programs. For example:
[:program,
[[:binary,
[:@int, "2", [1, 0]],
:+,
[:binary, [:@int, "2", [1, 2]], :*, [:@int, "3", [1, 6]]]]]]
(The third elements of each :@int, such as [1, 0], [1, 2] and [1, 6]
above are parser's internal record of the line and column position of
the respective input tokens. They do not play a role in the evaluation.)
If we change in this all square brackets to round ones and remove commas,
we will get a LISP list.
Write a LISP function that evaluates such lists derived from Ruby arithmetic
expressions, and rejects sexps that don't come from arithmetic expressions.
You can print the rejection message with the line and column number if you
like.
------------------ A dirty ruby hack for the substitution ---------------
# This is quick-and-very-dirty! What if strings in the sexp contain []s,
# commas, newlines? Escaping should be observed for a proper way to do this!
require 'ripper'
require 'pp'
code = "2+2 * 3"
puts Ripper.sexp(code).pretty_inspect.tr("[],\n", "() ")
-------------------------------------------------------------------------
5. I am picking anchors off HTML trees
The SBCL package "cl-html-parse" (see class notes on LISP packages
on how to install it) produces LISP representations of HTML documents
parsed into LISP lists.
Write a function that gets such a list representing a syntactically
valid HTML document, and prints all "A" (anchor) elements in it, as
follows: first the URL, then the text of the anchor. For example,
QuickLisp Beta
should produce
https://www.quicklisp.org/ QuickLisp Beta
(notice that the anchor text may contain any HTML markup, which
your function should remove, preserving the plain text).
I'll provide some parses to practice on, but just in case:
------------------ Using cl-html-parse with strings and files -------------------
CL-USER> (ql:quickload "cl-html-parse")
To load "cl-html-parse":
Load 1 ASDF system:
cl-html-parse
; Loading "cl-html-parse"
("cl-html-parse")
CL-USER> (html-parse:parse-html "QuickLisp Beta")
(((:A :HREF "http://www.quicklisp.org/") "QuickLisp " (:I "Beta")))
CL-USER> (html-parse:parse-html (open "myhtmlfile.html"))
((:HEAD (:TITLE "My page"))
(:BODY
(:P "Nothing here but plain old 1990s "
((:A :HREF "http://www.w3schools.com/html/") "HTML"))))
CL-USER>
----------------------------------------------------------------------------------
(see http://www.gigamonkeys.com/book/files-and-file-io.html for
hints on LISP file I/O)