Conceptional problem with macros

  • Follow


I would like to replace some repeated code with a macro or somewhat.

(let ((xx '("str1"
              "str2" "str3"
              "str2" "str3"
              "str1"
              "str2" "str3"
              "str2" "str3"
              "str1")))

Is there a possibility to abstract away some groups of strings with a
macro or something else, like

(defmacro str-group ()
  (... ?))

(defmacro long-macro-definition ()
  (code ...
    (let ((xx '("str1"
                  (str-group)
                  "str1"
                  (str-group)
                  "str1")))
    code ...)))

Example from cl-gtk2 tree-view
(let ((l (make-instance 'list-store :column-types '("gint"
 "gfloat" "gchararray" "gint"))))

thank you
hans
0
Reply schatzer.johann (29) 3/17/2011 5:31:52 AM

On Wed, 16 Mar 2011 22:31:52 -0700, hans wrote:

> I would like to replace some repeated code with a macro or somewhat.
> 
> (let ((xx '("str1"
>               "str2" "str3"
>               "str2" "str3"
>               "str1"
>               "str2" "str3"
>               "str2" "str3"
>               "str1")))
> 
> Is there a possibility to abstract away some groups of strings with a
> macro or something else, like
> 
> (defmacro str-group ()
>   (... ?))
> 
> (defmacro long-macro-definition ()
>   (code ...
>     (let ((xx '("str1"
>                   (str-group)
>                   "str1"
>                   (str-group)
>                   "str1")))
>     code ...)))
> 
> Example from cl-gtk2 tree-view
> (let ((l (make-instance 'list-store :column-types '("gint"
>  "gfloat" "gchararray" "gint"))))
> 
> thank you
> hans

I don't really see why you need a macro for this.  You can just use
CL's built-in list-manipulation functions to construct lists like
this, or maybe backquote:

(let* ((group '("str2" "str3" "str2" "str3"))
       (xx `("str1"
             ,@group
             "str1"
             ,@group
             "str1")))
  ...)

Best,

Tamas
0
Reply tkpapp (981) 3/17/2011 7:05:41 AM


hans <schatzer.johann@gmail.com> writes:

> I would like to replace some repeated code with a macro or somewhat.
>
> (let ((xx '("str1"
>               "str2" "str3"
>               "str2" "str3"
>               "str1"
>               "str2" "str3"
>               "str2" "str3"
>               "str1")))

from: 

>     (let ((xx '("str1"
>                   (str-group)
>                   "str1"
>                   (str-group)
>                   "str1")))


> Is there a possibility to abstract away some groups of strings with a
> macro or something else, like

No, there's no possibility.

Macros are functions that transform code (or code chunks) into code.

Here you have data, and you want to transform it into data.

So you CANNOT use a macro, you MUST use a function.  

For example, you could write something like:

    (expand-data-substitutions
     '(((str1                   "str1")
       (group23                 "str2" "str3")
       (group-str1-group23{2}   str1 group23 group23))
      (group-str1-group23{2} group-str1-group23{2} str1)))
    --> ("str1" "str2" "str3" "str2" "str3" "str1" "str2" "str3" "str2" "str3" "str1")


But in fact, for this problem you already have all you need in CL,
there's no need to design any complex kludge:

    (let* ((str1                   "str1")
           (group23                '("str2" "str3"))
           (group-str1-group23{2}  `(,str1 ,@group23 ,@group23)))
      `(,@group-str1-group23{2} ,@group-str1-group23{2} ,str1))
    --> ("str1" "str2" "str3" "str2" "str3" "str1" "str2" "str3" "str2" "str3" "str1")

so you can write:

    (let ((xx (let* ((str1                   "str1")
                     (group23                '("str2" "str3"))
                     (group-str1-group23{2}  `(,str1 ,@group23 ,@group23)))
                `(,@group-str1-group23{2} ,@group-str1-group23{2} ,str1))))
      ...)



Remember, 

          code --> code   = macro
          data --> data   = function


(there are functions hidden behind backquote and comma).

-- 
__Pascal Bourguignon__                     http://www.informatimago.com/
A bad day in () is better than a good day in {}.
0
Reply pjb (7667) 3/17/2011 7:21:48 AM

On Mar 17, 8:21=A0am, "Pascal J. Bourguignon" <p...@informatimago.com>
wrote:
> hans <schatzer.joh...@gmail.com> writes:
> > I would like to replace some repeated code with a macro or somewhat.
>
> > (let ((xx '("str1"
> > =A0 =A0 =A0 =A0 =A0 =A0 =A0 "str2" "str3"
> > =A0 =A0 =A0 =A0 =A0 =A0 =A0 "str2" "str3"
> > =A0 =A0 =A0 =A0 =A0 =A0 =A0 "str1"
> > =A0 =A0 =A0 =A0 =A0 =A0 =A0 "str2" "str3"
> > =A0 =A0 =A0 =A0 =A0 =A0 =A0 "str2" "str3"
> > =A0 =A0 =A0 =A0 =A0 =A0 =A0 "str1")))
>
> from:
>
> > =A0 =A0 (let ((xx '("str1"
> > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 (str-group)
> > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "str1"
> > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 (str-group)
> > =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "str1")))
> > Is there a possibility to abstract away some groups of strings with a
> > macro or something else, like
>
> No, there's no possibility.
>
> Macros are functions that transform code (or code chunks) into code.
>
> Here you have data, and you want to transform it into data.
>
> So you CANNOT use a macro, you MUST use a function. =A0
>
> For example, you could write something like:
>
> =A0 =A0 (expand-data-substitutions
> =A0 =A0 =A0'(((str1 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "str1")
> =A0 =A0 =A0 =A0(group23 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "str2" "str3")
> =A0 =A0 =A0 =A0(group-str1-group23{2} =A0 str1 group23 group23))
> =A0 =A0 =A0 (group-str1-group23{2} group-str1-group23{2} str1)))
> =A0 =A0 --> ("str1" "str2" "str3" "str2" "str3" "str1" "str2" "str3" "str=
2" "str3" "str1")
>
> But in fact, for this problem you already have all you need in CL,
> there's no need to design any complex kludge:
>
> =A0 =A0 (let* ((str1 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "str1")
> =A0 =A0 =A0 =A0 =A0 =A0(group23 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0'("str2" "=
str3"))
> =A0 =A0 =A0 =A0 =A0 =A0(group-str1-group23{2} =A0`(,str1 ,@group23 ,@grou=
p23)))
> =A0 =A0 =A0 `(,@group-str1-group23{2} ,@group-str1-group23{2} ,str1))
> =A0 =A0 --> ("str1" "str2" "str3" "str2" "str3" "str1" "str2" "str3" "str=
2" "str3" "str1")
>
> so you can write:
>
> =A0 =A0 (let ((xx (let* ((str1 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 "str1"=
)
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(group23 =A0 =A0 =A0 =A0 =A0 =
=A0 =A0 =A0'("str2" "str3"))
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0(group-str1-group23{2} =A0`(,s=
tr1 ,@group23 ,@group23)))
> =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 `(,@group-str1-group23{2} ,@group-str1-gr=
oup23{2} ,str1))))
> =A0 =A0 =A0 ...)
>
> Remember,
>
> =A0 =A0 =A0 =A0 =A0 code --> code =A0 =3D macro
> =A0 =A0 =A0 =A0 =A0 data --> data =A0 =3D function
>
> (there are functions hidden behind backquote and comma).
>
> --
> __Pascal Bourguignon__ =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0http://www.=
informatimago.com/
> A bad day in () is better than a good day in {}.


Thank you very much, this worked well within the string list.

In a similar problem the "group" is now a collection of macro calls
within a function or method call, always from a cl-gtk2 example.

(list-store-insert-with-values l n
   (macro-a 1)
   (macro-b 1)
   (macro-a 2)
   (macro-b 2)

   (macro-a 3)
   (macro-b 3)
   (macro-a 4)
   (macro-b 4))

How can you convert this into something like

(defmacro macro ()
   ...?)

(list-store-insert-with-values l n
   (macro 1 1 2 2)
   (macro 3 3 4 4))

; or even better
(list-store-insert-with-values l n
   (macro 1 2)
   (macro 3 4))
;

Thanks

0
Reply schatzer.johann (29) 3/17/2011 2:29:55 PM

hans <schatzer.johann@gmail.com> writes:

> On Mar 17, 8:21�am, "Pascal J. Bourguignon" <p...@informatimago.com>
> wrote:
>> hans <schatzer.joh...@gmail.com> writes:
>> > I would like to replace some repeated code with a macro or somewhat.
>>
>> > (let ((xx '("str1"
>> > � � � � � � � "str2" "str3"
>> > � � � � � � � "str2" "str3"
>> > � � � � � � � "str1"
>> > � � � � � � � "str2" "str3"
>> > � � � � � � � "str2" "str3"
>> > � � � � � � � "str1")))
>>
>> from:
>>
>> > � � (let ((xx '("str1"
>> > � � � � � � � � � (str-group)
>> > � � � � � � � � � "str1"
>> > � � � � � � � � � (str-group)
>> > � � � � � � � � � "str1")))
>> > Is there a possibility to abstract away some groups of strings with a
>> > macro or something else, like
>>
>> No, there's no possibility.
>>
>> Macros are functions that transform code (or code chunks) into code.
>>
>> Here you have data, and you want to transform it into data.
>>
>> So you CANNOT use a macro, you MUST use a function. �
>>
>> For example, you could write something like:
>>
>> � � (expand-data-substitutions
>> � � �'(((str1 � � � � � � � � � "str1")
>> � � � �(group23 � � � � � � � � "str2" "str3")
>> � � � �(group-str1-group23{2} � str1 group23 group23))
>> � � � (group-str1-group23{2} group-str1-group23{2} str1)))
>> � � --> ("str1" "str2" "str3" "str2" "str3" "str1" "str2" "str3" "str2" "str3" "str1")
>>
>> But in fact, for this problem you already have all you need in CL,
>> there's no need to design any complex kludge:
>>
>> � � (let* ((str1 � � � � � � � � � "str1")
>> � � � � � �(group23 � � � � � � � �'("str2" "str3"))
>> � � � � � �(group-str1-group23{2} �`(,str1 ,@group23 ,@group23)))
>> � � � `(,@group-str1-group23{2} ,@group-str1-group23{2} ,str1))
>> � � --> ("str1" "str2" "str3" "str2" "str3" "str1" "str2" "str3" "str2" "str3" "str1")
>>
>> so you can write:
>>
>> � � (let ((xx (let* ((str1 � � � � � � � � � "str1")
>> � � � � � � � � � � �(group23 � � � � � � � �'("str2" "str3"))
>> � � � � � � � � � � �(group-str1-group23{2} �`(,str1 ,@group23 ,@group23)))
>> � � � � � � � � `(,@group-str1-group23{2} ,@group-str1-group23{2} ,str1))))
>> � � � ...)
>>
>> Remember,
>>
>> � � � � � code --> code � = macro
>> � � � � � data --> data � = function
>>
>> (there are functions hidden behind backquote and comma).
>>
>> --
>> __Pascal Bourguignon__ � � � � � � � � � �http://www.informatimago.com/
>> A bad day in () is better than a good day in {}.
>
>
> Thank you very much, this worked well within the string list.
>
> In a similar problem the "group" is now a collection of macro calls
> within a function or method call, always from a cl-gtk2 example.
>
> (list-store-insert-with-values l n
>    (macro-a 1)
>    (macro-b 1)
>    (macro-a 2)
>    (macro-b 2)
>
>    (macro-a 3)
>    (macro-b 3)
>    (macro-a 4)
>    (macro-b 4))
>
> How can you convert this into something like
>
> (defmacro macro ()
>    ...?)
>
> (list-store-insert-with-values l n
>    (macro 1 1 2 2)
>    (macro 3 3 4 4))
>
> ; or even better
> (list-store-insert-with-values l n
>    (macro 1 2)
>    (macro 3 4))

Macros can only return one form, therefore a single macro call such as
(macro 1 2) just cannot generate four forms such as: (macro-a 1) 
(macro-b 1) (macro-a 2) (macro-b 2).

You must write a macro for an outer scope, like we used the LET*
operator above for data:


  (call-list-store-insert-with-values l n 1 1 2 2 3 3 4 4)

could be transformed into:

  (list-store-insert-with-values l n
    (macro-a 1)
    (macro-b 1)
    (macro-a 2)
    (macro-b 2)

    (macro-a 3)
    (macro-b 3)
    (macro-a 4)
    (macro-b 4))
 
The macro  call-list-store-insert-with-values to be written is trivial.

-- 
__Pascal Bourguignon__                     http://www.informatimago.com/
A bad day in () is better than a good day in {}.
0
Reply pjb (7667) 3/17/2011 3:29:09 PM

> From: hans <schatzer.joh...@gmail.com>
> I would like to replace some repeated code with a macro or somewhat.
> (let ((xx '("str1"
>               "str2" "str3"
>               "str2" "str3"
>               "str1"
>               "str2" "str3"
>               "str2" "str3"
>               "str1")))
> Is there a possibility to abstract away some groups of strings
> with a macro or something else, ...

A more fundamental question, on which you "beg the question", is
whether all instances of "str1" for example are by design
*always*and*forever* identical, or if it's just a coincidence that
at the moment they are the same. If at some time you decide you
want the first "str1" to change to "struno" instead, do you want
the other copies of that string to also change in exactly the same
way? Or do you want each copy to be independent of any other?

There's a more subtle question whether different instances of the
same string should always be EQ, or never EQ, or you don't care.
I'll assume you don't care. In the code you wrote above, they may
or may not be EQ depending on implementation and compiler settings
(and of course whether you compile the code or run it interpreted).
In my code below, they will always be EQ, which should be fine if
you don't care anyway.

If you want to ensure that all instances of "str1" change together
if any one of them changes, you'll need to very carefully do a
search and substitute, which is a pain. Better would be to have
each different string just *once* each in the source, bound to some
variable, then you refer to the variable multiple times. For
example:

(defparameter *s1* "struno")
(defparameter *s2* "strdos")
(defparameter *s3* "strtres")
(let ((xx (list *s1*
                *s2* *s3*
                *s2* *s3*
                *s1*
                *s2* *s3*
                *s2* *s3*
                *s1*)))

Now of you want to change "struno" to "struna", you have to change
only one place instead of three. If you want to change "strdos" to
"strduas", you have to change only one place instead of four.

Next comes the higher-level-data question of which groups of these
three strings are going to be repeated enough times to be worth
encapsulating into "canned" sequences, and of course whether these
"canned" sequences will be stable over time. If they aren't stable,
then there's no point optimizing the generation of the overall
sequence. But ignoring this issue, if you simply want to avoid the
necessity to manually re-key the global variable names, and the
typing mistakes that might occur, you can do this:

(defparameter *s1* "struno")
(defparameter *s2* "strdos")
(defparameter *s3* "strtres")
(defparameter *s123* (list *s1* *s2* *s3*))
(let ((xx (loop for ix in '(1  2 3  2 3  1  2 3  2 3  1)
            collect (nth (+ ix -1) *s123*))))

Now if you really want to guarantee that 2 3 always are together,
and if the combination of 1 2 3 2 3 is worth "canning", you can
combine this with some of Pascal's proposed solution:

(defparameter *s1* "struno")
(defparameter *s2* "strdos")
(defparameter *s3* "strtres")
(defparameter *s123* (list *s1* *s2* *s3*))
(let* ((n1 '(1))
       (n23 '(2 3))
       (ng3 (append n1 n23 n23))
       (nall (append ng3 ng3 n1))
       (xx (loop for ix in nall collect (nth (+ ix -1) *s123*))))

If you don't like having to offset the index when passing to nth,
you can include an extra dummy entry at the start of the value of
*st123*, so that "struno" will be at index 1 instead of 0, etc.:

(defparameter *s123* (list :DUMMY *s1* *s2* *s3*))

Alternately, at the cost of your code being slightly harder to
understand later, you can change these two lines of code:

(let* ((n1 '(0))
       (n23 '(1 2))

In either case the other change is not to subtract 1 from the index:

       (xx (loop for ix in nall collect (nth ix *s123*))))

If you really like using the back-quote macro instead of calling
APPEND directly, go ahead and adapt that aspect of Pascal's
proposal, another exercise for the reader.

There are so many different ways to accomplish this simple task,
depending on what is fixed for all time and what might change, also
depending on what safety checks you want to make sure your edit
doesn't screw up when you change your mind about some specifics,
that there's no one single answer to the question as you posed it.
You, as programmer, get to choose which of many ways to solve your
problem, given the ideas we have suggested in this thread.


In any case, there's no need to define any macro.
0
Reply seeWebInstead 3/21/2011 11:58:32 PM

5 Replies
29 Views

(page loaded in 0.157 seconds)

Similiar Articles:












7/27/2012 5:44:06 PM


Reply: