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 '())))
Related
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
I have the following recursive function in Lisp
(defun f (item tree)
(when tree
(if (equal item (car tree)) tree
(if (and (listp (car tree))
(equal item (caar tree)))
(car tree)
(if (cdr tree)
(f item (cdr tree)))))))
This function receives a tree and an item to look for in its immediate leaves. If item is the car of any sublist, then it will return that sublist. That is,
(f 'c '(a b c)) => (c)
(f 'b '(a b c)) => (b c)
(f 'a '((a 1 2) b c)) => (a 1 2)
I've recently been informed that (Emacs Lisp) doesn't do tail recursion optimization, so I've been advised to turn this into a while loop. All of my training in Lisp has been in avoidance of loops like this. (I maintain that they are un-functional, but that's borderline pedantic.) I've made the following attempt for more conformative style:
(defun f (item tree)
(let ((p tree))
(while p
(cond
((equal item (car p)) p)
((and (listp (car p))
(equal item (caar p)))
(car tree))
(t (f item (cdr p))))
(setq p (cdr p)))))
I've shortened the function name for brevity/clarity, but do have a look at where it is being used if you are a power-user of emacs.
Your "iterative" solution is still recursing. It's also not returning the values found in the cond expression.
The following version sets a variable to the found result. Then the loop ends if a result has been found, so it can be returned.
(defun f (item tree)
(let ((p tree)
(result nil))
(while (and p (null result))
(cond ((equal item (car p)) (setq result p))
((and (listp (car p))
(equal item (caar p)))
(setq result (car tree)))
(t (setq p (cdr p)))))
result))
I have found a recursive problem in one page that says the following:
If a person enter a string with two consecutive letters that are the same, it should put a 5 between them. For example if I enter "hello"
it should print "hel5lo"
I have done the following program in Scheme:
(define (function listT)
(if (empty? listT)
'()
(begin
(if (eq? (car listT) (car (cdr listT)))
(display 5)
(display (car listT))
)))
(function (cdr listT)))
and tested with:
(function'( 'h 'e 'l 'l 'o))
and the problem I got is
car: contract violation
expected: pair?
given: ()
I suppose that is because at one moment (car (cdr listT)) will face an empty list, have tried with a conditional before, but still with some issues.
Is it possible to do it only using recursion over the list of characters with cdr and car? I mean not with new variables, strings, using reverse or loops?
Any help?
Thanks
This happens when there is only one character left in the list; (cdr listT) will be the empty list '() and the car of the empty list is undefined.
So you either need to check that the cdr isn't empty, for example:
(define (f str)
(let loop ((lst (string->list str)) (res '()))
(if (null? lst)
(list->string (reverse res))
(let ((c (car lst)))
(loop (cdr lst)
(cons c
(if (and (not (null? res)) (char=? c (car res)))
(cons #\5 res)
res)))))))
or, instead of looking one character ahead, turn around your logic and keep track of the last character, which is initialised to some value that will be different in every case (not as elegant as the first solution though IMO):
(define (f str)
(list->string
(let loop ((prev #f) (lst (string->list str)))
(if (null? lst)
'()
(let ((c (car lst)))
(if (equal? c prev)
(cons #\5 (cons c (loop c (cdr lst))))
(cons c (loop c (cdr lst)))))))))
[EDIT alternatively, with an explicit inner procedure:
(define (f str)
(define (inner prev lst)
(if (null? lst)
'()
(let ((c (car lst)))
(if (equal? c prev)
(cons #\5 (cons c (inner c (cdr lst))))
(cons c (inner c (cdr lst)))))))
(list->string (inner #f (string->list str))))
]
Testing:
> (f "hello")
"hel5lo"
> (f "helo")
"helo"
> (f "heloo")
"helo5o"
Side note: don't double quote:
> '('h 'e 'l 'l 'o)
'('h 'e 'l 'l 'o)
> (car '('h 'e 'l 'l 'o))
''h
This is probably not what you expected. Instead:
> '(h e l l o)
'(h e l l o)
> (car '(h e l l o))
'h
or
> (list 'h 'e 'l 'l 'o)
'(h e l l o)
> (car (list 'h 'e 'l 'l 'o))
'h
Also note that these are symbols, whereas, since you start from a string, you want characters:
> (string->list "hello")
'(#\h #\e #\l #\l #\o)
EDIT 2
I see you are still struggling with my answer. Here's a solution that should be as minimal as you requested, I hope this is it:
(define (f lst (prev #f))
(unless (null? lst)
(when (equal? (car lst) prev) (display "5"))
(display (car lst))
(f (cdr lst) (car lst))))
or even
(define (f lst)
(unless (null? lst)
(display (car lst))
(when (and (not (null? (cdr lst))) (equal? (car lst) (cadr lst)))
(display "5"))
(f (cdr lst))))
Testing:
> (f '(h e l l o))
hel5lo
> (f '(h e l o))
helo
> (f '(h e l o o))
helo5o
I have found a solution:
(define (func lisT)
(if (empty? (cdr lisT))
(display (car lisT))
(begin
(if (eq? (car lisT) (car (cdr lisT)))
(begin
(display (car lisT))
(display 5)
)
(display (car lisT))
)
(func (cdr lisT))
)
))
Here's a solution including just one, top-level recursive function:
(define (insert list item)
(if (< (length list) 2) ;; not enough elements to compare?
list ;; then just return the input
(let ((first (car list)) ;; capture the first element,
(second (cadr list)) ;; the second element,
(rest (insert (cdr list) item))) ;; and the recursively processed tail
(cons first ;; construct a list with the first element
(if (eq? first second) ;; compare the first two and return either
(cons item rest) ;; the item before the rest
rest))))) ;; or just the rest
It takes as input a list and an item to be inserted between each two consecutive identical elements. It does not display anything, but rather returns another list with the result of the insertion. For example,
(insert '(1 2 2 3 3 3 2 2 1) 0)
results in
(1 2 0 2 3 0 3 0 3 2 0 2 1)
This hopefully solves your problem and seeds further experimentation.
Here is a straightforward function from a list to a list:
(define (add5s s)
(cond ((null? s) s)
((null? (cdr s)) s)
((equal? (car s) (cadr s)) (cons (car s) (cons 5 (add5s (cdr s)))))
(else (cons (car s) (add5s (cdr s))))
)
)
A list either:
is null
has one element
begins with two equal elements
begins with two unequal elements
A list with a 5 put between consecutive equal elements is respectively:
the list
the list
the first element followed by a 5 followed by the rest of it with a 5 put between consecutive equal elements
the first element followed by the rest of it with a 5 put between consecutive equal elements
A Scheme string is not a list of characters or a list of symbols. If you want to input and output strings then you should use the corresponding string operators. Or write a function that defines this one, calls it with string->list of an input string and outputs list->string of this one's result list. Or a function like this one but that branches on string->list of its input string and outputs list->string of what this one returns.
(It is really not clear what code is to be written. You say "enters a string", but your "tested" code is a function that takes a list as argument, rather than reading from a port. And you say "put a 5" but you print argument list elements or a 5 via display to a port, rather than returning a value of the type of the argument. And you give an example passing an argument that is a list of quoted symbols rather than just symbols let alone characters. (If you want to pass a list of symbols then use '(h e l l o) or (list 'h 'e 'l 'l 'o).) Say exactly what is to be produced, eg, a function with what arguments, return value and effect on ports.)
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 '())))
Although the following code works perfectly well in DrRacket environment, it generates the following error in WeScheme:
Inside a cond branch, I expect to see a question and an answer, but I see more than two things here.
at: line 15, column 4, in <definitions>
How do I fix this? The actual code is available at http://www.wescheme.org/view?publicId=gutsy-buddy-woken-smoke-wrest
(define (insert l n e)
(if (= 0 n)
(cons e l)
(cons (car l)
(insert (cdr l) (- n 1) e))))
(define (seq start end)
(if (= start end)
(list end)
(cons start (seq (+ start 1) end))))
(define (permute l)
(cond
[(null? l) '(())]
[else (define (silly1 p)
(define (silly2 n) (insert p n (car l)))
(map silly2 (seq 0 (length p))))
(apply append (map silly1 (permute (cdr l))))]))
Another option would be to restructure the code, extracting the inner definitions (which seem to be a problem for WeScheme) and passing around the missing parameters, like this:
(define (insert l n e)
(if (= 0 n)
(cons e l)
(cons (car l)
(insert (cdr l) (- n 1) e))))
(define (seq start end)
(if (= start end)
(list end)
(cons start (seq (+ start 1) end))))
(define (permute l)
(cond
[(null? l) '(())]
[else (apply append (map (lambda (p) (silly1 p l))
(permute (cdr l))))]))
(define (silly1 p l)
(map (lambda (n) (silly2 n p l))
(seq 0 (length p))))
(define (silly2 n p l)
(insert p n (car l)))
The above will work in pretty much any Scheme implementation I can think of, it's very basic, standard Scheme code.
Use local for internal definitions in the teaching languages.
If you post your question both here and at the mailing list,
remember to write you do so. If someone answers here, there
is no reason why persons on the mailing list should take
time to answer there.
(define (insert l n e)
(if (= 0 n)
(cons e l)
(cons (car l)
(insert (cdr l) (- n 1) e))))
(define (seq start end)
(if (= start end)
(list end)
(cons start (seq (+ start 1) end))))
(define (permute2 l)
(cond
[(null? l) '(())]
[else
(local [(define (silly1 p)
(local [(define (silly2 n) (insert p n (car l)))]
(map silly2 (seq 0 (length p)))))]
(apply append (map silly1 (permute2 (cdr l)))))]))
(permute2 '(3 2 1))