Prev: Evaluating/compiling forms in the current lexical environment.
Next: When are clojures more advantageous than CLOS?
From: Frode V. Fjeld on 29 Jan 2010 12:26 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 29 Jan 2010 13:23 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 29 Jan 2010 14:06 (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 29 Jan 2010 16:15 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 29 Jan 2010 16:20
"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. |