From: Mamut
Subject: Incredibly stupid newbie question
Date: 
Message-ID: <1132906452.417638.122100@g43g2000cwa.googlegroups.com>
I am really ashamed to bug you with this question. I have just started
learning LISP and, unfortunately, can't spend more than an hour each
day on self-education :(

Anyway. The question is this. Consider the following piece of C++ code:
std::string Concat(const std::vector<std::string> & v)
{
  std::string result;

  for (std::vector<std::string>::const_iterator it = v.begin, end =
v.end();
    it != end;
    ++it
  )
  {
    result += *it + ", ";
  }

  return result;
}

As you see, this function iterates over a string and prints out each of
it's elements in sequence, separated by a delimiter. I would like to do
the same in LISP with some (defun/defmacro print-delimited (str delim)
... )

I _guess_ this is doable with LOOP or even with (map...) , but I just
can't get my mind bent in the right direction.

Could you help у with that, please? (I promise I will ask more seriuos
questions in the future :) )

Thank you.

From: Pascal Costanza
Subject: Re: Incredibly stupid newbie question
Date: 
Message-ID: <3uo0dpF1223m0U1@individual.net>
Mamut wrote:

> As you see, this function iterates over a string and prints out each of
> it's elements in sequence, separated by a delimiter. I would like to do
> the same in LISP with some (defun/defmacro print-delimited (str delim)
> ... )

It seems to me that you rather want to iterate over a collection of 
strings. Here are two versions:

? (with-output-to-string (s)
     (loop for str in '("abc" "def" "ghi") do
           (princ str s)
           (princ ", " s)))
"abc, def, ghi, "

? (with-output-to-string (s)
     (loop for str on '("abc" "def" "ghi")
           do (princ (car str) s)
           when (cdr str)
           do (princ ", " s)))
"abc, def, ghi"


Pascal

-- 
My website: http://p-cos.net
Closer to MOP & ContextL:
http://common-lisp.net/project/closer/
From: Mamut
Subject: Re: Incredibly stupid newbie question
Date: 
Message-ID: <1132907864.726107.287930@g49g2000cwa.googlegroups.com>
Thank you! I am more stupid than I thought :) Indeed, I needed strings,
not just one string
From: Peder O. Klingenberg
Subject: Re: Incredibly stupid newbie question
Date: 
Message-ID: <kshda13yqh.fsf@beto.netfonds.no>
Pascal Costanza <··@p-cos.net> writes:

> Mamut wrote:
>
>> As you see, this function iterates over a string and prints out each of
>> it's elements in sequence, separated by a delimiter. I would like to do
>> the same in LISP with some (defun/defmacro print-delimited (str delim)
>> ... )
>
> It seems to me that you rather want to iterate over a collection of
> strings. Here are two versions:

[snip]

Here's the inevitable format version:

