Arc Forumnew | comments | leaders | submitlogin
Reverse order in "for" was never intended to work ?
2 points by thaddeus 5675 days ago | 7 comments
Hi,

I presume the reverse order in "for" was never intended to work...

    arc> (for i 28 23
             (prn i))
    nil

    arc> (for i -1 -4
         (prn i))
    nil
So I hacked pg's code, not the greatest fix, but it works for all scenarios:

    (mac for2 (v start end . body)
      (w/uniq (gi gm)
         (if (> start end)
            `(with (,v nil ,gi ,start ,gm (- ,end 1))
               (loop (set ,v ,gi) (> ,v ,gm) (set ,v (- ,v 1))
                 ,@body))
            `(with (,v nil ,gi ,start ,gm (+ ,end 1))
              (loop (set ,v ,gi) (< ,v ,gm) (set ,v (+ ,v 1))
                 ,@body)))))

    arc> (for2 i 28 23
             (prn i))
    28
    27
    26
    25
    24
    23
    nil

    arc> (for2 i 23 28
      (prn i))
    23
    24
    25
    26
    27
    28
    nil

    arc> (for2 i -1 -4
      (prn i))
    -1
    -2
    -3
    -4
    nil

    arc> (for2 i -4 -1
      (prn i))
    -4
    -3
    -2
    -1
    nil
My only problem has been that when I try to substitute the 'for2" mac for the original 'for' mac,

I get an error, on loading arc, in ac.scm char 604:

    >: expects type <real number> as 2nd argument, given: (- end start 1 . nil); other arguments were: 0
and I start getting lost in all the ac.scm code.

Anyone have any ideas on what's happening here? I can always just use for2, but it would be nice to fix it well.

Please and Thanks, T.



2 points by CatDancer 5674 days ago | link

I think that would get me in trouble sometimes... in arc2:

  arc> (for i 1 2 (prn i))
  1
  2
  nil
  arc> (for i 1 1 (prn i))
  1
  nil
  arc> (for i 1 0 (prn i))
  nil
no surprises... but with your change, I'd get 0 1.

How about a variant of "for" which takes a step argument? That way if I wanted to count down I could say

  (forstep i 10 0 -1 (prn "auto-destruct in " i))

-----

2 points by thaddeus 5674 days ago | link

This will work:

    (mac forstep (v start end step . body)
        (w/uniq (gi gm)
	    (case (isnt step 0) 
              t (if (> start end)
                    `(with (,v nil ,gi ,start ,gm (- ,end (abs ,step)))
                       (loop (set ,v ,gi) (>= (- ,v (abs ,step)) ,gm) (set ,v (- ,v (abs ,step)))
                         ,@body))
                    `(with (,v nil ,gi ,start ,gm (+ ,end (abs ,step)))
                       (loop (set ,v ,gi) (<= (+ ,v (abs ,step)) ,gm) (set ,v (+ ,v (abs ,step)))
	                ,@body)))
                      nil)))
Test cases: (some input cases are just to make sure there are no infinite runs)....

    (forstep i 5 10 1 (prn i)) 
    (forstep i 5 10 2 (prn i))
    (forstep i 10 5 2 (prn i))
    (forstep i 5 10 -2 (prn i))
    (forstep i 0 1 0 (prn i))
    (forstep i 1 0 0 (prn i))
    (forstep i -2 2 1 (prn i))
    (forstep i 2 -2 1 (prn i))
    (forstep i -4 2 2 (prn i))
    (forstep i -4 2 -2 (prn i))
    (forstep i 4 -2 -2 (prn i))
    (forstep i 4 -2 2 (prn i))

That being said, I don't understand why changing 'for' this way would create problems for you, or why you want (for i 1 0 (prn i)) to return nil.

also, I still think it would be nice for arc to support keyword arguments (hint, hint pg - though he's probably not reading this), then I could have 'step' as an optional arg with a default. i.e....

    (mac for (v start end (o step 1)  . body)...

    could allow:

    (for i 20 5 (prn i)) and also something like: (for i 20 5 step:2 (prn i))
T.

-----

1 point by skenney26 5673 days ago | link

Here's an alternative version of a step macro:

  (mac step (v init end by . body)
    (w/uniq (gi ge gtest gupdate)
     `(withs (,v nil ,gi ,init ,ge ,end
              ,gtest   (if (< ,gi ,ge) <= >=)
              ,gupdate (if (< ,gi ,ge) + -))
        (loop (set ,v ,gi)
              (,gtest ,v ,ge)
              (set ,v (,gupdate ,v ,by))
          ,@body))))

-----

1 point by thaddeus 5673 days ago | link

A much better alternate version too. The redundant code in my hacked version was obvious and ugly, but I struggled reducing it to a simpler form... Thanks!

I made a few slight adjustments though, as it failed 2 of my test cases (however unlikely they are to occur, infinite runs scare me).

    (mac step (v init end by . body)
     (if (isnt by 0) 
       (w/uniq (gi ge gtest gupdate)
        `(withs (,v nil ,gi ,init ,ge ,end
                 ,gtest   (if (< ,gi ,ge) <= >=)
                 ,gupdate (if (< ,gi ,ge) + -))
           (loop (set ,v ,gi)
                 (,gtest ,v ,ge)
                 (set ,v (,gupdate ,v (abs ,by)))
             ,@body))) nil ))
now passes these two test cases:

    (step i 5 10 -2 (prn i))
    (step i 0 1 0 (prn i))
Thanks! T.

-----

1 point by CatDancer 5674 days ago | link

That being said, I don't understand why changing 'for' this way would create problems for you

Perhaps I'm doing i from 1 to the number of things I have, and sometimes I have zero things.

Actually though it was a bit silly of me to suggest "forstep" when your version of "for" was doing what you wanted it to do. It's not a version that I'd want to use in my code, but that's no reason for you not to have your version.

-----

1 point by thaddeus 5674 days ago | link

ahh, I see. gotcha. T.

-----

1 point by pg 5666 days ago | link

I don't recommend using this version. The test on the values of start and end is outside the macro expansion. That only works if they're constants.

-----