views:

177

answers:

2

This is a homework question,I'm trying to do a Depth-first search function in Scheme,Here's the code I've written so far:

(define explore
(λ(node visited)
  (let* ([neighbors             (force (cdr node))]
         [next        (nextNode visited neighbors)]
         [is-visited        (member? node visited)])

    (cond

      ;if I have no unvisited neighbours print current node and go up one level
      [(equal? next #f)
       (begin
         (display (car node))
         (display " "))]

      ;if current node is not visited and I have unvisited neighbors
      ;print current node,mark as visited and visit it's neighbors
      [(and (equal? is-visited #f) (not (equal? next #f)))
       (begin
         (display (car node))
         (display " ")
         (explore next (cons node visited)))])

    ;go and visit the next neighbor
    (if (not (equal? (nextNode (cons next visited) neighbors) #f ))
     (explore (nextNode (cons next visited) neighbors) (cons node visited))))))  

'node' is the current node
'visited' is a list in witch I keep track of the nodes I visited
'nextNode' is a function that returns the first unvisited neighbor if any or #f otherwise
'member?' test's if a node is in the visited list

The Graph representation is using adjacent made using references to nodes with letrec so that's why I use force in 'neighbors': Eg:
(letrec ([node1 (list "NY" (delay (list node2 node3)))],where node2 and node3 are defined as node1

The problem witch I'm dealing with is that my visited lists looses track of some of the nodes I visited when I come out of recursion,How can I fix this ?

+1  A: 

You have to get the new visited list as a returned value from your recursive calls. You'll either have to modify explore so that it returns its visited list, or define a helper function that you recurse on instead of explore. Then after you recurse, you'll have to use the new visited list that the function returns.

EDIT: Perhaps a better way would be to just restructure your function. I think it's more complicated than it needs to be. You're just doing a depth-first traversal, right? Not really a search? You could try something more like this, using an explicit stack to keep track of nodes to visit, and a list of nodes visited:


(define dft
  (lambda (graph)
    (helper graph '(1) '())))

(define helper
  (lambda (graph stack visited)
    (if (empty? stack)
      '() ;we're done. you've got a nice list of visited nodes now, what will you do with it? Return it?
      (let ([currentNode (car stack)])
        (if (member? currentNode visited) 
            (helper graph 
                    ;what do you think the next two parameters are?
                    )
            (begin
              (display currentNode)
              (helper graph 
                      ;what do you think the next two parameters are?
                      ))))))

Since this is a homework problem, I've left the two parameters for the helper function for you to fill in. The nicest thing about this approach is that it's very simple to change it to a breadth-first traversal.

Here's a hint: the parameters for the two different cases will be slightly different.

ajduff574
If i return the visited list how can I fetch it when coming out of recursion here :(explore next (cons node visited)))]),cand I define a local variable in witch to store it,if yes how ?if I return it when coming out of recursion here:[(equal? next #f) ( begin (display (car node)) (display " ") (cons node visited)]and I do (display (explore next (cons node visited))) it will print void ,why is that ?
John Retallack
what does tree represent in " (helper tree" ?
John Retallack
Sorry, I originally wrote the function thinking trees instead of graphs. You would pass the graph in to the function, since you would need the graph to find the neighbors. I've edited the code to be clearer.
ajduff574
I think I know what the parametrs are,but I can't figure out where you push nodes onto the stack,shouldn't the algorithm be something like: get node from stack,mark as visited,push all unvisited neighbors onto stack,repet until stack is empty
John Retallack
You can push nodes onto the stack in the parameter list. For example, one parameter could be something like this:(append neighbors (cdr stack))This represents a new stack made of the old stack minus the current node, with the neighbors pushed onto the top.
ajduff574
I suppose it's also worth mentioning that you could eliminate the inner if statement if you never push visited nodes onto the stack. I'm assuming we simply push all neighbors onto the stack.
ajduff574
I got it,but I still don't get it how this is going to solve the problem with the visited list,won't I lose elements from it when returning from recursion ?
John Retallack
I should make my last comment clearer. I'm assuming we push all neighbors onto the stack as long as the current node hasn't been visited. If you'd like to filter out the neighbors that have been visited before pushing them, you can remove the inner-most if statement, since the current node will never have been visited (remember, we get it from the stack). There's even a scheme function called filter that you could use to remove all the visited neighbors from the list of neighbors. This approach might be a bit more complicated, though.
ajduff574
It won't lose elements if you return it. If you substitute "visited" for the '() right before the first comment, the visited list as it is when you're done visiting will be returned. Since we're always returning the result of helper, this visited list will be propagated up through the recursive calls, and dft will actually return the visited list.
ajduff574
for it to substitute visited shouldn't the helper call be nested inside another helper call,something like (helper graph stack (helper...)) ?
John Retallack
No. You can imagine the calls to look something like this: dft -> helper -> helper -> helper -> helper. dft calls helper. Then helper calls helper, which calls helper, which calls helper, which returns the visited list. Since all the previous functions simply return the results of whatever they called, the visited list is passed upwards as the return value of these calls.
ajduff574
You will, however, have to change the visited list you pass into the helper functions. So one of the parameters would be something like: (cons currentNode visited).
ajduff574
I did it it's working ,except if the graph has cycles,I can't tell why is that
John Retallack
You may be passing the wrong parameters into the helper function. Make sure that if the node has been visited, you don't push the neighbors onto the stack. Also make sure that you're putting the current node into the visited list if it isn't already: (cons currentNode visited).
ajduff574
Sorry, I also just noticed a bug in my code. You may have to swap the let statement with the first if statement. I edited my answer to reflect this.
ajduff574
thanks a lot,the problems with cycles was that I didn't check to see if an unvisited node was in the stack before pushing it
John Retallack
A: 

I answered here as well.

One method of doing this is just to return the list so you have access to it at higher levels of recursion.

Another method is to have your list be stored in a variable outside of the recursion. In other words not stored on the stack. Since it is not a good idea to use a global variable for this we need to have some local recursion.

The following code is a foolish way to reverse a list but it does illustrate the technique I am talking about.

(define (letrecreverse lst)
  (letrec ((retlist '())
           (reverse (lambda (lst)
                      (if (null? lst)
                          '()
                          (begin
                            (set! retlist (cons (car lst) retlist))
                            (reverse (cdr lst)))))))
    (reverse lst)
    retlist))

(letrecreverse '(1 2 3 4))
;outputs '(4 3 2 1)

Can you adopt this technique for your purposes?

Davorak
I'm not allowed to use set
John Retallack
Then you need to be returning you list on the stack from each level so you can access it on the one above the return.
Davorak