From: Dr. Edmund Weitz
Subject: counting list items in FORMAT statement
Date: 
Message-ID: <m37ksw3znw.fsf@bird.agharta.de>
Hi!

I have a variable *PLACES* that holds a list of lists, like so:

  * (defparameter *places* '((1 2 3) nil nil (4 6) (8 5) (0) nil (7)))
  *PLACES*

I want each element of *PLACES* to be printed in reverse and on a line
of its own while preceded by its position in *PLACES*, i.e. I want

  0: 3 2 1
  1:
  2:
  3: 6 4
  4: 5 8
  5: 0
  6:
  7: 7

The following statement works fine, and I could just use it and be
happy with it:

  * (dotimes (i (length *places*))
      (format t "~A:~{ ~A~}~%" i (reverse (nth i *places*))))
  0: 3 2 1
  1:
  2:
  3: 6 4
  4: 5 8
  5: 0
  6:
  7: 7
  NIL

But... - just out of curiosity - I'm asking myself if it would be
possible to get rid of the DOTIMES loop and have the positions
automatically be printed by a FORMAT statement that'll take *PLACES*
as its only format argument. The solution that I came up with is

  * (let ((my-counter 0))
      (defun my-counter-printer (stream format-arg colon-p at-sign-p)
        (declare (ignore format-arg colon-p at-sign-p))
        (princ my-counter stream)
        (incf my-counter)))
  Converted MY-COUNTER-PRINTER.

  MY-COUNTER-PRINTER
  * (format t "~{~/my-counter-printer/:~{ ~A~}~%~}"
            (mapcan #'(lambda (place) (list nil (reverse place)))
                    *places*))
  0: 3 2 1
  1:
  2:
  3: 6 4
  4: 5 8
  5: 0
  6:
  7: 7
  NIL
  * 

It kind of works but I admit that

- it is plain ugly,

- it'll only work once or I'll have to provide a facility to reset
  MY-COUNTER,

- it doesn't fullfill my own "specs" 'cause I have to mangle *PLACES*
  before I can hand it over to my FORMAT statement.

Is there a way to really get what I want? Did I miss something while
reading 22.3 of the CLHS?

Thanks for your time,
Edi.

PS: Let me re-iterate: This is just out of curiosity. I'm perfectly
happy with the DOTIMES loop above and I'm quite sure that stuffing all
the logic into one FORMAT statement wouldn't really increase the
program's readability.

From: Janis Dzerins
Subject: Re: counting list items in FORMAT statement
Date: 
Message-ID: <871yj4gkbp.fsf@asaka.latnet.lv>
···@agharta.de (Dr. Edmund Weitz) writes:

> Hi!
> 
> I have a variable *PLACES* that holds a list of lists, like so:
> 
>   * (defparameter *places* '((1 2 3) nil nil (4 6) (8 5) (0) nil (7)))
>   *PLACES*
> 
> I want each element of *PLACES* to be printed in reverse and on a line
> of its own while preceded by its position in *PLACES*, i.e. I want
> 
>   0: 3 2 1
>   1:
>   2:
>   3: 6 4
>   4: 5 8
>   5: 0
>   6:
>   7: 7
> 
> The following statement works fine, and I could just use it and be
> happy with it:
> 
>   * (dotimes (i (length *places*))
>       (format t "~A:~{ ~A~}~%" i (reverse (nth i *places*))))
>   0: 3 2 1
>   1:
>   2:
>   3: 6 4
>   4: 5 8
>   5: 0
>   6:
>   7: 7
>   NIL
> 
> But... - just out of curiosity - I'm asking myself if it would be
> possible to get rid of the DOTIMES loop and have the positions
> automatically be printed by a FORMAT statement that'll take *PLACES*
> as its only format argument.

DOLIST would be a better start.

And extended LOOP:

(loop
 for i upfrom 0
 for list in *places*
 do (format t "~&~A:~{ ~A~}~%" i (reverse list)))

-- 
Janis Dzerins

  Eat shit -- billions of flies can't be wrong.
From: Pekka P. Pirinen
Subject: Re: counting list items in FORMAT statement
Date: 
Message-ID: <u6681cmb0.fsf@globalgraphics.com>
···@agharta.de (Dr. Edmund Weitz) writes:
> I want each element of *PLACES* to be printed in reverse and on a line
> of its own while preceded by its position in *PLACES*, i.e. I want
> 
>   0: 3 2 1
>   1:
>   2:
>   3: 6 4
>   4: 5 8
>   5: 0
>   6:
>   7: 7
> 
> [reasonable solution using DOTIMES and REVERSE omitted]
>
> [...] I'm asking myself if it would be possible to get rid of the
> DOTIMES loop and have the positions automatically be printed by a
> FORMAT statement that'll take *PLACES* as its only format argument.
>
> [...] Is there a way to really get what I want? Did I miss something
> while reading 22.3 of the CLHS?

This is not possible, because the FORMAT language is fairly poor.  If
a control structure (something like iterating in reverse) is not
provided, it's usually not possible to even simulate it.  In
particular, FORMAT computations really only have one piece of state:
the position in the current argument list.  This is not enough to
directly compute anything interesting: A language with one integer
variable (whose range is restricted by the input) is not very strong.
However, if the goal is to replace all control structures by FORMAT,
one can use the trick of contructing a new format string and
reapplying that to the arguments.  This allows a surprising amount of
computation:

CL-USER 124> 
  (format t
    (format nil ·····@?~~}"
      (format nil ·····@?~~}"
        (format nil
          (format nil ·····@··················@·····@··················@*~A"
            ··········@{~~*~}" ···@{~~2:*" "~}~0^~}~~0^~~}"
            (format nil "~% ~A: ~/quote-format/"
              "~#[~;0~*~:;~*~#/D-parm/~:*~]"
              (format nil ·····@··················@·····@··················@*~A"
                ··········@{~~*~}" ···@{~~2:*" "~}~0^~}~~0^~~}"
                " ~A")))
    *places*) *places*) *places*) *places*)
 0:  3 2 1
 1: 
 2: 
 3:  6 4
 4:  5 8
 5:  0
 6: 
 7:  7
