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

Pages: 1-

ACONDTIMES

Name: Anonymous 2010-10-01 20:40

How do you like my new macro?

(defmacro acondtimes ((var count) test &body then)
  (if (plusp count)
      (let ((sym (gensym)))
        `(let* ((,var ,count)
                (,sym ,test))
           (if ,sym
               (let ((it ,sym)) ,@then)
               (acondtimes (,var ,(1- count)) ,test ,@then))))
      nil))


Hardly tested; rude bug reports expected.

Name: Anonymous 2010-10-01 21:22

Yup, that happened.
(defmacro acondtimes ((var count &optional (start 0)) test &body then)
  (if (< start count)
      (let ((sym (gensym)))
        `(let* ((,var ,start)
                (,sym ,test))
           (if ,sym
               (let ((it ,sym)) ,@then)
               (acondtimes (,var ,count ,(1+ start)) ,test ,@then))))
      nil))

Name: Anonymous 2010-10-01 21:31

Things I don't like about this macro.

1. I don't see why you need to bind var at all.
2. binding it in the body (I'm a dirty Schemer who prefers =>)
3. The fact that the test is not a pure function.

Name: Anonymous 2010-10-01 21:32

s/function/expression/

Name: Anonymous 2010-10-01 21:40

>>3
scratch 1. I get it now. You want to do
(acondtimes (var 3) (=2p var) `(success on ,var with ,it))
or whatever, and have that work "as expected"

I'm afraid I don't see the use for it in general programming.

Name: Anonymous 2010-10-02 3:26

>>5
Yeah, that's what I'm doing. I think I'll need it a few times in the near future, so here it is. Not sure why you'd want test to be a pure function.

But what is =>?

Name: Anonymous 2010-10-02 7:28

>>6
Well, I don't tend to think of side effecting functions as ideal candidates for predicates (usually because I have them return an unspecified value), and I consider the pattern here to be more clearly expressed with a loop.
=> is a literal used in Scheme's cond form that extracts the value returned by the test (performed here by it, but it does not introduce a new binding. Instead, it calls the body, which must be a procedure, with the returned value as its argument.
An example may be clearer.
(cond ((memp positive? '(-4 -3 -2 -1 0 1 2 3 4))
        =>
        (lambda (x) `(success with ,x)))
       (else "doesn't reach here"))

gives
(success with (1 2 3 4))

Name: Anonymous 2010-10-02 15:50

If you want to eliminate it, just take a new argument with the symbol name to use. It will be slightly more verbose, but it's what I usually use to avoid that minor hygiene problem.

Name: Anonymous 2010-10-02 18:41

>>8
Eliminate what? The macro does take an argument with the symbol name to use.

Name: Anonymous 2010-10-02 18:50

>>9
the ``it'' symbol?

Name: Anonymous 2010-10-02 19:14

>>10
Oh, that. Last thing I'd want to eliminate.

Name: Anonymous 2010-10-03 0:23

I don't really see a need for it, or why would you implement it in such a verbose manner, the first is roughtly equivalent to:
(dotimes (,var ,count) (awhen ,test (return (progn ,@body))))
while the second is roughly equivalent to
(loop for ,var from ,start to ,count do (awhen ,test (return (progn ,@body))))

Of course, that's just in a macro, however in real code, some of those do usually go away (progn or the return, or even the awhen).

It took me longer to understand what your macro was doing than to write that code, however if this pattern is very common for your code, it might be worth using the macro.

There's only a few problems with it:
- The macro can generate a lot of duplicate code for larger values of count
- It will only work for constant count's, while my LOOP or DOTIMES version would work with variable count's too.
- Binding it to the result of the test (anaphoric ``it'') is a hygiene problem, but some people are fine with it. A hygienic solution is to pass the name of the variable you want binded in an argument (the right way to call such a macro would be bcondtimes).
- I don't really have >>3's third complaint, however a functional version of this macro is possible (and it's also considered an acceptable macro technique), it would go like this:


;;; well-known anaphoric when implementation
(defmacro awhen (test &body body)
  `(let ((it ,test)) (when it ,@body)))

;;; functional version of condtimes
(defun condtimes* (count test then &optional (start 0))
  (loop for var from start to count do
    (awhen (funcall test count) (return (funcall then var it)))))

;;; condtimes implemented through the functional version
(defmacro acondtimes ((var count &optional (start 0)) test &body then)
  `(condtimes* ,count
                 #'(lambda (,var) ,test)
                 #'(lambda (,var it)
                       (declare (ignorable it))
                       ,@then)
                 ,start))

Name: Anonymous 2011-02-03 6:52

Name: Anonymous 2013-01-19 14:32

/prog/ will be spammed continuously until further notice. we apologize for any inconvenience this may cause.

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