Return Styles: Pseud0ch, Terminal, Valhalla, NES, Geocities, Blue Moon. Entire thread

A student's question

Name: Anonymous 2011-05-30 12:20

My teacher keeps drilling into our collective heads that using the break command is harmful, and that if we wish to terminate a loop early it would be far better to create a boolean variable, set it to 0, and add a condition to the head of the loop, and set the boolean variable to 1 when a break is needed. Note that this way you actually have to check this every single loop (where it is not needed almost every time) as well as waste a command to reset the boolean in case it was set to true.

Is there a reason to this?

Name: Anonymous 2011-05-30 12:27

dont question u tutor u piece of shit

Name: Anonymous 2011-05-30 12:28

That's stupid. Use break.

Name: Anonymous 2011-05-30 12:31

our collective heads
So the set of student-tasks is multiplexed onto a smaller set of kernel-schedulable heads, or what?

Name: Anonymous 2011-05-30 12:36

>>3
Thought so, but wanted to make sure I wasn't missing some deep magic. Thank you.
>>4
I have to admit, I lol'd

Name: Anonymous 2011-05-30 12:44

It's about source code clarity. Ensuring loops and functions have only one entry point and only one exit point keeps control flow simple and lessens the risk of introducing bugs when modifying the code.

It's the kind of rule that you're likely to see in large projects where it's expected someone else will have to do maintenance on your code years after you've left the company.

Name: Anonymous 2011-05-30 13:16

Hilarious, it must suck being taught by such a fucking idiot. At least they're not a women.

Name: Anonymous 2011-05-30 13:18

>>7
Weird, I assumed they were a women when I first read >>1.

Name: Anonymous 2011-05-30 13:20

...She is a woman, yes.

Name: Anonymous 2011-05-30 13:21

>>1
Your teacher is a idiot. Let's say I have a Devyrish piece of code that determines if a number is prime..

boolean isPrime = true;

for (int divisor = 2; divisor <= number/2 && isPrime; divisor++) {
  if (number % divisor == 0) {
    isPrime = false:
  }
}

Now let's say I wanna be like the SICP fags that roam this joint and abstract this loop. What do I abstract? Divisor or isPrime?

The answer becomes a bit clearer if I would re-write this loop using a break statement.

Name: Anonymous 2011-05-30 13:23

>>9
oh well that explains it...

just ignore them then. If they try to get on at you for not doing what they say slap her and tell her to "know her fucking place". Works a charm.

Name: Anonymous 2011-05-30 13:28

>>10
You could abstract the second clause in the for(;;) to be divisor <= number/divisor

Name: Anonymous 2011-05-30 13:35

Use goto.

Name: Anonymous 2011-05-30 13:35

>>12
And let's say that I wanted do what is sometimes called "method abstraction".

Using boolean values, it would be hard to tell if the method should be called divisor() or isPrime(). Whereas if I re-wrote this looping using a break statement, I could pretty much tell right away that the method would be called isPrime().

Name: Anonymous 2011-05-30 14:59

>>13
His teacher would probably get an aneurysm.

Name: Anonymous 2011-05-30 16:21

>>1
Yes. Break is harmful. You should read SICP and use continuations instead of loops with break/continue.

Name: Anonymous 2011-05-30 16:35

>>16
No. SICP is harmful. You should use break and continue instead of Lisp faggotry.

Name: Anonymous 2011-05-30 16:42

>>17
Young man, you have learned a little bit and think you knows it all, but soon you will grow out of your sophomoric sophistication and come to realize that the world is more complicated, and you will begin again to understand that LISP is The Right Thing.

Name: Anonymous 2011-05-30 16:45

>>18
What can LISP offer over C?

Name: Anonymous 2011-05-30 16:48

>>18
no, lisp is for faggots

Name: Anonymous 2011-05-30 16:49

>>19
macros.

Name: Anonymous 2011-05-30 16:50

>>19
functionality

Name: Anonymous 2011-05-30 17:00

>>21
Consider me an ignorant barbarian for a moment (because I largely am when it comes to LISP), and explain to me the virtues and advantages of macros.

Name: Anonymous 2011-05-30 17:15

