Numbers
Due: September 9 11:59pm
Why?
The purpose of this project is to give you an excuse to learn how to hack in LISP. You will be asked to write a few simple LISP functions, mainly to manipulate lists. Eventually, you will solve a simple graph search problem. As noted above, this is worth 2% of your final grade, but its real value is that it will help you to become comfortable enough with LISP that you will find it much easier to do the next assignment (though I'd like to point out that 2% is still 2%, and even those of you who dream in defmacro might be happy that you earned that 2% come the end of the semester).
References
First off, the Class Resources pages should point you to many online and offline references. Locally, you should be able to run lisp images on any CNS-supported machine. We have Allegro CL from Franz available on our Unix and Windows environments. The lisp examples from class (in the syllabus) should help in getting you up to speed.
Clarification:Because the point of this is to learn lisp, please write the functions yourself. So, even though there is a "member" function, you are to write one yourself without using the "member" function from lisp.
Each of the functions below should be put in a file/attachment named yourgtaccount.lisp.
Basic LISP
(my-member 'a '(b c j a)) => (A);;;; Problem 1 (my-member '(c d) '(a b c (a b) (c d) j k)) => ((C D) J K) (my-member 'a '(b c (a d) e)) => nil
(my-insert 'a '(b c d e) 0) => (A B C D E) (my-insert 'a '(b c d e) 3) => (B C D A E) (my-insert 'a '(b c d e) 14) => (B C D E A)
(my-remove-dups '(a b a c a d)) => (B C A D) (my-remove-dups '(a b (a c) a d)) => (B (A C) A D) (my-remove-dups '(a b (b c) a (b c) d)) => (B A (B C) D)
(my-intersection '(a b c) '(c a e)) => (A C) (my-intersection '(a b (a c) c) '(a c b)) => (A B C) (my-intersection '(a b (a c) c) '((a c) b)) => (B (A C))
(dyadic-apply #'+ '(1 2 3) '(6 5 4)) => (7 7 7) (dyadic-apply #'list '(a b c) '(1 2 3 4)) => ((A 1) (B 2) (C 3))
(tree-sum '(1 2 3 4 5)) => 15 (tree-sum '(a 1 b 2)) => 3 (tree-sum '(a (1 (2 (3 b) c) d e) f 4)) => 10
(my-flatten '(a b (c d))) => (A B C D) (my-flatten '(a (b (c (d (e f))) g (h i (j k)) l (m)) (((n))))) => (A B C D E F G H I J K L M N)
(full-reverse '(a b c d)) => (D C B A) (full-reverse '(a (b c) d)) => (D (C B) A) (full-reverse '((a b) (c (d e) f g (h i) j k) l m)) => (M L (K J (I H) G F (E D) C) (B A))
;; replace all occurrences of B with A (my-subst 'a 'b '(t h b d s t b r n e r)) => (T H A D S T A R N E R) (my-subst 'a 'b '(a b (a b (a b)) (a b))) => (A A (A A (A A)) (A A)) ;; replace (P Q) with A (my-subst 'a '(p q) '(p q (p q) (((p q) a b (p q a))))) => (P Q A ((A A B (P Q A))))
An adjacency list might be stored in the following way. For each node, a list is kept of all nodes that are one step away. This list has the form (source node . list of adjacent nodes). For example, if you can reach nodes B, C, and D from node A, the corresponding list would be (A B C D). By grouping all of these lists into a single list, the graph is fully represented.
A list of edges is a less compact way to represent a graph, but may provide easier access if the edges have weights. For example, we could represent an edge from node A to node B as (A B). If this edge had a weight of 7, the representation might change to (A B 7).
Note that in both representation, we must specify whether edges are uni- or bi-directional. That is, does (A B) imply (B A) or must (B A) be explicit in the representation if the edge in that direction exists? For this problem, assume that all edges are unidirectional meaning that if B is adjaceny to A (i.e. (A B)), A is not necessarily adjacent to B (i.e. (B A) is *not* implied).
;; define a graph via an adjacency list (defparameter adjlist '((a b c) (b e) (c d) (d b c e) (e d f h) (f e) (g h) (h e g))) (adj-list-to-edge-list '((a b))) => ((A B)) (adj-list-to-edge-list '((a b c d))) => ((A B) (A C) (A D)) (adj-list-to-edge-list adjlist) => ((A B) (A C) (B E) (C D) (D B) (D C) (D E) (E D) (E F) (E H) (F E) (G H) (H E) (H G))It is important to realize that the order of the nested lists (i.e. the edges) is not important. So '((A B) (A C) (B C)) is equivalent to '((B C) (A C) (A B)) for example.
One common technique that uses much less memory is to simply store the optimal predecessor of each node. Since a search will define a tree rooted at the source node, we know that each node was reached from exactly one other node. If we simply store this so-called "parent node," we can easily reconstruct the desired path given a goal node.
For example, if our search was looking for a path to node E from node A and it determined that we got to B from A, to C from A, to D from B, and to E from D, we might have a data structure that looks like this:
((B A) (C A) (D B) (E D))Each internal list holds a node in the first position and its parent in the second. With this representation, it is easy to reconstruct the path. Of course, contructing the path from the start node to the goal node will require another search (note that from node A, we can get to nodes B or C), but the path is unambiguous when reconstructed backward (i.e. from goal to start node). Luckily, it is a trivial matter to reverse the path after it is reconstructed.
Write a function that takes a list of nodes and parents and returns a path from a given start node to a given goal node. You may assume that the path will always exist and be unambiguous.
(reconstruct-path 'a 'c '((b a) (c b))) => (A B C) (reconstruct-path 'a 'e '((b a) (c a) (d b) (e d))) => (A B D E) (reconstruct-path 'b 'e '((g a) (a c) (c d) (b a) (i b) (f e) (j i) (r i) (d r) (e g))) => (B I R D C A G E)
(add-edge '(a b 3) nil) => ((A B 3)) (add-edge '(c d 5) '((a b 3))) => ((A B 3) (C D 5)) (add-edge '(a c 1) '((a b 3) (c d 5))) => ((A C 1) (A B 3) (C D 5)) (add-edge '(b d 4) '((a c 1) (a b 3) (c d 5))) => ((A C 1) (A B 3) (B D 4) (C D 5))
Write a function that takes the description of a graph, a start node, and a goal node , and determines if the goal node can be reached from the given starting point. The return value should be a path connecting the nodes (i.e. a list of nodes) or nil if no such path exists. Your path does not have to be optimal. The description of the graph will be an adjacency list represented as a list of lists. Each internal list is of the form (Node . List of adjacencies). All edges are unidirectional. Please solve this problem by implementing a depth-first search.
;; define a graph via an adjacency list (defparameter adjlist '((a b c) (b e) (c d) (d b c e) (e d f h) (f e) (g h) (h e g)))This means that you can get to B & C from A in one step, to E from B, to D from C, to B, C, & E from D, etc.
(dfs adjlist 'a 'g) => (A C D E H G) (dfs adjlist 'e 'c) => (E D C) (dfs adjlist 'h 'a) => nilNote that these solutions are not necessarily unique. For example:
(dfs adjlist 'a 'g) => (A B E H G)is also acceptable.