What little functions/macros do you use?

Discussion of Common Lisp
Kohath
Posts: 61
Joined: Mon Jul 07, 2008 8:06 pm
Location: Toowoomba, Queensland, Australia
Contact:

Re: What little functions/macros do you use?

Post by Kohath » Mon Mar 29, 2010 6:15 pm

So, and-let*, I presume as in http://srfi.schemers.org/srfi-2/srfi-2.html, cool, I get it, I'll try using it. Jasper, that seems like a nice implementation, except it can't handle non-variable/binding clauses.

I think the symbols named 'until and 'while might conflict, but you should be able to work around that if you really want, because neither loop nor iterate have function bindings for them.
Jasper wrote:There really isn't any source of confusion in how you arranged things in with-easy-arrays, just a little pet worry of mine. Sometimes it is just a personal preference, just like you liked 1ref, 1elt, don't think those personal preferences are very productive, by the way. It may be a distraction, especially in dealing with other people's code.
I think that they shine (true, my preference :)) when I want to write some bits of array processing code in mathematical style, and since I don't have an editor that can do LaTeX style code rendering :cry:, I'm pretty happy with things like this :mrgreen:. Having said that, perhaps they're not what you were looking for when you started the post :oops:.

About def-setf-fun, I looked at it and went - cool! I think it's great for the simple case, like some other language's getters and setters, but I can't think of a situation when I want to put anything else in body (but that doesn't mean such situations don't exist).
Dolda wrote:And finally, how comes CL doesn't come with a built-in definition of WHILE?
I have got a while in my utilities file, but I rarely use it. For me it hasn't been fear, but just that loop and other tools do a better job, one way being to provide the 'state' variables to use destructively. So I use things that kindof look like (in a loose CLHS sense :)):

Code: Select all

(loop [with state = blah] while condition do stuff do other-stuff)
;; or
(iter [(with state = blah)] (while condition) multiple forms to do stuff)
Note that while could be replaced by until if that removes a not, it's also defined in both loop and iterate.

Dolda
Posts: 4
Joined: Sun Mar 28, 2010 6:23 pm

Re: What little functions/macros do you use?

Post by Dolda » Mon Mar 29, 2010 6:44 pm

I can see your point about (loop while (...)) does the job of while. I guess I just avoid keyworded LOOPs like the plague. I find the syntax so un-Lisp-like. :)

Jasper
Posts: 209
Joined: Fri Oct 10, 2008 8:22 am
Location: Eindhoven, The Netherlands
Contact:

Re: What little functions/macros do you use?

Post by Jasper » Tue Mar 30, 2010 7:35 am

Kohath wrote:Having said that, perhaps they're not what you were looking for when you started the post :oops:.
They are little function/macros you use, aren't they? It fits the bill..

About def-setf-fun; you can put a docstring/declarations/assertions and such in there, but also just because there is no real reason not to give that freedom.

I rarely use LOOP, sometimes if i need to collect in a way mapcar and such doesn't allow me, i use

Code: Select all

