Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scheme early "short circuit return"?

I'm trying to find out how I can do an "early return" in a scheme procedure without using a top-level if or cond like construct.

(define (win b)
 (let* ((test (first (first b)))
        (result (every (lambda (i) (= (list-ref (list-ref b i) i) test))
                       (enumerate (length b)))))
  (when (and (not (= test 0)) result) test))
 0)

For example, in the code above, I want win to return test if the when condition is met, otherwise return 0. However, what happens is that the procedure will always return 0, regardless of the result of the when condition.

The reason I am structuring my code this way is because in this procedure I need to do numerous complex checks (multiple blocks similar to the let* in the example) and putting everything in a big cond would be very unwieldy.

like image 926
Suan Avatar asked Mar 12 '10 16:03

Suan


2 Answers

Here is how to use call/cc to build return yourself.

(define (example x)
  (call/cc (lambda (return)
    (when (< x 0) (return #f))
    ; more code, including possible more calls to return
    0)))

Some Schemes define a macro called let/cc that lets you drop some of the noise of the lambda:

(define (example x)
  (let/cc return
    (when (< x 0) (return #f))
    0))

Of course if your Scheme doesn't, let/cc is trivial to write.


This works because call/cc saves the point at which it was called as a continuation. It passes that continuation to its function argument. When the function calls that continuation, Scheme abandons whatever call stack it had built up so far and continues from the end of the call/cc call. Of course if the function never calls the continuation, then it just returns normally.

Continuations don't get truly mind-bending until you start returning them from that function, or maybe storing them in a global data structure and calling them later. Otherwise, they're just like any other language's structured-goto statements (while/for/break/return/continue/exceptions/conditions).


I don't know what your complete code looks like, but it might be better to go with the cond and to factor out the complex checks into separate functions. Needing return and let* is usually a symptom of overly imperative code. However, the call/cc method should get your code working for now.

like image 82
Nathan Shively-Sanders Avatar answered Nov 09 '22 23:11

Nathan Shively-Sanders


One way would be to use recursion instead of looping, then an early exit is achieved by not recursing further.

like image 35
cobbal Avatar answered Nov 09 '22 23:11

cobbal