(format nil "~{~a~^, ~}" '("abc" "def" "ghi"))

...Peder...
-- 
I wish a new life awaited _me_ in some off-world colony.
From: Mamut
Subject: Re: Incredibly stupid newbie question
Date: 
Message-ID: <1132909097.072600.165350@z14g2000cwz.googlegroups.com>
Should've read "Practical Common Lisp" more carefully :)
From: Kaz Kylheku
Subject: Re: Incredibly stupid newbie question
Date: 
Message-ID: <1132942751.218273.168280@z14g2000cwz.googlegroups.com>
Mamut wrote:
> Should've read "Practical Common Lisp" more carefully :)

In addition to the FORMAT based solutions and the WITH-OUTPUT-TO-STRING
solutions, you might want one that doesn't involve doing stream I/O to
strings!

For instance, there is the REDUCE function which will decimate a
sequence through a binary operator.

  (reduce (lambda (left right) (concatenate 'string left ", " right))
#("a" "b" "c"))

  => "a, b, c"

If the list contains just one element, it is just returned by REDUCE.
The lambda function is not called.

If the list is empty, an error will be signaled, because your lambda is
then called with no arguments. You can make your lambda handle that
case by making the two arguments optional:

  (reduce #'(lambda (&optional left right)
              (if left (concatenate 'string left ", " right) ""))
          #())

If you wanted maximum efficiency, you probably wouldn't do it in any of
these ways.

Note that the C++ solution uses the += operator to add to a single
string instance. The std::string class can implement this efficiently,
by growing the internal character vector in larger leaps that reserve
space for material to be added. So less memory copying takes place. The
stream-based Lisp solutions might well do the same thing within the
string stream.

Here, we are just repeatedly taking three strings and catenating them
to produce a new one. The growing string keeps being reallocated and
copied, and the intermediate strings become garbage.

Here is a preliminary solution which calculates how much space is
needed, allocates a string exactly that big, and then copies the data
into it. Maybe someone can make this smaller?


(defun catenate-with-separator (string-vector sep-string)
  (case (length string-vector)
    (0 "")
    (1 (aref string-vector 0))
    (otherwise
      (let (total-length)
        (loop for str across string-vector
              summing (length str) into total
              finally (setf total-length
                            (+ total
                               (* (1- (length string-vector))
                                  (length sep-string)))))
        (let ((out-string (make-string total-length)))
          (loop for str across string-vector
                with out-string = (make-string total-length)
                with out-length = 0
                for str-pos = 0 then out-length
                for sep-pos = (length str)
                            then (+ out-length (length str))
                do (progn
                     (replace out-string str :start1 str-pos)
                     (incf out-length (length str))
                     (when (< sep-pos total-length)
                       (replace out-string sep-string :start1 sep-pos)
                       (incf out-length (length sep-string))))
                finally (return out-string)))))))
From: Damien Diederen
Subject: Re: Incredibly stupid newbie question
Date: 
Message-ID: <87hda0zbpk.fsf@keem.bcc>
Hi,

"Kaz Kylheku" <········@gmail.com> writes:
[deletia]
> Here is a preliminary solution which calculates how much space is
> needed, allocates a string exactly that big, and then copies the data
> into it. Maybe someone can make this smaller?
>
> (defun catenate-with-separator (string-vector sep-string)
>   (case (length string-vector)
>     (0 "")
>     (1 (aref string-vector 0))
>     (otherwise
>       (let (total-length)
>         (loop for str across string-vector
>               summing (length str) into total
>               finally (setf total-length
>                             (+ total
>                                (* (1- (length string-vector))
>                                   (length sep-string)))))
>         (let ((out-string (make-string total-length)))
>           (loop for str across string-vector
>                 with out-string = (make-string total-length)
>                 with out-length = 0
>                 for str-pos = 0 then out-length
>                 for sep-pos = (length str)
>                             then (+ out-length (length str))
>                 do (progn
>                      (replace out-string str :start1 str-pos)
>                      (incf out-length (length str))
>                      (when (< sep-pos total-length)
>                        (replace out-string sep-string :start1 sep-pos)
>                        (incf out-length (length sep-string))))
>                 finally (return out-string)))))))

A tad shorter, and works with any sequence type:

(defun catenate-with-separator (strings separator)
  (let ((count 0)
        (strings-length 0))
    (map nil (lambda (string)
               (incf count)
               (incf strings-length (length string))) strings)
    (case count
      (0 "")
      (1 (elt strings 0))
      (otherwise
       (let ((buffer (make-string (+ strings-length (* (1- count) (length separator)))))
             (index 0))
         (flet ((deposit (string)
                  (replace buffer string :start1 index)
                  (incf index (length string))))
           (map nil (lambda (string)
                      (deposit string)
                      (unless (zerop (decf count))
                        (deposit separator))) strings))
         buffer)))))

(minimally tested)

Enjoy,
Damien.

-- 
http://foobox.net/~dash/

Live fast, die young, and leave a good looking corpse.
		--James Dean
From: John Thingstad
Subject: Re: Incredibly stupid newbie question
Date: 
Message-ID: <op.s0t8q8wupqzri1@mjolner.upc.no>
Here's a vesion that dosn't need length..

(defun append-buffer (string buffer)
   (loop for char across string do
     (vector-push-extend char buffer)))

(defun concatenate-with-seperator (string-list seperator)
   (let ((buffer (make-array 0 :element-type 'base-char :adjustable t  
:fill-pointer 0)))
     (loop for (string . next-p) on string-list
           do
           (append-buffer string buffer)
           when next-p do
           (append-buffer seperator buffer))
     (coerce buffer 'simple-string)))


On Fri, 25 Nov 2005 19:19:11 +0100, Kaz Kylheku <········@gmail.com> wrote:

> Mamut wrote:
>> Should've read "Practical Common Lisp" more carefully :)
>
> In addition to the FORMAT based solutions and the WITH-OUTPUT-TO-STRING
> solutions, you might want one that doesn't involve doing stream I/O to
> strings!
>
> For instance, there is the REDUCE function which will decimate a
> sequence through a binary operator.
>
>   (reduce (lambda (left right) (concatenate 'string left ", " right))
> #("a" "b" "c"))
>
>   => "a, b, c"
>
> If the list contains just one element, it is just returned by REDUCE.
> The lambda function is not called.
>
> If the list is empty, an error will be signaled, because your lambda is
> then called with no arguments. You can make your lambda handle that
> case by making the two arguments optional:
>
>   (reduce #'(lambda (&optional left right)
>               (if left (concatenate 'string left ", " right) ""))
>           #())
>
> If you wanted maximum efficiency, you probably wouldn't do it in any of
> these ways.
>
> Note that the C++ solution uses the += operator to add to a single
> string instance. The std::string class can implement this efficiently,
> by growing the internal character vector in larger leaps that reserve
> space for material to be added. So less memory copying takes place. The
> stream-based Lisp solutions might well do the same thing within the
> string stream.
>
> Here, we are just repeatedly taking three strings and catenating them
> to produce a new one. The growing string keeps being reallocated and
> copied, and the intermediate strings become garbage.
>
> Here is a preliminary solution which calculates how much space is
> needed, allocates a string exactly that big, and then copies the data
> into it. Maybe someone can make this smaller?
>
>
> (defun catenate-with-separator (string-vector sep-string)
>   (case (length string-vector)
>     (0 "")
>     (1 (aref string-vector 0))
>     (otherwise
>       (let (total-length)
>         (loop for str across string-vector
>               summing (length str) into total
>               finally (setf total-length
>                             (+ total
>                                (* (1- (length string-vector))
>                                   (length sep-string)))))
>         (let ((out-string (make-string total-length)))
>           (loop for str across string-vector
>                 with out-string = (make-string total-length)
>                 with out-length = 0
>                 for str-pos = 0 then out-length
>                 for sep-pos = (length str)
>                             then (+ out-length (length str))
>                 do (progn
>                      (replace out-string str :start1 str-pos)
>                      (incf out-length (length str))
>                      (when (< sep-pos total-length)
>                        (replace out-string sep-string :start1 sep-pos)
>                        (incf out-length (length sep-string))))
>                 finally (return out-string)))))))
>



-- 
Using Opera's revolutionary e-mail client: http://www.opera.com/mail/
From: J.C. Roberts
Subject: Re: Incredibly stupid newbie question
Date: 
Message-ID: <aqmeo1pbcqroei1k3t203ih54vfk76i4eq@4ax.com>
On 25 Nov 2005 00:58:17 -0800, "Mamut" <········@gmail.com> wrote:

>Should've read "Practical Common Lisp" more carefully :)

Yes, even if it's just for the humor: 

PCL: Page 23 
|Technically, you could have also used FORMAT to loop over the 
|database itself, turning our dump-db function into a one-liner:
|
|(defun dump-db ()
|  (format t "~{~{~a:~10t~A~%~}~%~}" *DB*))
|
|That's either very cool or very scary depending on you point of view.

I was in stitches the first time I read that.

Kind Regards,
JCR
From: Simon Katz
Subject: Re: Incredibly stupid newbie question
Date: 
Message-ID: <enudo11320vqqbabicupa96igr5oroblha@4ax.com>
On Fri, 25 Nov 2005 09:30:48 +0100, Pascal Costanza <··@p-cos.net>
wrote:

>Mamut wrote:
>
>> As you see, this function iterates over a string and prints out each of
>> it's elements in sequence, separated by a delimiter. I would like to do
>> the same in LISP with some (defun/defmacro print-delimited (str delim)
>> ... )
>
>It seems to me that you rather want to iterate over a collection of 
>strings. Here are two versions:
>
>? (with-output-to-string (s)
>     (loop for str in '("abc" "def" "ghi") do
>           (princ str s)
>           (princ ", " s)))
>"abc, def, ghi, "
>
>? (with-output-to-string (s)
>     (loop for str on '("abc" "def" "ghi")
>           do (princ (car str) s)
>           when (cdr str)
>           do (princ ", " s)))
>"abc, def, ghi"
>
>
>Pascal

For the second approach above, I prefer:

(with-output-to-string (s)
  (loop for (str . more-p) on '("abc" "def" "ghi")
        do (princ str s)
        when more-p
        do (princ ", " s)))

Simon

___________________
Real email address:
(substitute ··@ #\+ (substitute #\s #\! "u!enet001+nomi!tech.com"))
From: John Thingstad
Subject: Re: Incredibly stupid newbie question
Date: 
Message-ID: <op.s0sghbhnpqzri1@mjolner.upc.no>
On Fri, 25 Nov 2005 09:14:12 +0100, Mamut <········@gmail.com> wrote:

> I am really ashamed to bug you with this question. I have just started
> learning LISP and, unfortunately, can't spend more than an hour each
> day on self-education :(
>
> Anyway. The question is this. Consider the following piece of C++ code:
> std::string Concat(const std::vector<std::string> & v)
> {
>   std::string result;
>
>   for (std::vector<std::string>::const_iterator it = v.begin, end =
> v.end();
>     it != end;
>     ++it
>   )
>   {
>     result += *it + ", ";
>   }
>
>   return result;
> }
>
> As you see, this function iterates over a string and prints out each of
> it's elements in sequence, separated by a delimiter. I would like to do
> the same in LISP with some (defun/defmacro print-delimited (str delim)
> ... )
>
> I _guess_ this is doable with LOOP or even with (map...) , but I just
> can't get my mind bent in the right direction.
>
> Could you help у with that, please? (I promise I will ask more seriuos
> questions in the future :) )
>
> Thank you.
>

(reduce (lambda (e1 e2) (concatenate 'string e1 delim e2) list)


-- 
Using Opera's revolutionary e-mail client: http://www.opera.com/mail/