>>16-23
But Scheme has break and continue! They are called call-with-current-continuation!
This should be portable R6RS code, but it is untested
(define-syntax while
  (lambda (stx)
    (syntax-case stx ()
      ((while cond body body+ ...)
       (with-syntax ((break (datum->syntax #'body 'break))
                     (continue (datum->syntax #'body 'continue)))
         #'(let loop ()
             (call-with-current-continuation
              (lambda (break)
                (let loop ()
                  (when cond
                    (call-with-current-continuation
                     (lambda (continue)
                       body body+ ...))
                    (loop)))))))))))

(define-syntax for
  (lambda (stx)
    (syntax-case stx ()
      ((for init cond incr body body+ ...)
       (with-syntax ((break (datum->syntax #'body 'break))
                     (continue (datum->syntax #'body 'continue)))
         #'(call-with-current-continuation
            (lambda (break)
              init
              (let loop ()
                (when cond
                  (call-with-current-continuation
                   (lambda (continue)
                     body body+ ...))
                  incr (loop))))))))))

;(while #t (display "read ") (display "SICP "))
; => read SICP read SICP read SICP
;(while #t (display "read ") (continue) (display "SICP"))
; => read read read read ...
;(while #t (display "read ") (break) (display "SICP"))
; => read


Yes, continue does the right thing in for (executes incr).
If you had read your SICP today, you would've known. You also would known that all the call-with-current-continuations can be optimized to call-with-escape-continuations.

Name: Anonymous 2011-05-30 17:19

>>1
this:

for(int ii=0; ii<1000; ii++) {
    for(int jj=0; jj<1000; jj++) {
        for(int kk=0; kk<1000; kk++) {
            if(wantToBreak) goto loopEnd;
        }
    }
}
loopEnd:
...

is by some considered bad. However i prefer this solution over:

needToBreak = false
for(int ii=0; ii<1000 && !needToBreak; ii++) {
    for(int jj=0; jj<1000 && !needToBreak; jj++) {
        for(int kk=0; kk<1000 && !needToBreak; kk++) {
            if(wantToBreak) needToBreak = true;
        }
    }
}

and definitely instead of

needToBreak = false
for(int ii=0; ii<1000 && !needToBreak; ii++) {
    for(int jj=0; jj<1000 && !needToBreak; jj++) {
        for(int kk=0; kk<1000 && !needToBreak; kk++) {
            if(wantToBreak) {
                needToBreak = true;
                break
            }
        }
        if(needToBreak) break;
    }
    if(needToBreak) break;
}


bottom line: break is fine as long as you want to break from non-nested loop

Name: Anonymous 2011-05-30 17:23

>>25
The last one is 50% redundant. How about:
il: for(int ii=0; ii<1000; ii++) {
    jl: for(int jj=0; jj<1000; jj++) {
        kl: for(int kk=0; kk<1000; kk++) {
            if (wantToBreak) break il;
        }
    }
}

Name: Anonymous 2011-05-30 17:25

You should just use perl and next labels.

Name: Anonymous 2011-05-30 17:29

>>24
Optimized Racket version:

(define-syntax-parameter break
  (lambda (stx)
    (raise-syntax-error 'break "not in a loop")))
(define-syntax-parameter continue
  (lambda (stx)
    (raise-syntax-error 'continue "not in a loop")))

(define-syntax-rule (let-stxparam ((x y) ...) . body)
  (syntax-parameterize ((x (syntax-rules () ((x) (y)))) ...)
    . body))
(define-syntax-rule (let/ec-stxparam x . body)
  (let/ec k
    (let-stxparam ((x k)) . body)))

(define-syntax-rule (for init cond incr . body)
  (let/ec-stxparam break
    init
    (let loop ()
      (when cond
        (let/ec-stxparam continue . body)
        incr (loop)))))
(define-syntax-rule (while cond . body)
  (for (void) cond (void) . body))

Name: Anonymous 2011-05-30 17:30

>>26
we're speaking about c, c++ or java?

Name: Anonymous 2011-05-30 17:33

>>29
aight, no bool / boolean in c so that leaves c++/java.
but c++ does not support break with labels so java

Name: Anonymous 2011-05-30 17:35

>>26
They are all call-with-escape-continuation. That may be implemented with CL's block+return-from too (same concept).

(define-syntax for/break-at
  (syntax-rules ()
    ((_ name init cond body . rest)
     (call-with-current-continuation
      (lambda (name) (for init cond body . rest))))))
;; short one
(define-syntax-rule (for/break-at n . rest)
  (let/ec n (for . rest)))

(for/break-at il (set! ii 0) (< ii 1000) (inc! ii)
 (for/break-at jl (set! jj 0) (< jj 1000) (inc! jj)
  (for/break-at kl (set! kk 0) (< kk 1000) (inc! kk)
   (when break? (il)))))


It looks bad, though, but that's because I'm just forcing C-style for into Scheme.

Name: Anonymous 2011-05-30 17:42

>>31
Also, an implementation of CL's tagbody (not exactly, same semantics, different syntax) in Racket, but it should be easily portable to R6RS too:

(define-syntax-parameter goto
  (lambda (stx)
    (raise-syntax-error 'goto (if (identifier? stx) "bad syntax" "bad syntax (not inside a tagbody)") stx)))

(define-syntax (tagbody stx)
  (define (%tag-next x)
    (let ((x (syntax->list x)))
      (do ((x x (cdr x))
           (y (cdr x) (cdr y))
           (r (list (car x)) (cons (list (car x) (car y)) r)))
        ((null? y) (reverse (cons (list (car x) #'void) r))))))
  (syntax-case stx ()
    ((tagbody (tag b ...) ...)
     (andmap identifier? (syntax->list #'(tag ...)))
     (with-syntax (((first (tag next) ...) (%tag-next #'(tag ...))))
       #'(letrec ((tag
                   (lambda ()
                     (let/ec escape
                       (syntax-parameterize
                           ((goto (lambda (stx)
                                    (syntax-case stx ()
                                      ((goto label)
                                       (if (and (identifier? #'label)
                                                (or (free-identifier=? #'label #'tag) ...))
                                           #'(escape label)
                                           (raise-syntax-error 'goto "bad syntax (not a valid label)" stx #'label)))))))
                         b ...
                         (next))))) ...)
           (let loop ((f (first)))
             (if (procedure? f) (loop (f)) f)))))))


;; Example taken from the HyperSpec: http://www.lispworks.com/documentation/HyperSpec/Body/s_tagbod.htm
(define-syntax-rule (inc! x n) (set! x (+ x n)))

(let ((val #f))
  (tagbody
   (start
    (set! val 1)
    (goto point-a)
    (inc! val 16))
   (point-c
    (inc! val 4)
    (goto point-b)
    (inc! val 32))
   (point-a
    (inc! val 2)
    (goto point-c)
    (inc! val 64))
   (point-b
    (inc! val 8)))
  val)
; 15


Really, more languages should provide some non-local exit mechanism, and not in the form of exceptions.

Name: Anonymous 2011-05-30 18:04

>>23
Macro systems have a range of uses. Being able to choose the order of evaluation (see lazy evaluation and non-strict functions) enables the creation of new syntactic constructs (e.g. control structures) indistinguishable from those built into the language. For instance, in a Lisp dialect that has cond but lacks if, it is possible to define the latter in terms of the former using macros.

Next, macros make it possible to define data languages that are immediately compiled into code, which means that constructs such as state machines can be implemented in a way that is both natural and efficient.[8]

Macros can also be used to introduce new binding constructs. The most well-known example is the transformation of let into the application of a function to a set of arguments.

Felleisen conjectures[9] that these three categories make up the primary legitimate uses of macros in such a system.

Name: Anonymous 2011-05-30 18:07

>>33
where are the footnotes, faggot?

Name: Anonymous 2011-05-30 18:18

>>25
That's pretty much the situation. Could you explain why the first is considered bad by some? Is it more or less what >>6 said, or is there a more mechanical reason?

Name: Anonymous 2011-05-30 18:21

>>32
And try-catch-finally-style exceptions:
And this, >>23, is why macros and continuations are important: I, alone, in less than one hour, have written what takes ages in other languages.

(define exception-handler
  (make-parameter (lambda (x) (error "uncaught exception:" x))))

(define (throw x)
  ((exception-handler) x))

(define-syntax try
  (syntax-rules (catch else finally)
    ((try body ... (catch exception (predicate . pbody) ...) (finally . fbody))
     (let* ((finally (lambda () . fbody))
            (old-throw (exception-handler))
            (handler (lambda (exception)
                       (let ((x (cond ((predicate exception) . pbody) ...
                                      (else (finally) (old-throw exception)))))
                         (finally) x))))
       (let/ec throw-cont
         (let ((x (parameterize ((exception-handler (lambda (x) (throw-cont (handler x)))))
                    body ...)))
           (finally) x))))
    ((try body ... (catch exception (predicate . pbody) ...))
     (try body ... (catch exception (predicate . pbody) ...) (finally (void))))
    ((try body ... (finally . fbody))
     (try body ... (catch e) (finally . fbody)))))

(try
 (try
  2
  (throw 3)
  4
  (catch e
    (string? (display "not here")))
  (finally (display "finally")))
 4
 (catch e
   (number? (display " here") e)))

; output: finally here
; returns: 3

Name: Anonymous 2011-05-30 18:57

>>30
For every numeric type, C has 0 is false and everything else is true.
>>31
This is cool beans. I wish I knew more lisps ;_;

Name: Anonymous 2011-05-30 19:02

>>36
That code sucks in terms of trying to get your point across.

Name: Anonymous 2011-05-30 19:04

>>24
You get a F for the lame attempt at an explanation. Ever hear of being concise and to the point you retard? Given that long and verbose shit code, I bet not.

Name: Anonymous 2011-05-30 19:07

>>38
It sucks pretty hard actually, I know that, but I wanted to avoid set!/boxes/mutation.

>>37
Only Java has labeled breaks, though. C# has goto.

Newer Posts
Don't change these.
Name: Email:
Entire Thread Thread List