(defmacro collecting ((&key (init '(list)) (onto (gensym))
                                 (collect 'collecting) (append 'appending)
                                 (last (gensym)) (append-1 (gensym)))
                      &body body)
  "Collect everything asked to, return result. (Also, appending)
If you want to use two different collectings, you need to provide the\
 collect argument.(To avoid namespace collision, and to separate the two.)"
  `(let ((,onto ,init) ,last)
     (declare (ignorable ,onto))
     (labels ((,append-1 (collected)
                (if (null ,onto)
                   (setf ,onto collected
                           ,last (last ,onto))
                   (setf (cdr ,last) collected
                         ,last (last ,last))))
                (,append (&rest appended)
                 (dolist (a appended)
                    (,append-1 a)))
              (,collect (&rest collected)
                (,append-1 collected)))
       ,@body)
     ,onto))
It looks a little involved, mostly to make the adding of elements to the end efficient. I have this idea that it is good that mapcar and such can list things back for you, but for more complicated iterators, like over quad trees, it is silly to put the plumbing of the accumulating in the functions iterating over them. Btw i changed it a little before posting it here, made the argument list &key, made the code more something i like now. Of course i also have similar accumulating, summing, etcetera macros. (And if it gets too nested, i denest it.)

That doesn't cover stuff like iterating by CDDR, though, then i often resort to DO, which isn't very neat.

One little problem i don't have a satisfactory macro for is defvars. I mean, you can declaim types of them, but then you have to enter values accordingly. I just found DEFINE-SYMBOL-MACRO, so i guess i could try make a macro that replaces the symbol with a setf-able function that automatically converts arbitrary input to the type the variable is declaimed too. Perhaps 'variable-setf-hook'. That doesn't fix anything when the variable is changed locally in LET, though which for me is the majority of cases..

karol.skocik
Posts: 10
Joined: Tue Sep 22, 2009 4:50 pm

Re: What little functions/macros do you use?

Post by karol.skocik » Sat Apr 03, 2010 12:38 am

I sometimes use a handy macro which allows me to construct lists in order (without need to be reversed).
Also, it has a nice feature - keeps size of the list - no need to #'length at the end when you need it.
And the best features at the end - it gives you a hand on last and previous to last elements which
is very handy when you plan to destructively add stuff to the end, without copying anything.
Also - it allows you to supply a list to work on as an argument, when you need to use it on something already made.

Code: Select all

(defun before-last (list)
  (if (cddr list)
      (before-last (cdr list))
      list))

(defmacro nconcing ((&key (init nil)
                          (into 'nconc-result)
                          (call 'nconc-it)
                          (count nil)
                          (last nil)
                          (before-last nil))
                    &body body)
  (let ((head-sym (gensym "HEAD"))
        (tail-sym (gensym "TAIL")))
    `(let* ((,head-sym (cons nil ,init))
               (,tail-sym (last ,head-sym))
               (,into (cdr ,head-sym))
               ,@(when count `((,count (length ,init))))
               ,@(when last `((,last nil)))
               ,@(when before-last `((,before-last (before-last ,init)))))
       (flet ((,call (x)
                ,@(when before-last
                        `((setf ,before-last 
                                (unless (eq ,head-sym ,tail-sym)
                                  ,tail-sym))))
                (rplacd ,tail-sym (setf ,tail-sym (list x)))
                (setf ,into (cdr ,head-sym))
                ,@(when last `((setf ,last ,tail-sym)))
                ,@(when count `((incf ,count)))))
         ,@body))))
before-last is used only when you supply list as an argument and you require to get hands on before to last element.

gugamilare
Posts: 406
Joined: Sat Mar 07, 2009 6:17 pm
Location: Brazil
Contact:

Re: What little functions/macros do you use?

Post by gugamilare » Sat Apr 03, 2010 8:35 am

karol.skocik wrote:

Code: Select all

(defun before-last (list)
  (if (cddr list)
      (before-last (cdr list))
      list))
You can use (last list 2) instead:

Code: Select all

