From: Frode V. Fjeld on
Adam White <spudboy(a)iinet.net.au> writes:

> Given a mixed list of strings and other items, I'd like to concatenate
> all strings (and only strings) adjacent to each other.

(defun f (list)
(loop while list
collect (if (not (stringp (car list)))
(pop list)
(with-output-to-string (s)
(loop while list
while (stringp (car list))
do (write-string (pop list) s)))))))

--
Frode V. Fjeld
From: W. James on
Ole Arndt wrote:

> Adam White <spudboy(a)iinet.net.au> writes:
>
> > Given a mixed list of strings and other items, I'd like to
> > concatenate all strings (and only strings) adjacent to each other.
> >
> > So for '("1" "2" 3 4 "5" "6" 7 8 "9"), we should return
> >
> > ("12" 3 4 "56" 7 8 "9")
> >
> > My first solution which works, but is as ugly as sin is:
> >
> > (loop
> > with curr = ""
> > with save = '()
> > for p in '("1" "2" 3 4 "5" "6" 7 8 "9") do
> > (cond
> > ((stringp p) (setf curr (concatenate 'string curr p)))
> > ((equal curr "") (push p save))
> > (t (push curr save) (push p save) (setf curr "")))
> > finally (return (nreverse (cons curr save))))
> >
> > Surely there has got to be a better way to do this!
> >
> > Any pointers?
>
> And another solution:
>
> (defun merge-strings (list)
> (labels ((conc (beg cur rest)
> (cond ((null cur)
> beg)
> ((and (stringp cur) (stringp (first rest)))
> (conc beg
> (concatenate 'string cur (first rest))
> (rest rest)))
> (t
> (conc (nconc beg (list cur))
> (first rest)
> (rest rest))))))
> (conc nil (first list) (rest list))))

CL (COBOL-LISP) isn't very good at handling lists. You've illustrated
that very well.
Let's use Matzlisp:

["1","2",3,4,"5","6",7,8,"9"].inject([]){|a,x|
(if x.is_a?(String) and a.last.is_a?(String)
a.last
else
a
end) << x
a
}
==>["12", 3, 4, "56", 7, 8, "9"]


--

From: Richard Fateman on


(defun f(l)
(cond ((and(stringp (car l))(stringp (cadr l)))
(f (cons (concatenate 'string (car l) (cadr l))
(cddr l))))
((cadr l) (cons (car l)(f (cdr l))))
(t l)))

;; (f '("1" "2" 3 4 "5" "6" 7 8 "9")) --> ("12" 3 4 "56" 7 8 "9")

;; obviously this could be made longer and more "self documenting"
;; and avoiding cdr and friends.

;; like this



(defun f(lis)
(let ((h (first lis))(r (rest lis)))
(cond ((and(stringp h)(stringp (first r)))
(f (cons (concatenate 'string h (first r))
(rest r))))
(r (cons h (f r)))
(t lis))))
From: Kaz Kylheku on
On 2010-01-29, Adam White <spudboy(a)iinet.net.au> wrote:
>
> Given a mixed list of strings and other items, I'd like to concatenate
> all strings (and only strings) adjacent to each other.
>
> So for '("1" "2" 3 4 "5" "6" 7 8 "9"), we should return
>
> ("12" 3 4 "56" 7 8 "9")
>
> My first solution which works, but is as ugly as sin is:
>
> (loop
> with curr = ""
> with save = '()
> for p in '("1" "2" 3 4 "5" "6" 7 8 "9") do
> (cond
> ((stringp p) (setf curr (concatenate 'string curr p)))
> ((equal curr "") (push p save))
> (t (push curr save) (push p save) (setf curr "")))
> finally (return (nreverse (cons curr save))))
>
>
> Surely there has got to be a better way to do this!
>
> Any pointers?

I dislike all of the solutions hitherto given. :)

The following is fantasy syntax, based on imaginary extensions to Fare Rideau's
pattern notation:

;; munch the input-list as follows: sequences of strings are catenated
;; into a single string object which is collected. All other items are
;; collected as-is.

(lex-collect input-list (tok)
((list (1+ (of-type string))) (apply #'concatenate 'string tok))
((list item) item))

Notes:

- lex-collect returns an implicit list, which is constructed by
collecting the return values of the rule bodies.

- A rule can append multiple items to the list by returning multiple
values. Returning (values) means that nothing is collected.

- tok specifies the name of a variable which is always bound to the entire
lexeme that is matched by a rule. This spares the user from writing patterns
of the form (and variable-name (actual pattern match ...)) just to capture
the whole thing into a variable.

- Rules extract the longest possible match from the input object. If two or
more rules apply to the object, the one which extracts the most wins. In case
of a tie, the earlier rule wins.

- The unmatched remainder of the input object becomes a new object, handled by
the next iteration. If the input object is (1 2 3), then
the rule (list item) binds item to 1, and leaves the remainder (2 3).

- If the input object is nil, lex-collect evaluates any explicit
rule which matches nil, and then terminates, returning the collected list.
Thus if there is no explicit rule for nil, the behavior is as if there
was a rule (nil (values))

- If the input doesn't match any rule, and the input object is other than nil,
an error is signaled.

- A rule with an empty body signals the same kind of error, if it matches.
Successful rules must evaluate at least one expression.
From: Raymond Wiker on
"Frode V. Fjeld" <frode(a)netfonds.no> writes:

> Adam White <spudboy(a)iinet.net.au> writes:
>
>> Given a mixed list of strings and other items, I'd like to concatenate
>> all strings (and only strings) adjacent to each other.
>
> (defun f (list)
> (loop while list
> collect (if (not (stringp (car list)))
> (pop list)
> (with-output-to-string (s)
> (loop while list
> while (stringp (car list))
> do (write-string (pop list) s)))))))

I came up with the following:

(defun f(list)
(loop for (item . next) on list
with saved = nil
when (stringp item)
do (push item saved)
when (and saved (or (null next) (not (stringp item))))
collect (apply #'concatenate 'string (nreverse saved))
when (not (stringp item))
do (setq saved nil)
and
collect item))

--- not sure if this is a good idea, but at least it shows off some
other aspects of loop.