From: Ron Garret
Subject: Format puzzle
Date: 
Message-ID: <rNOSPAMon-132CBA.17492517112007@news.gha.chartermi.net>
Here's a function that does what the Python string.join function does:

(defun join (l &optional (delim ""))
  (format nil (format nil "~~{~~A~~#[~~:;~A~~]~~}" delim) l))

Is there a way to do this with only a single call to format?

rg

From: Ron Garret
Subject: Re: Format puzzle
Date: 
Message-ID: <rNOSPAMon-901AC3.18092417112007@news.gha.chartermi.net>
In article <·······························@news.gha.chartermi.net>,
 Ron Garret <·········@flownet.com> wrote:

> Here's a function that does what the Python string.join function does:
> 
> (defun join (l &optional (delim ""))
>   (format nil (format nil "~~{~~A~~#[~~:;~A~~]~~}" delim) l))

Or, simpler...

(defun join (l &optional (delim ""))
  (format nil (format nil "~~{~~A~~^~A~~}" delim) l))

but still doesn't answer the original question:

> 
> Is there a way to do this with only a single call to format?
> 
> rg
From: Joshua Taylor
Subject: Re: Format puzzle
Date: 
Message-ID: <deaba798-6a05-4ce5-a573-10ed2de2722c@41g2000hsh.googlegroups.com>
On Nov 17, 8:49 pm, Ron Garret <·········@flownet.com> wrote:
> Here's a function that does what the Python string.join function does:
>
> (defun join (l &optional (delim ""))
>   (format nil (format nil "~~{~~A~~#[~~:;~A~~]~~}" delim) l))

This actually seems to have a subtle bug if delim has format
directives:

(join '("one" "two" "three" "four") "~A,,")
=>
"onetwo,,threefour,,"

A version with only call to format might be:

(defun join (l &optional (delim ""))
  (format nil (concatenate 'string "~{~A~^" delim "~}") l))

but that will have the same problem with format directives in the
delimiter. with-output-to-string can make some kinds of string
construction easy (and this way avoids the problem with format
strings, and, in fact, doesn't use any calls to format):

(defun join (l &optional (delim ""))
  (with-output-to-string (s)
    (do ((l l (cdr l)))
	((endp l))
      (write-string (car l) s)
      (unless (endp (cdr l))
	(write-string delim s)))))

I don't think either of these is quite what you're looking for though.
The first isn't really any different; a call to format is avoided with
concatenate. The second is much bigger. There are also options that
would construct another list of delimiter strings, or make a big list
of with the elements of l and delimiters interspersed, but those
aren't neat format usages.

I looked at ~* (go to) to try to work something like ·······@*~} (cons
delim l) to print each item and then the first item of the list, but
this moves around in the arguments to be processed and would be an
infinite loop (which LispWorks' debugger caught!).

I'm hoping someone with more format skills than I will show us a
glorious one-liner.

//J
From: Ron Garret
Subject: Re: Format puzzle
Date: 
Message-ID: <rNOSPAMon-449684.20303117112007@news.gha.chartermi.net>
In article 
<····································@d50g2000hsf.googlegroups.com>,
 Tayssir John Gabbour <············@googlemail.com> wrote:

> On Nov 18, 2:49 am, Ron Garret <·········@flownet.com> wrote:
> > Here's a function that does what the Python string.join function does:
> >
> > (defun join (l &optional (delim ""))
> >   (format nil (format nil "~~{~~A~~#[~~:;~A~~]~~}" delim) l))
> >
> > Is there a way to do this with only a single call to format?
> 
> Is the following in the spirit of the rules?
> 
> (defun join (list &optional (delim ""))
>   (format nil (concatenate 'string "~{~A~^" delim "~}")
>           list))

Yes as I stated them, not as I intended them.  What I really wanted to 
know is if FORMAT was capable of emulating string.join using only a 
single static format string and no other function calls.  It seems like 
the format control language can do nearly everything under the sun, but 
I couldn't figure out how to make it do this seemingly simple thing.  Of 
course, there's no sport in doing it using actual code.  :-)

rg
From: Kent M Pitman
Subject: Re: Format puzzle
Date: 
Message-ID: <ubq9sb1y6.fsf@nhplace.com>
Ron Garret <·········@flownet.com> writes:

> Here's a function that does what the Python string.join function does:
> 
> (defun join (l &optional (delim ""))
>   (format nil (format nil "~~{~~A~~#[~~:;~A~~]~~}" delim) l))
> 
> Is there a way to do this with only a single call to format?

Heh.  I assume you mean to rule out solutions like I used for
  http://groups.google.com/group/comp.lang.lisp/msg/07e4ac421ef5dbd7
... although I suppose it's at least worth my pre-protesting any such
hypothetical restriction by saying that the whole point of ~/.../ is
to allow people to overcome missing operations in FORMAT, so any claim
that FORMAT is too weak to do various kinds of things is subject to
the same kinds of responses that we give when someone says Lisp
doesn't have a pre-defined SQUARE function or a pre-defined NREVERSEF
macro.  We provide abstraction techniques to cover.

(As a curiosity, btw, does Python have a FORMAT function and can you
do this in a single call to Python's FORMAT?  If so, just for color
and in the spirit of general enlightenment, it'd be interesting to see
what FORMAT operation/paradigm they added that enabled this.)

All of that said, we've also had this discussion about this exact
puzzle with ~~ quite recently, including all the various associated
gotchas.  I don't have the patience to figure out what to Google for,
but I know it's there.
From: Ron Garret
Subject: Re: Format puzzle
Date: 
Message-ID: <rNOSPAMon-E33017.00190918112007@news.gha.chartermi.net>
In article <·············@nhplace.com>,
 Kent M Pitman <······@nhplace.com> wrote:

> Ron Garret <·········@flownet.com> writes:
> 
> > Here's a function that does what the Python string.join function does:
> > 
> > (defun join (l &optional (delim ""))
> >   (format nil (format nil "~~{~~A~~#[~~:;~A~~]~~}" delim) l))
> > 
> > Is there a way to do this with only a single call to format?
> 
> Heh.  I assume you mean to rule out solutions like I used for
>   http://groups.google.com/group/comp.lang.lisp/msg/07e4ac421ef5dbd7

Not necessarily.  But the solution to the problem using this approach 
doesn't seem particularly elegant to me.  The problem seems to be not so 
much a deficiency of format's functionality as with its argument 
handling.  You need to make the delim argument "sticky" somehow, and the 
format string language doesn't seem to have a facility for that.  (This 
is not necessarily a criticism, just an observation).

> (As a curiosity, btw, does Python have a FORMAT function

Sort of.  It has the % operator on strings, and then there are add-on 
template libraries like Cheetah and Mako.

> and can you do this in a single call to Python's FORMAT?

Not with the built-in one (at least not in any way that I can think of 
offhand) but certainly with the add-ons (they are Turing-complete).  
And, of course, string.join is built-in.

rg
From: Kent M Pitman
Subject: Re: Format puzzle
Date: 
Message-ID: <u7ikgb1h0.fsf@nhplace.com>
Tayssir John Gabbour <············@googlemail.com> writes:

> On Nov 18, 2:49 am, Ron Garret <·········@flownet.com> wrote:
> > Here's a function that does what the Python string.join function does:
> >
> > (defun join (l &optional (delim ""))
> >   (format nil (format nil "~~{~~A~~#[~~:;~A~~]~~}" delim) l))
> >
> > Is there a way to do this with only a single call to format?
> 
> Is the following in the spirit of the rules?
> 
> (defun join (list &optional (delim ""))
>   (format nil (concatenate 'string "~{~A~^" delim "~}")
>           list))

You'll be wanting to write a SUPERQUOTE-FOR-FORMAT operation to
bulletproof the input delim in the case of it containing a "~".
(Or else, at minimum, you want to check delim and make sure that
it is it is not a sequence containing #\~ as one of its elements.)

I think the point of Ron's query, though, was that he wants not to
cons and throw away an intermediate string, which you're doing at
least once for the FORMAT string and once again if you end up having
to superquote the delim.

Whether the consing issue is or is not something to care about is an
issue of where this is used, how good the gc is, etc.  But my point is
that it's something you didn't really bypass by calling CONCATENATE.

(Some might call this call to CONCATENATE a "poor man's FORMAT",
suggesting that the effect of FORMAT was really already in use even
if you didn't dignify it with the name ... a kind of loose analog of
the way Greenspun's Tenth Law[*] might be interpreted to say that you
didn't really avoid using Lisp just because you wrote all the 
relevant substitutes and then just didn't call it Lisp.)

[*] http://seal.web.cern.ch/seal/main/greenspun.html
From: Tayssir John Gabbour
Subject: Re: Format puzzle
Date: 
Message-ID: <520f9e92-1ce7-48c6-8533-914c03891666@a28g2000hsc.googlegroups.com>
On Nov 18, 5:51 am, Kent M Pitman <······@nhplace.com> wrote:
> You'll be wanting to write a SUPERQUOTE-FOR-FORMAT operation to
> bulletproof the input delim in the case of it containing a "~".
> (Or else, at minimum, you want to check delim and make sure that
> it is it is not a sequence containing #\~ as one of its elements.)

Yeah, I said "whoops" and deleted the post from Google Groups right
after I sent it. ;)

(Hey, Gray and Reuter got add() wrong at first when trying to show a
nonbuggy program, and let's not talk about mathematicians getting
tripped up on basic probability...)
From: Michael Weber
Subject: Re: Format puzzle
Date: 
Message-ID: <4ce9b70c-fc86-413e-9cbd-8a8ad0307db5@l1g2000hsa.googlegroups.com>
On Nov 18, 2:49 am, Ron Garret <·········@flownet.com> wrote:
> Here's a function that does what the Python string.join function does:
>
> (defun join (l &optional (delim ""))
>   (format nil (format nil "~~{~~A~~#[~~:;~A~~]~~}" delim) l))
>
> Is there a way to do this with only a single call to format?

Does it count if no format call is needed at all?

(cl-interpol:enable-interpol-syntax)
(defun join (l &optional (cl-interpol:*list-delimiter* ""))
  ···@{l}")


Cheers,
Michael
From: Ron Garret
Subject: Re: Format puzzle
Date: 
Message-ID: <rNOSPAMon-C7D08B.10090618112007@news.gha.chartermi.net>
In article 
<····································@l1g2000hsa.googlegroups.com>,
 Michael Weber <·········@foldr.org> wrote:

> On Nov 18, 2:49 am, Ron Garret <·········@flownet.com> wrote:
> > Here's a function that does what the Python string.join function does:
> >
> > (defun join (l &optional (delim ""))
> >   (format nil (format nil "~~{~~A~~#[~~:;~A~~]~~}" delim) l))
> >
> > Is there a way to do this with only a single call to format?
> 
> Does it count if no format call is needed at all?
> 
> (cl-interpol:enable-interpol-syntax)
> (defun join (l &optional (cl-interpol:*list-delimiter* ""))
>   ···@{l}")

It wasn't my original intention, but thanks for bringing cl-interpol to 
my attention!

rg
From: Juho Snellman
Subject: Re: Format puzzle
Date: 
Message-ID: <87sl34cl8d.fsf@vasara.proghammer.com>
Ron Garret <·········@flownet.com> writes:

> Here's a function that does what the Python string.join function does:
> 
> (defun join (l &optional (delim ""))
>   (format nil (format nil "~~{~~A~~#[~~:;~A~~]~~}" delim) l))
> 
> Is there a way to do this with only a single call to format?

(defvar *join-separator* nil)
(defun join-aux (&rest args) (write-string *join-separator* (car args)))
(defun join (stream list *join-separator*)
  (format stream "~{~a~^~:*~/join-aux/~}" list))

-- 
Juho Snellman
From: Tobias C. Rittweiler
Subject: Re: Format puzzle
Date: 
Message-ID: <87ve7z94gy.fsf@freebits.de>
Ron Garret <·········@flownet.com> writes:

> Here's a function that does what the Python string.join function does:
>
> (defun join (l &optional (delim ""))
>   (format nil (format nil "~~{~~A~~#[~~:;~A~~]~~}" delim) l))
>
> Is there a way to do this with only a single call to format?
>
> rg

If you don't mind a consing solution, what about

  (format nil "~:{~A~A~}" (loop for word in list 
                                collect (list word delim)))

  -T.
From: Rob Warnock
Subject: Re: Format puzzle
Date: 
Message-ID: <8qadnQO6_eyt4dzanZ2dnUVZ_gCdnZ2d@speakeasy.net>
Tobias C. Rittweiler <···@freebits.de.invalid> wrote:
+---------------
| Ron Garret <·········@flownet.com> writes:
| > Here's a function that does what the Python string.join function does:
| > (defun join (l &optional (delim ""))
| >   (format nil (format nil "~~{~~A~~#[~~:;~A~~]~~}" delim) l))
| > Is there a way to do this with only a single call to format?
| 
| If you don't mind a consing solution, what about
|   (format nil "~:{~A~A~}" (loop for word in list 
|                                 collect (list word delim)))
+---------------

Your solution appends a spurious final "delim", e.g.:

    > (let ((list '(a b c))
	    (delim #\,))
	(format nil "~:{~A~A~}" (loop for word in list
				      collect (list word delim))))

    "A,B,C,"
    >

But you can fix that with a bit of uglification:

    > (let ((list '(a b c))
	    (delim #\,))
	(format nil ·········@[~A~]~}"
		    (loop for tail on list
		      collect (list (first tail)
				    (when (rest tail) delim)))))

    "A,B,C"
    >

which can be simplified considerably by changing the LOOP iterator
from FOR...IN to FOR...ON, to allow lookahead, and *not* using sublists:

    > (let ((list '(a b c))
	    (delim #\,))
	(format nil "~{~A~^~A~}" (loop for tail on list
				   collect (first tail)
				   when (rest tail)
				     collect delim)))

    "A,B,C"
    >

But if you're going to allow LOOP, then why use FORMAT at all?!?
Since FORMAT probably uses WITH-OUTPUT-TO-STRING "under the hood",
just do so directly:

    > (let ((list '(a b c))
	    (delim #\,))
	(with-output-to-string (s)
	  (loop for tail on list
	    do (princ (first tail) s)
	       (when (rest tail)
		 (princ delim s)))))

    "A,B,C"
    >

Those still awake will note that Joshua Taylor suggested this two days ago
in <·········································@41g2000hsh.googlegroups.com>,
except he used DO & WRITE-STRING and the above uses LOOP & PRINC.


-Rob

p.s. To *really* beat a dead horse, we can merge this with the
"so something different the first time" thread [remember that?
"Subject: Multiple arguments to mapcar?"], switch back to FOR...IN,
rearrange things a bit, and get:

    > (let ((list '(a b c))
	    (delim #\,))
	(with-output-to-string (s)
	  (loop for word in list
		and first = t then nil
	    do (unless first
		 (princ delim s))
	       (princ word s))))

    "A,B,C"
    >

p.p.s. It must be really late here, or I never
could have even *considered* this version:  ;-}  ;-}

    > (let ((list '(a b c))
	    (delim #\,))
	(with-output-to-string (s)
	  (loop for word in list
		and maybe-delim = "" then delim
	    do (princ maybe-delim s)
	       (princ word s))))

    "A,B,C"
    >

-----
Rob Warnock			<····@rpw3.org>
627 26th Avenue			<URL:http://rpw3.org/>
San Mateo, CA 94403		(650)572-2607