CL-USER> (last '(1 2 3 4 5 6) 2)
(5 6)

karol.skocik
Posts: 10
Joined: Tue Sep 22, 2009 4:50 pm

Re: What little functions/macros do you use?

Post by karol.skocik » Sat Apr 03, 2010 10:54 am

gugamilare wrote:
karol.skocik wrote:

Code: Select all

(defun before-last (list)
  (if (cddr list)
      (before-last (cdr list))
      list))
You can use (last list 2) instead:

Code: Select all

CL-USER> (last '(1 2 3 4 5 6) 2)
(5 6)
Thanks! Didn't know that.
Karol

Jasper
Posts: 209
Joined: Fri Oct 10, 2008 8:22 am
Location: Eindhoven, The Netherlands
Contact:

Re: What little functions/macros do you use?

Post by Jasper » Sun Apr 04, 2010 6:24 am

Hmm, never really needed the previous of the last of the list. Counting is a nice touch, but i do prefer give things one purpose, of course you could do:

Code: Select all

(collecting (:onto your-list)
  (let ((cnt 0) (before-last (last your-list 2)))
    (flet ((collecting (element)
             (setq cnt (+ cnt 1)
                   before-last (or (cdr before-last) (last your-list 2)))
             (collecting element))) ;Since it is a flet, defers to the collecting created from macro collecting.
      ....)
That is a bunch longer though, but then again, the user can make his/her own macros to do this. collecting-with-count, collecting-with-before-last or something.. Btw, both the name of the macro and the name of the flet is collecting, it works, but now i think about it, is an accident waiting to happen!

Btw, how do you prevent lisp whining about the return of collecting to be not accessible sometimes? Hmm, maybe it might be confusing about collecting that it returns what it collected, as opposed to the last expression in the body in the first place..

karol.skocik
Posts: 10
Joined: Tue Sep 22, 2009 4:50 pm

Re: What little functions/macros do you use?

Post by karol.skocik » Mon Apr 05, 2010 1:28 am

Jasper wrote:Hmm, never really needed the previous of the last of the list. Counting is a nice touch, but i do prefer give things one purpose, of course you could do:

Code: Select all

(collecting (:onto your-list)
  (let ((cnt 0) (before-last (last your-list 2)))
    (flet ((collecting (element)
             (setq cnt (+ cnt 1)
                   before-last (or (cdr before-last) (last your-list 2)))
             (collecting element))) ;Since it is a flet, defers to the collecting created from macro collecting.
      ....)
That is a bunch longer though, but then again, the user can make his/her own macros to do this. collecting-with-count, collecting-with-before-last or something.. Btw, both the name of the macro and the name of the flet is collecting, it works, but now i think about it, is an accident waiting to happen!

Btw, how do you prevent lisp whining about the return of collecting to be not accessible sometimes? Hmm, maybe it might be confusing about collecting that it returns what it collected, as opposed to the last expression in the body in the first place..
I use before-last to remove last element quickly as a list post-processing or other manipulating of the list's tail without traversing. http://github.com/ks/X.FDATATYPES/blob/ ... c-ctx.lisp has some uses of that.
Also, these last, before-last and count are expanded only when macro user uses them. The link above has some uses of all 3 at the same time, so I guess separating each functionality into specific macro would prevent usage of their combinations. But, like I said - their functionality is there only when needed.

Jasper
Posts: 209
Joined: Fri Oct 10, 2008 8:22 am
Location: Eindhoven, The Netherlands
Contact:

Re: What little functions/macros do you use?

Post by Jasper » Sun Apr 11, 2010 1:24 pm

I guess my contentions that it should be one-purpose is rather petty, so i added those features to collecting (and collect is now the name of the collector inside.) There are still a lot of naming conventions differing.. nconcing vs collecting, into vs onto, conc-it vs collect etcetera. I also have the keyword :ret; whether to return the collected result. (can save a line, not sure if it is that nice though.)

Just to make my macro-set closer in terms to that of Iterate, i made a little iterator to iterate in parallel. Basically a form and a body, where in the form you specify how to iterate in paralel with expressions like (:do var init change) (:until until), (:range var from to &optional (by 1)), (:list var list &optional (by 'cdr)), (:array ...) ~73 lines plus newer collecting.(and there is a macro to add those things) I don't see loose macros doing things handily in parallel together. Example:

Code: Select all

(collecting (:ret t)
  (do-parallel ((:range i 1 +5) (:range j 1 +5))
    (collect (list i j))));Gives (1 1) (2 2) (3 3) (4 4) (5 5) possibilities
I don't think i have any more macros to mention. Did still want to say that i don't like long docstrings like Kohath did with the With-arrefers, nor long commented bits with your life story ontop of source files.(Unless it is a one-file package, perhaps) The thing is that if you accept those, you might accepting long code too, and it rather distracts from the code.

Extensive/wordy documentation is fine, but you can use little side-files for that, and that Documentation is also setf-able.(well done CL!) So you can do that in separate files aswel.

karol.skocik
Posts: 10
Joined: Tue Sep 22, 2009 4:50 pm

Re: What little functions/macros do you use?

Post by karol.skocik » Mon Apr 12, 2010 1:23 am

I guess this one is also useful sometimes:
Beware - it uses my let-star library. To remove dependency - replace `(let* ((,dim-syms (array-dimensions ,matrix)))
with `(destructuring-bind (,dim-syms) (array-dimensions ,matrix) ....)

Code: Select all

(defmacro do-matrix-indices (indices matrix &body body)
  (let ((indices-length (length indices)))
    (assert (and (not (zerop indices-length))
                 (every #'symbolp indices)
                 (eql indices-length (length (remove-duplicates indices))))
            nil "invalid indices, expected unique symbols")
    (let ((dim-syms (mapcar (lambda (i) (gensym (format nil "~A-" i))) indices)))
      (labels ((rec (rem-indices rem-dim-syms)
                 (if rem-dim-syms
                     `(dotimes (,(car rem-indices) ,(car rem-dim-syms))
                        ,@(list (rec (cdr rem-indices) (cdr rem-dim-syms))))
                     `(progn ,@body))))
        `(let* ((,dim-syms (array-dimensions ,matrix)))
           (declare (dynamic-extent ,@dim-syms))
           ,(rec indices dim-syms))))))

Post Reply