Marco Antoniotti wrote
> FORMAT is your friend
You are giving me ideas. How about
(defun join (word-list separator)
(format nil
(concatenate 'string "~{~A~^" separator "~}")
word-list))
which uses ~^ to stop and not put the separator after the
last string.
Alan Crowe
From: Nils Goesche
Subject: Re: concatenate atoms into string
Date:
Message-ID: <ly4qyt5faq.fsf@cartan.de>
Alan Crowe <····@cawNOtech.freeSPAMserve.co.uk> writes:
> Marco Antoniotti wrote
> > FORMAT is your friend
>
> You are giving me ideas. How about
>
> (defun join (word-list separator)
> (format nil
> (concatenate 'string "~{~A~^" separator "~}")
> word-list))
>
> which uses ~^ to stop and not put the separator after the
> last string.
Nice try, but what if the separator is "~"? :-)
Regards,
--
Nils G�sche
"Don't ask for whom the <CTRL-G> tolls."
PGP key ID 0x0655CFA0
In article <··················@twister1.libero.it>,
DaveNlp <(NOSPAM)·········@iol.it> wrote:
> Does a similar function exists?
>
> (defun join (wlist sepchr)
> (let ((string nil))
> (dolist (sym wlist string)
> (setf string
> (concatenate 'string
> string (if string sepchr nil) (string sym))))))
Well, this only makes one call to concatenate, hardly conses otherwise,
and is general for all sequences. I like it.
(defun join-seq (type list separator)
(let ((concatenate-args
(cons (first list) (loop for element in (rest list)
collect separator
collect element))))
(apply #'concatenate type concatenate-args)))
* (join-seq 'string '("happy" "new" "year") "-")
"happy-new-year"
* (join-seq 'vector '(#(8 0 0) #(5 5 5) #(1 2 1 2)) #("-"))
#(8 0 0 "-" 5 5 5 "-" 1 2 1 2)
* (join-seq 'list '((one two three) (four five six seven)) '(nil))
(ONE TWO THREE NIL FOUR FIVE SIX SEVEN)
* (join-seq 'string '((#\h #\a #\p #\p #\y) "new" #(#\y #\e #\a #\r)) '(#\-))
"happy-new-year"
-bcd
Brian Downing <·············@lavos.net> wrote:
+---------------
| Well, this only makes one call to concatenate, hardly conses otherwise,
| and is general for all sequences. I like it.
|
| (defun join-seq (type list separator)
| (let ((concatenate-args
| (cons (first list) (loop for element in (rest list)
| collect separator
| collect element))))
| (apply #'concatenate type concatenate-args)))
+---------------
An idiom I've found myself using with LOOP to special-case
first elements is this:
(defun join-seq (type list separator)
(apply #'concatenate type
(loop for element in list
and first = t then nil
unless first
collect separator
collect element)))
or, depending on the phase of the moon and what I've had for lunch,
this [note use of ON here]:
(defun join-seq (type list separator)
(apply #'concatenate type
(loop for tail on list
collect (car tail)
when (cdr tail)
collect separator)))
But both of these do involve a test per iteration that yours doesn't,
so are slightly less efficient I suppose.
-Rob
-----
Rob Warnock, PP-ASEL-IA <····@rpw3.org>
627 26th Avenue <URL:http://rpw3.org/>
San Mateo, CA 94403 (650)572-2607
"DaveNlp" <(NOSPAM)·········@iol.it> wrote in message news:<··················@twister1.libero.it>...
> Does a similar function exists?
>
> (defun join (wlist sepchr)
> (let ((string nil))
> (dolist (sym wlist string)
> (setf string
> (concatenate 'string
> string (if string sepchr nil) (string sym))))))
>
> >(join '("happy" "new" "year") "-")
> >"happy-new-year"
This kind of repeated reduction of a sequence through a binary
operator is nicely handled functionally by REDUCE. Just take care of
the empty list case; REDUCE internally handles the case when the list
has length one:
(if wlist
(reduce #'(lambda (x y)
(concatenate 'string x sepchr y))
wlist))
Apologies about the use of the implicit NIL result from IF. ;)
Kaz Kylheku wrote:
> "DaveNlp" <(NOSPAM)·········@iol.it> wrote in message news:<··················@twister1.libero.it>...
>
>>Does a similar function exists?
>>
>>(defun join (wlist sepchr)
>> (let ((string nil))
>> (dolist (sym wlist string)
>> (setf string
>> (concatenate 'string
>> string (if string sepchr nil) (string sym))))))
>>
>>
>>>(join '("happy" "new" "year") "-")
>>>"happy-new-year"
>
>
> This kind of repeated reduction of a sequence through a binary
> operator is nicely handled functionally by REDUCE. Just take care of
> the empty list case; REDUCE internally handles the case when the list
> has length one:
>
> (if wlist
> (reduce #'(lambda (x y)
> (concatenate 'string x sepchr y))
> wlist))
>
> Apologies about the use of the implicit NIL result from IF. ;)
FWIW, I find the use of :initial-value clearer, as in:
(reduce (lambda (x y) (concatenate 'string x sepchr y))
wlist
:initial-value nil)
Then you don't have to handle the empty list.
Pascal
On Tue, 30 Sep 2003 20:24:18 +0200, Pascal Costanza wrote:
> FWIW, I find the use of :initial-value clearer, as in:
>
> (reduce (lambda (x y) (concatenate 'string x sepchr y))
> wlist
> :initial-value nil)
>
> Then you don't have to handle the empty list.
Except this gives you a leading sepchr.
=> "-happy-new-year"
Would WHEN be more 'proper' than IF in this case?
Pascal Costanza <········@web.de> wrote in message news:<············@newsreader2.netcologne.de>...
> Kaz Kylheku wrote:
>
> > "DaveNlp" <(NOSPAM)·········@iol.it> wrote in message news:<··················@twister1.libero.it>...
> >>>(join '("happy" "new" "year") "-")
> >>>"happy-new-year"
> >
> >
> > This kind of repeated reduction of a sequence through a binary
> > operator is nicely handled functionally by REDUCE. Just take care of
> > the empty list case; REDUCE internally handles the case when the list
> > has length one:
> >
> > (if wlist
> > (reduce #'(lambda (x y)
> > (concatenate 'string x sepchr y))
> > wlist))
> >
> > Apologies about the use of the implicit NIL result from IF. ;)
>
> FWIW, I find the use of :initial-value clearer, as in:
>
> (reduce (lambda (x y) (concatenate 'string x sepchr y))
> wlist
> :initial-value nil)
>
> Then you don't have to handle the empty list.
But this screws up the processing for non-empty lists. The
:initial-value parameter is not useful in this situation, unless you
are prepared to handle the NIL value of X in the lambda and just
return Y. If you are going to do that, you can dispense with the
:INITIAL-VALUE and just make it handle two optional parameters
instead, checking the first one for NIL.
In effect, when you have :INITIAL-VALUE, it's as if that value was
added to the front of the sequence (or to the tail, if you have
:FROM-END T). So the list () is processed as (NIL) which returns NIL,
which is okay, but the list ("happy") is processed as (nil "happy")
which yields "-happy", and ("happy" "new") yields "-happy-new" and so
on. Always a troublesome dash at the beginning.
:INITIAL-VALUE works superbly when there is some idempotent element
that can be seeded into any length sequence with no ill effect. For
example, products and sums:
(reduce #'my-mult product-list :initial-value 1)
(reduce #'my-add term-list :initial-value 0)
A potential problem with using reduce like this:
(defun join (wlist sepchr)
(if wlist
(reduce #'(lambda (x y)
(concatenate 'string x sepchr y))
wlist)))
is that join then uses Schlemiel the painter's algorithm.
http://discuss.fogcreek.com/techInterview/default.asp?cmd=show&ixPost=153
Since concatenate's arguments are copied, the y argument to
the lambda expression is accumulating and repeatedly copying
the whole string. So if you have n strings of n characters,
the algorithm is order n cubed. DaveNlp's original join
> (defun join (wlist sepchr)
> (let ((string nil))
> (dolist (sym wlist string)
> (setf string
> (concatenate 'string
> string (if string sepchr nil) (string sym))))))
had the same problem, with string getting longer and longer
and getting copied again and again.
(defun join3 (word-list separator)
(apply #'concatenate 'string
(first word-list)
(mapcan #'(lambda(x)(list separator x))
(rest word-list))))
seems to do the trick, running my test cases in n squared
time.
To my great surprise it handles the case of the empty word
list correctly as written. I think I've understood how that
has happened.
(car nil) => nil
(cdr nil) => nil
mapcan is cool about begin passed an empty list
(apply foo bar '()) turns into (funcall foo bar)
(concatenate 'string nil) => ""
That is the first time in 30 years of computer programming
that a computer has /voluntarily/ done what I wanted it to.
Isn't CL wonderful :-)
I don't know what to do about a really long list overflowing
the stack when apply tries to call concatenate on the list.
Thoughts anyone?
Alan Crowe
"Kaz Kylheku" <···@ashi.footprints.net> ha scritto nel messaggio
> This kind of repeated reduction of a sequence through a binary
> operator is nicely handled functionally by REDUCE. Just take care of
> the empty list case; REDUCE internally handles the case when the list
> has length one:
>
> (if wlist
> (reduce #'(lambda (x y)
> (concatenate 'string x sepchr y))
> wlist))
>
> Apologies about the use of the implicit NIL result from IF. ;)
It was just the method that I looked for. :)
I have done a small modification to your idea
in manner that it works with numbers, symbols and lists.
(defun join-all (wlist sepchr)
(if wlist
(reduce #'(lambda (x y)
(concatenate 'string
(if (numberp x) (princ-to-string x)
(if (listp x) (join x sepchr) (string x)))
sepchr
(if (numberp y) (princ-to-string y)
(if (listp y) (join y sepchr) (string y)))))
wlist)))
>(setq mm (list "1" "2"))
>(join (list mm 'a 'b "ejz" 3.2) " ")
>"1 2 A B ejz 3.2"
Thx
DaveNlp