nil

This uses two new directives, that I think are reasonable components
of a FORMAT-based language: a quoting operator and an integer output
routine.

(defun quote-format (str arg colonp at-sign-p)
  "Output argument with all the tildes quoted."
  (declare (ignore colonp at-sign-p))
  (loop for c across arg
        if (char= c #\~)
          do (write-string "~~" str)
        else
          do (write-char c str)))

(defun D-parm (str arg colonp at-sign-p &rest parms)
  "Output first parameter as a decimal integer."
  (declare (ignore arg colonp at-sign-p))
  (format str "~D" (first parms)))

I think the ~#/D-parm/ idiom could be programmed just using FORMAT
repeatedly, but this Friday afternoon is not long enough to work that
out.

> PS: Let me re-iterate: This is just out of curiosity. I'm perfectly
> happy with the DOTIMES loop above and I'm quite sure that stuffing all
> the logic into one FORMAT statement wouldn't really increase the
> program's readability.

You can say that again!  However, there is some method in that
madness.  You might notice an idiom repeated twice:

 (format nil ·····@··················@·····@··················@*~A"
   ··········@{~~*~}" ···@{~~2:*" "~}~0^~}~~0^~~}"
   <arg>)

This contructs a string that can be applied to a list to build a
string that processes it backwards, applying the final argument to
each element.  (If functions were allowed, this would be a good
candidate to wrap up, say MAKE-FORMAT-BACKWARD.)  Here it is in
action:

CL-USER 125> (make-format-backward " ~A")
··········@{~~*~} ·····@{~~2:* ~~A~}~0^~}~~0^~~}"
CL-USER 126> (format nil * '(0 1 2))
"~{~*~* ~A~2:* ~A~2:* ~A~0^~}"

The idiom "~#[~;0~*~:;~*~#/D-parm/~:*~]" outputs (1- length), and I
use this to create the line counter, by applying it backwards over the
main list.  However, outputting the elements themselves cannot be done
on the same iteration, because we want to do that forwards, so I build
a string with the numbering and the reverse iteration over each
element, something like this:

" 0 ~{~*~* ~A~2:* ~A~2:* ~A~0^~}
 1 ~{~0^~}
..."

wrap it up in some braces and apply it again.

If I believed these techniques were of any use at all, I would provide
a more detailed explanation.  As it is, I hope this amused someone
else as well.
-- 
Pekka P. Pirinen
(format t ··@?" "(format t ····@?\" ~:*~S)")