SKILL Q: Other looping constructs besides "foreach?"

Discussion in 'Cadence' started by Edward, Jun 21, 2007.

  1. Edward

    Edward Guest

    I'm writing a few utilities to make my workflow go smoother, and I've
    just started to think about refactoring a few functions that use
    "foreach." Specifically, I'm looking for something like a "do-
    until," a foreach that stops iterating a list once it finds the
    element it's looking for. Because SKILL is a dialect of Lisp, I
    could write a recursive list-searching function, but first I would
    like to hear from anyone who is more familiar with SKILL's standard
    command set. Maybe there is a command I missed that already does what
    I want.

    Thanks,

    Edward
     
    Edward, Jun 21, 2007
    #1
  2. Edward

    jayl-news Guest

    Like while() and unless()?

    -Jay-
     
    jayl-news, Jun 22, 2007
    #2
  3. Edward

    Edward Guest

    Yes, while() is a good one, but I was looking for an iterative
    selector, a keep-looking-until-you-find-this-and-and-then-stop
    function. After perusing the SKILL reference and user-guide, I found
    exists() and this is closer to what I wanted.

    Thanks,

    Edward
     
    Edward, Jun 22, 2007
    #3
  4. Edward

    cadence Guest

    this is pretty easy with SKILL's powerful macro system.
    How about the following? untested...


    (defmacro do_until (test expr1 @rest others)
    ,expr1
    ,@others
    (unless ,test
    ,expr1
    ,@others))


    What value do you want the do_until to return? If you want it
    to return the last expression of the body that was evalutated, or
    perhaps
    the first non-nil value of test the macro will be a little more
    complicated
    but not much more. E.g., the following will return the first non-nil
    value
    of test and repeat the given body until the test is non-nil.

    (defmacro do_until (test expr1 @rest others)
    (let ((var (gensym)))
    ,expr1
    ,@others
    (unless (setq ,var ,test) ,expr1 ,@others)
    ,var))


    Example.

    (let ((x 1.1))
    (do_until (x > 100)
    (println x)
    (x = 2*x)))
     
    cadence, Jun 27, 2007
    #4
  5. There's exists(), as you said, and also member()/memq() which are effectively
    specialized forms of exists(). Another possibility is forall() which is similar
    to exists except the condition is the opposite - it keeps going while something
    is true, and it only returns t if it made it through the whole list.

    You can also do early jump-outs by using things like this:

    prog(()
    foreach(a '(1 2 3 4 5 6 7)
    println(a)
    when(a>4 return('break))
    )
    )

    (the value in the return doesn't really matter - I just returned a symbol to
    indicate the the prog was exited early).

    Personally I don't like doing the above, but in some exceptional cases it's a
    reasonable thing to do.

    Regards,

    Andrew.
     
    Andrew Beckett, Jun 27, 2007
    #5
  6. Jim,

    These are definitely untested ;-(
    You missed out the backquote, and there's no kind of looping in there. It's not
    obvious to me how you intended them to work...

    Even assuming you meant to put a `progn(...) around the code:

    (defmacro do_until (test expr1 @rest others)
    (let ((var (gensym)))
    `(progn
    ,expr1
    ,@others
    (unless (setq ,var ,test) ,expr1 ,@others)
    ,var)))

    It still doesn't work properly - it just executes the body a maximum of twice.

    Perhaps you also meant to have while (with a null on the condition) instead of
    unless?

    (defmacro do_until (test expr1 @rest others)
    (let ((var (gensym)))
    `(progn
    ,expr1
    ,@others
    (while (null (setq ,var ,test)) ,expr1 ,@others)
    ,var)))

    And there's no particular need to have expr1 and others separated:

    (defmacro do_until (test @rest others)
    (let ((var (gensym)))
    `(progn
    ,@others
    (while (null (setq ,var ,test)) ,@others)
    ,var)))

    although I guess this gives a nasty error if you don't give enough args to
    do_until, whereas the one with expr1 is clearer.

    Regards,

    Andrew.
     
    Andrew Beckett, Jun 28, 2007
    #6
  7. Edward

    cadence Guest

    hi andrew yes you're right. i wrote those before heading to bed.
    And on my pillow i realized
    some very obvious mistakes. thanks for correcting them for me.

    yes, the point of having two variables expr1 and others is to hide the
    degenerate
    case that no expressions were given. It makes it clear that at least
    one
    expression is required and that others are optional. It could be
    improved slightly:

    (defmacro do_until (test expr1 @rest body)
    ,@body
    (while (not ,test)
    nil
    ,@body))

    The nil, inserted in the while, assures that the body of the while is
    never empty, thus allowing
    a usage such as

    x=42
    (do_until (zerop (mod ++x 17)))


    thanks again andrew for clearing it up, but i think you are loging on
    too early in the
    morning (grins).

    -jim
     
    cadence, Jun 28, 2007
    #7
Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.