Finding the path between 2 nodes in a graph in scheme - scheme

can someone please explain why i have the error:
map: contract violation
expected: list?
given: 'e3
argument position: 2nd
other arguments...:
#<procedure>
in the code below???
My expected output is: '(e4 e5 e0)
I'm trying to use the depth first search algorithm to find the path between 2 nodes, the code is:
#lang racket
(define successors ; finding successor of a node in graph
(lambda (node graph)
(let ((val (assq node graph)))
(if val
(cdr val)
'()))))
(define make-task cons)
(define task-node car)
(define task-path cdr)
(define same-node? eq?)
(define linked ; function to find the path between 2 nodes in a graph
(lambda (start end graph) ; begin, end: nodes graph: a graph
(define handle-node
(lambda (node path todo seen)
(if (same-node? node end)
(reverse (cons node path))
(if (assq node path)
(process-todo todo seen)
(process-todo (append (map (lambda (successor)
(make-task successor
(cons node
path)))
(successors node
graph))
todo)
(cons node seen))))))
(define process-todo
(lambda (todo seen)
(if (null? todo)
#f
(let ((task (car todo)))
(handle-node (task-node task) ;;; node
(task-path task) ;;; path
((lambda (x y) (cdr y)) task todo) ;;; rest to do
seen)))))
(handle-node start '() '() '())))
(linked 'e4 'e0 '((e1 . e0) (e2 . e1) (e3 . e2) (e4 . e3) (e4 . e5) (e5 . e0)))
Thank you very much!!

Your successor function has a 'type' error. The alternate of the if returns a list (actually '()) whereas the consequent of the if returns (cdr val) which is not a list (based on your input data). This is confirmed from the error message - a list? was expected but a symbol? was provided.
It looks like your graph representation is meant to be an association between the node name and a list of the connected node names. But your input data is not in that form! Try:
'((e1 e0) (e2 e1) ...)
By the way, successor is written idiomatically as:
(define (successor node graph)
(cond ((assq node graph) => cdr)
(else '())))

Related

Implementing shortest path in racket using BFS in a purely functional way?

How would we go about implementing get the shortest path between the start vertex and the end vertex?
The program should return a list of edges (shortest path) using BFS.
(define (new-paths path node net)
(map (lambda (n) (cons n path)) (cdr (assoc node net))))
(define (shortest-path start end net)
(bfs end (list (list start)) net))
;; Breadth-first search
(define (bfs end queue net)
(display queue) (newline) (newline) ; entertainment
(if (null? queue)
'()
(let ((path (car queue)))
(let ((node (car path)))
(if (equal? node end) ;; Graham used CL eql
(reverse path)
(bfs end
(append (cdr queue)
(new-paths path node net))
net))))))
I came up with but this does not seem to work. Can someone provide an implementation in a purely functional way?
In the form (get-shortest-path vertices edges src dest)
An example of the call would be
(get-shortest-path '(a b c d) (cons (cons a b) (cons b c) (cons c d)) 'a 'c)
; Get the shortest path between a and c

Finding path between 2 points in Racket

I have following list of connections:
(define routelist
(list
(list'a 'b)
(list'a 'c)
(list'b 'e)
(list'b 'f)
(list'b 'c)
(list'a 'd)
(list'e 'f)
(list'f 'g)))
Routes between 'a and 'g are to be found. This page shows a solution in Prolog: http://www.anselm.edu/homepage/mmalita/culpro/graf1.html
I could manage following solution, though it is iterative:
(define (mainpath routelist start end (outl '()))
(if (equal? start end)
(println "Same start and end points.")
(for ((item routelist))
(when (equal? start (list-ref item 0))
(set! outl (cons start outl))
(if (equal? end (list-ref item 1))
(begin
; PATH FOUND:
(set! outl (cons end outl))
(println (reverse outl)))
(begin
(mainpath (rest routelist) (list-ref item 1) end outl)
(set! outl (rest outl))))))))
(mainpath routelist 'a 'g)
Output:
'(a b e f g)
'(a b f g)
How can a functional solution be achieved in Racket?
Here is a very simple solution:
(define (mainpath routelist start end)
(define (neighbors node)
(map second (filter (lambda (x) (eq? (first x) node)) routelist)))
(define (visit node visited)
(when (not (member node visited))
(when (eq? node end)
(println (reverse (cons node visited))))
(let ((new-visited (cons node visited)))
(map (lambda (x) (visit x new-visited)) (neighbors node)))))
(visit start '())
"No more paths")
This recursive function, that can manage also graphs with loops, keeps a list of nodes already visited along the current path and stops when it has visited all the nodes reachable from the start node. When the current node is the end node, the current path is printed.
Use DFS algorithm will be ok.
(define (mainpath routelist start end)
(letrec ([next-nodes (λ (node)
(for/list ([al routelist]
#:when (eq? node (first al)))
(second al)))]
[path (λ (node vlist)
(let ([new-list (cons node vlist)])
(when (eq? node end)
(println (reverse new-list)))
(for ([next (next-nodes node)]
#:unless (memq next vlist))
(path next new-list))))])
(path start '())))

How do I ensure the empty list isn't printed (Scheme)?

I have this code:
(define graph `(A (B (C)) (D (E)) (C (E))))
(define (prog1 graph)
(let ([seen `()])
(define (sub g)
(cond
[(member (car g) seen) `()]
[else
(set! seen (cons (car g) seen))
(cond
[(null? (cdr g)) (list (car g))]
[else
(cons (car g) (map sub (cdr g)))])]))
(sub graph)))
It prints a connected graph where all the nodes appear once. However, if a node has already been visited I return the empty list `(). This causes a problem with the output and I don't know how to fix it:
When running (prog1 graph) The current output is: (A (B (C)) (D (E)) ())
However, I want the output to be (A (B (C)) (D (E)))
Any hint on how I can modify the code to achieve this would be great.
If the empty lists only occur at the topmost level in the list, you could filter them out. Replace the last line in your procedure with this:
(filter (negate null?) (sub graph))
Or simply this:
(remove '() (sub graph))
If the empty lists occur at any nesting level, you can apply the same idea (filtering out empty lists) recursively, at each step in the traversal.

Scheme Error, Return Binary Search Tree as Ordered List (R5RS)

I am a noob at Scheme. I have a binary search tree. The format of a node is a list of three elements, the first being the value at the node, the second being the left child node, and the third being the right child node. The "make" function creates an empty tree that looks like this: ( () () () ). I am able to create the tree, insert values, and find if a certain value exists in the tree. My problem comes when I try to write a function that returns the tree as an ordered list. Insert and Make functions:
;Inserts a number into the tree
(define (insert t x)
(cond ((null? (car t))
(list x (make) (make)))
((< x (car t))
(list (car t) (insert (cadr t) x) (caddr t)))
((> x (car t))
(list (car t) (cadr t) (insert (caddr t) x) ))
)
)
;Makes a new empty tree
(define (make)
(list (list) (list) (list))
)
Those works fine. Here is my as-list:
;Gives list of all numbers
(define (as-list t)
(cond
(
(null? (car t) )
(list)
)
(
#t
(append (as-list (cadr t)) (as-list (car t)) (as-list (caddr t)))
)
)
)
Running this, I get a contract violation, saying it expected "mpair?". I do not believe this is a logic error on my part, but it may be. Is this another parentheses problem?
Thank you for your time!
Your recursion should be
(append (as-list (cadr t)) (list (car t)) (as-list (caddr t)))
You only want to call as-list on trees, and the car of your t is not a tree.

Problem with implementing Depth First Search in Scheme

Im trying to implement Depth First Search in Scheme, but I can only get it to partially work.
This is my code:
(define (depth-first-search graph node neighbour path dest)
(cond ((null? neighbour) #f)
((equal? node dest) path)
((member (car neighbour) path) (depth-first-search graph node (cdr neighbour) path dest))
((memq (car neighbour) (car graph)) (depth-first-search (cdr graph) (car neighbour) (memq (car neighbour) (car graph)) (append path (list (car neighbour))) dest))
(else depth-first-search (cdr graph) path dest)))
And this is my graph, data structure:
(define complete-graph
'((a b c d e)
(b a c)
(c a b f)
(d a e h)
(e a d)
(f c g i)
(g f h i j)
(h d g j)
(i f g j)
(j g h i)))
This is how I call the procedure:
(depth-first-search complete-graph (caar complete-graph) (cdar complete-graph) (list (caar complete-graph)) 'd)
To procedure shoud return the full path frome the start-node to the dest(ination) as a list, but it only seems to work with some start and destination nodes. If I start with 'a and 'c, it returns the right list '(a b c), but if I try with 'a and 'd, I get #f in return. So it probably is something wrong with the backtracking in the algorithm. But I have looked at the code for too long, and I really can't find the problem..
Assuming node 'a has children '(b c d e), node 'b has children '(a c), node ... First you need a function that expands a node to its children.
(define (expand graph node)
(let ((c (assq node graph)))
(if c (cdr c) '())))
Second: you have to remember all visited nodes. In general hat is different from the path (maybe it does not matter in this example). Third: you need to remember all nodes you want to visit (result from the node expansion process). So define a helper function
(define (dfs* graph visited border path dest)
If there are no nodes left to visit, then no road exist.
(cond ((null? border) #f)
If the first element in border is equal to our destination, then we are happy
((eq? (car border) dest) (cons (car border) path))
Lets check all visited nodes. If the first node in border was visited before then proceed without node expansion
((memq (car border) visited)
(dfs* graph visited (cdr border) path dest))
Otherwise expand the first node of border
(else (dfs* graph
(cons (car border) visited)
(append (expand graph (car border)) (cdr border))
(cons (car border) path)
dest))))
Call that helper function with starting values for visited, border and path:
(define (dfs graph src dst)
(dfs* graph '() (list src) '() dst)
For breath first search: append the expanded node at the end of border
Edit:
a) visited and path are the same, you can drop one of them
b) the path is returned in reverse order
c) The procedure is not correct, path contains all visited nodes. But a post processing of the result of dfs* will do the job.
You don't want to change the graph as you do the depth-first search, just the current node. If you want to do things purely functionally, have your function look like:
(define (depth-first-search graph node dest path)
(let dfs ((node node) (path path))
(let ((recur (lambda (node) (dfs node (cons node path)))))
; Write code here
; Recursive calls should use recur, not dfs or depth-first-search
...)))
(returning a path or #f as the result). You can use ormap (in Racket or SRFI-1) to iterate through all neighbors of a node, returning the first value that is not #f.

Resources