From: bradb
Subject: multiple-value-bind and cond
Date: 
Message-ID: <1158358460.521942.39970@i42g2000cwa.googlegroups.com>
I'm using CL-PPCRE (thanks a lot Edi!) to parse a string multiple times
looking for a match.  I may need to parse the same input string against
a couple of different patterns in order to classify the input string.
I also want to know where that match ends.  I really want to do
something like:

(cond
 ((scan pattern1 input) (values 'pattern1 end))
 ((scan pattern2 input) (values 'pattern2 end)))

Normally you would obtain end by doing:
(multiple-value-bind (start end) (scan pattern1 input)) if pattern1 is
not matched, then start and end will be NIL.
I can't think of any nice way to code this, you either need to
(multiple-value-bind (start end) (scan pattern1 input)
 (when start (return (values 'pattern1 end))))
(multiple-value-bind (start end) (scan pattern2 input)
 (when start (return (values 'pattern2 end))))

Which is a bit ugly and imperative.

or do the scan twice as
(cond
 ((scan pattern1 input) (multiple-value-bind (start end) (scan pattern1
input) (values 'pattern1 end))))
Which is just plain ugly.

Any thoughts on a nice way to structure this kind of problem?

Cheers
Brad

From: Bill Atkins
Subject: Re: multiple-value-bind and cond
Date: 
Message-ID: <m2ejuc4uj1.fsf@machamp-218.dynamic.rpi.edu>
"bradb" <··············@gmail.com> writes:

> I'm using CL-PPCRE (thanks a lot Edi!) to parse a string multiple times
> looking for a match.  I may need to parse the same input string against
> a couple of different patterns in order to classify the input string.
> I also want to know where that match ends.  I really want to do
> something like:
>
> (cond
>  ((scan pattern1 input) (values 'pattern1 end))
>  ((scan pattern2 input) (values 'pattern2 end)))
>
> Normally you would obtain end by doing:
> (multiple-value-bind (start end) (scan pattern1 input)) if pattern1 is
> not matched, then start and end will be NIL.
> I can't think of any nice way to code this, you either need to
> (multiple-value-bind (start end) (scan pattern1 input)
>  (when start (return (values 'pattern1 end))))
> (multiple-value-bind (start end) (scan pattern2 input)
>  (when start (return (values 'pattern2 end))))
>
> Which is a bit ugly and imperative.
>
> or do the scan twice as
> (cond
>  ((scan pattern1 input) (multiple-value-bind (start end) (scan pattern1
> input) (values 'pattern1 end))))
> Which is just plain ugly.
>
> Any thoughts on a nice way to structure this kind of problem?
>
> Cheers
> Brad

Hmm:

;; acond = anaphoric cond
(acond
  ((nth-value 1 (scan pattern1 input)) (values 'pattern1 it))
  ((nth-value 1 (scan pattern2 input)) (values 'pattern2 it)))

Or:

(labels ((match-end (pattern)
           (nth-value 1 (scan pattern input))))
  (acond ((match-end pattern1) (values 'pattern1 it))
         ((match-end pattern2) (values 'pattern2 it))))

Or if there are to be a whole bunch of these clauses:

;; only a macro so that you don't have to type "patternx" more than once
(macrolet ((end-of-match pattern)
	   (let ((result (gensym)))
	     `(let ((,result `(nth-value 1 (scan ,pattern input))))
		(and ,result (list ',pattern ,result)))))
  (or (end-of-match pattern1) (end-of-match pattern2)))

HTH,
Bill
From: bradb
Subject: Re: multiple-value-bind and cond
Date: 
Message-ID: <1158361216.433200.282910@i42g2000cwa.googlegroups.com>
Bill Atkins wrote:
> "bradb" <··············@gmail.com> writes:
>
> > I'm using CL-PPCRE (thanks a lot Edi!) to parse a string multiple times
> > looking for a match.  I may need to parse the same input string against
> > a couple of different patterns in order to classify the input string.
> > I also want to know where that match ends.  I really want to do
> > something like:
> >
> > (cond
> >  ((scan pattern1 input) (values 'pattern1 end))
> >  ((scan pattern2 input) (values 'pattern2 end)))
> >
> > Normally you would obtain end by doing:
> > (multiple-value-bind (start end) (scan pattern1 input)) if pattern1 is
> > not matched, then start and end will be NIL.
> > I can't think of any nice way to code this, you either need to
> > (multiple-value-bind (start end) (scan pattern1 input)
> >  (when start (return (values 'pattern1 end))))
> > (multiple-value-bind (start end) (scan pattern2 input)
> >  (when start (return (values 'pattern2 end))))
> >
> > Which is a bit ugly and imperative.
> >
> > or do the scan twice as
> > (cond
> >  ((scan pattern1 input) (multiple-value-bind (start end) (scan pattern1
> > input) (values 'pattern1 end))))
> > Which is just plain ugly.
> >
> > Any thoughts on a nice way to structure this kind of problem?
> >
> > Cheers
> > Brad
>
> Hmm:
>
> ;; acond = anaphoric cond
> (acond
>   ((nth-value 1 (scan pattern1 input)) (values 'pattern1 it))
>   ((nth-value 1 (scan pattern2 input)) (values 'pattern2 it)))
>
> Or:
>
> (labels ((match-end (pattern)
>            (nth-value 1 (scan pattern input))))
>   (acond ((match-end pattern1) (values 'pattern1 it))
>          ((match-end pattern2) (values 'pattern2 it))))
>
> Or if there are to be a whole bunch of these clauses:
>
> ;; only a macro so that you don't have to type "patternx" more than once
> (macrolet ((end-of-match pattern)
> 	   (let ((result (gensym)))
> 	     `(let ((,result `(nth-value 1 (scan ,pattern input))))
> 		(and ,result (list ',pattern ,result)))))
>   (or (end-of-match pattern1) (end-of-match pattern2)))
>
> HTH,
> Bill

Thanks for the insight guys.  I think I will probably go with Pascal's
loop solution - both code forms look about as pretty/ugly as each
other, and writing a loop means I don't have to write a macro.
Of course, my particular case is a bit lucky because all my cases are
of similar form - otherwise the anaphoric cond would be a good idea.
Assuming for a moment though that I were to write an ACOND style macro,
is it possible to let the anaphor be multiple values?  Ie,  (IT is the
anaphor)

(ACOND
 ((scan pattern1 input) (nth-value 1 it)))

Cheers
Brad
From: Bill Atkins
Subject: Re: multiple-value-bind and cond
Date: 
Message-ID: <m2psdwbu0p.fsf@machamp-218.dynamic.rpi.edu>
"bradb" <··············@gmail.com> writes:

> Bill Atkins wrote:
>> "bradb" <··············@gmail.com> writes:
>>
>> > I'm using CL-PPCRE (thanks a lot Edi!) to parse a string multiple times
>> > looking for a match.  I may need to parse the same input string against
>> > a couple of different patterns in order to classify the input string.
>> > I also want to know where that match ends.  I really want to do
>> > something like:
>> >
>> > (cond
>> >  ((scan pattern1 input) (values 'pattern1 end))
>> >  ((scan pattern2 input) (values 'pattern2 end)))
>> >
>> > Normally you would obtain end by doing:
>> > (multiple-value-bind (start end) (scan pattern1 input)) if pattern1 is
>> > not matched, then start and end will be NIL.
>> > I can't think of any nice way to code this, you either need to
>> > (multiple-value-bind (start end) (scan pattern1 input)
>> >  (when start (return (values 'pattern1 end))))
>> > (multiple-value-bind (start end) (scan pattern2 input)
>> >  (when start (return (values 'pattern2 end))))
>> >
>> > Which is a bit ugly and imperative.
>> >
>> > or do the scan twice as
>> > (cond
>> >  ((scan pattern1 input) (multiple-value-bind (start end) (scan pattern1
>> > input) (values 'pattern1 end))))
>> > Which is just plain ugly.
>> >
>> > Any thoughts on a nice way to structure this kind of problem?
>> >
>> > Cheers
>> > Brad
>>
>> Hmm:
>>
>> ;; acond = anaphoric cond
>> (acond
>>   ((nth-value 1 (scan pattern1 input)) (values 'pattern1 it))
>>   ((nth-value 1 (scan pattern2 input)) (values 'pattern2 it)))
>>
>> Or:
>>
>> (labels ((match-end (pattern)
>>            (nth-value 1 (scan pattern input))))
>>   (acond ((match-end pattern1) (values 'pattern1 it))
>>          ((match-end pattern2) (values 'pattern2 it))))
>>
>> Or if there are to be a whole bunch of these clauses:
>>
>> ;; only a macro so that you don't have to type "patternx" more than once
>> (macrolet ((end-of-match pattern)
>> 	   (let ((result (gensym)))
>> 	     `(let ((,result `(nth-value 1 (scan ,pattern input))))
>> 		(and ,result (list ',pattern ,result)))))
>>   (or (end-of-match pattern1) (end-of-match pattern2)))
>>
>> HTH,
>> Bill
>
> Thanks for the insight guys.  I think I will probably go with Pascal's
> loop solution - both code forms look about as pretty/ugly as each
> other, and writing a loop means I don't have to write a macro.
> Of course, my particular case is a bit lucky because all my cases are
> of similar form - otherwise the anaphoric cond would be a good idea.
> Assuming for a moment though that I were to write an ACOND style macro,
> is it possible to let the anaphor be multiple values?  Ie,  (IT is the
> anaphor)
>
> (ACOND
>  ((scan pattern1 input) (nth-value 1 it)))
>
> Cheers
> Brad

You could include something vaguely this in the expansion of each
clause of your ACOND:

 ;; untested!
 `(... (symbol-macrolet ((it (funcall (lambda () ,form))))
            ,consequent))

It seems to me that's the only way this could work, since you lose
every value but the first when you assign a multi-valued expression to
IT.

CL-USER> (defun foo ()
	   (values 0 1 2))
FOO
CL-USER> (symbol-macrolet ((it (funcall (lambda () (foo)))))
	   (nth-value 1 it))
1

HTH,
Bill
From: Bill Atkins
Subject: Re: multiple-value-bind and cond
Date: 
Message-ID: <m2zmd0af1y.fsf@machamp-218.dynamic.rpi.edu>
Bill Atkins <······@rpi.edu> writes:

> "bradb" <··············@gmail.com> writes:
>
>> Bill Atkins wrote:
>>> "bradb" <··············@gmail.com> writes:
>>>
>>> > I'm using CL-PPCRE (thanks a lot Edi!) to parse a string multiple times
>>> > looking for a match.  I may need to parse the same input string against
>>> > a couple of different patterns in order to classify the input string.
>>> > I also want to know where that match ends.  I really want to do
>>> > something like:
>>> >
>>> > (cond
>>> >  ((scan pattern1 input) (values 'pattern1 end))
>>> >  ((scan pattern2 input) (values 'pattern2 end)))
>>> >
>>> > Normally you would obtain end by doing:
>>> > (multiple-value-bind (start end) (scan pattern1 input)) if pattern1 is
>>> > not matched, then start and end will be NIL.
>>> > I can't think of any nice way to code this, you either need to
>>> > (multiple-value-bind (start end) (scan pattern1 input)
>>> >  (when start (return (values 'pattern1 end))))
>>> > (multiple-value-bind (start end) (scan pattern2 input)
>>> >  (when start (return (values 'pattern2 end))))
>>> >
>>> > Which is a bit ugly and imperative.
>>> >
>>> > or do the scan twice as
>>> > (cond
>>> >  ((scan pattern1 input) (multiple-value-bind (start end) (scan pattern1
>>> > input) (values 'pattern1 end))))
>>> > Which is just plain ugly.
>>> >
>>> > Any thoughts on a nice way to structure this kind of problem?
>>> >
>>> > Cheers
>>> > Brad
>>>
>>> Hmm:
>>>
>>> ;; acond = anaphoric cond
>>> (acond
>>>   ((nth-value 1 (scan pattern1 input)) (values 'pattern1 it))
>>>   ((nth-value 1 (scan pattern2 input)) (values 'pattern2 it)))
>>>
>>> Or:
>>>
>>> (labels ((match-end (pattern)
>>>            (nth-value 1 (scan pattern input))))
>>>   (acond ((match-end pattern1) (values 'pattern1 it))
>>>          ((match-end pattern2) (values 'pattern2 it))))
>>>
>>> Or if there are to be a whole bunch of these clauses:
>>>
>>> ;; only a macro so that you don't have to type "patternx" more than once
>>> (macrolet ((end-of-match pattern)
>>> 	   (let ((result (gensym)))
>>> 	     `(let ((,result `(nth-value 1 (scan ,pattern input))))
>>> 		(and ,result (list ',pattern ,result)))))
>>>   (or (end-of-match pattern1) (end-of-match pattern2)))
>>>
>>> HTH,
>>> Bill
>>
>> Thanks for the insight guys.  I think I will probably go with Pascal's
>> loop solution - both code forms look about as pretty/ugly as each
>> other, and writing a loop means I don't have to write a macro.
>> Of course, my particular case is a bit lucky because all my cases are
>> of similar form - otherwise the anaphoric cond would be a good idea.
>> Assuming for a moment though that I were to write an ACOND style macro,
>> is it possible to let the anaphor be multiple values?  Ie,  (IT is the
>> anaphor)
>>
>> (ACOND
>>  ((scan pattern1 input) (nth-value 1 it)))
>>
>> Cheers
>> Brad
>
> You could include something vaguely this in the expansion of each
> clause of your ACOND:
>
>  ;; untested!
>  `(... (symbol-macrolet ((it (funcall (lambda () ,form))))
>             ,consequent))
>
> It seems to me that's the only way this could work, since you lose
> every value but the first when you assign a multi-valued expression to
> IT.
>
> CL-USER> (defun foo ()
> 	   (values 0 1 2))
> FOO
> CL-USER> (symbol-macrolet ((it (funcall (lambda () (foo)))))
> 	   (nth-value 1 it))
> 1
>
> HTH,
> Bill

(defmacro multiple-value-acond (&rest clauses)
  (if (null clauses)
      nil
      (destructuring-bind (test &rest consequents) (first clauses)
	
	`(symbol-macrolet ((it (funcall (lambda () ,test))))
	   (if it
	       (progn
		 ,@consequents)
	       (multiple-value-acond ,(rest clauses)))))))

(multiple-value-acond
 ((round 34.5)
  (nth-value 1 it)))
;; ==> 0.5
From: bradb
Subject: Re: multiple-value-bind and cond
Date: 
Message-ID: <1158363874.972755.321500@i3g2000cwc.googlegroups.com>
Bill Atkins wrote:
> Bill Atkins <······@rpi.edu> writes:
>
> > "bradb" <··············@gmail.com> writes:
> >
> >> Bill Atkins wrote:
> >>> "bradb" <··············@gmail.com> writes:
> >>>
> >>> > I'm using CL-PPCRE (thanks a lot Edi!) to parse a string multiple times
> >>> > looking for a match.  I may need to parse the same input string against
> >>> > a couple of different patterns in order to classify the input string.
> >>> > I also want to know where that match ends.  I really want to do
> >>> > something like:
> >>> >
> >>> > (cond
> >>> >  ((scan pattern1 input) (values 'pattern1 end))
> >>> >  ((scan pattern2 input) (values 'pattern2 end)))
> >>> >
> >>> > Normally you would obtain end by doing:
> >>> > (multiple-value-bind (start end) (scan pattern1 input)) if pattern1 is
> >>> > not matched, then start and end will be NIL.
> >>> > I can't think of any nice way to code this, you either need to
> >>> > (multiple-value-bind (start end) (scan pattern1 input)
> >>> >  (when start (return (values 'pattern1 end))))
> >>> > (multiple-value-bind (start end) (scan pattern2 input)
> >>> >  (when start (return (values 'pattern2 end))))
> >>> >
> >>> > Which is a bit ugly and imperative.
> >>> >
> >>> > or do the scan twice as
> >>> > (cond
> >>> >  ((scan pattern1 input) (multiple-value-bind (start end) (scan pattern1
> >>> > input) (values 'pattern1 end))))
> >>> > Which is just plain ugly.
> >>> >
> >>> > Any thoughts on a nice way to structure this kind of problem?
> >>> >
> >>> > Cheers
> >>> > Brad
> >>>
> >>> Hmm:
> >>>
> >>> ;; acond = anaphoric cond
> >>> (acond
> >>>   ((nth-value 1 (scan pattern1 input)) (values 'pattern1 it))
> >>>   ((nth-value 1 (scan pattern2 input)) (values 'pattern2 it)))
> >>>
> >>> Or:
> >>>
> >>> (labels ((match-end (pattern)
> >>>            (nth-value 1 (scan pattern input))))
> >>>   (acond ((match-end pattern1) (values 'pattern1 it))
> >>>          ((match-end pattern2) (values 'pattern2 it))))
> >>>
> >>> Or if there are to be a whole bunch of these clauses:
> >>>
> >>> ;; only a macro so that you don't have to type "patternx" more than once
> >>> (macrolet ((end-of-match pattern)
> >>> 	   (let ((result (gensym)))
> >>> 	     `(let ((,result `(nth-value 1 (scan ,pattern input))))
> >>> 		(and ,result (list ',pattern ,result)))))
> >>>   (or (end-of-match pattern1) (end-of-match pattern2)))
> >>>
> >>> HTH,
> >>> Bill
> >>
> >> Thanks for the insight guys.  I think I will probably go with Pascal's
> >> loop solution - both code forms look about as pretty/ugly as each
> >> other, and writing a loop means I don't have to write a macro.
> >> Of course, my particular case is a bit lucky because all my cases are
> >> of similar form - otherwise the anaphoric cond would be a good idea.
> >> Assuming for a moment though that I were to write an ACOND style macro,
> >> is it possible to let the anaphor be multiple values?  Ie,  (IT is the
> >> anaphor)
> >>
> >> (ACOND
> >>  ((scan pattern1 input) (nth-value 1 it)))
> >>
> >> Cheers
> >> Brad
> >
> > You could include something vaguely this in the expansion of each
> > clause of your ACOND:
> >
> >  ;; untested!
> >  `(... (symbol-macrolet ((it (funcall (lambda () ,form))))
> >             ,consequent))
> >
> > It seems to me that's the only way this could work, since you lose
> > every value but the first when you assign a multi-valued expression to
> > IT.
> >
> > CL-USER> (defun foo ()
> > 	   (values 0 1 2))
> > FOO
> > CL-USER> (symbol-macrolet ((it (funcall (lambda () (foo)))))
> > 	   (nth-value 1 it))
> > 1
> >
> > HTH,
> > Bill
>
> (defmacro multiple-value-acond (&rest clauses)
>   (if (null clauses)
>       nil
>       (destructuring-bind (test &rest consequents) (first clauses)
>
> 	`(symbol-macrolet ((it (funcall (lambda () ,test))))
> 	   (if it
> 	       (progn
> 		 ,@consequents)
> 	       (multiple-value-acond ,(rest clauses)))))))
>
> (multiple-value-acond
>  ((round 34.5)
>   (nth-value 1 it)))
> ;; ==> 0.5

Of course 'IT' can potentially get evaluated multiple times.  I looked
at the CLHS to see how multiple-value-call/bind works, and I suspect if
somebody was cleverer than me, they could possibly make the underlying
machinery work - but it would be a hell of a hairy macro.
I'm definately going with the LOOP solution, but the anaphoric cond is
an interesting diversion - though I guess all anaphors would have
problems when multiple values come into the picture.

Cheers
Brad
From: Bill Atkins
Subject: Re: multiple-value-bind and cond
Date: 
Message-ID: <m2fyesekgg.fsf@machamp-218.dynamic.rpi.edu>
"bradb" <··············@gmail.com> writes:

> Of course 'IT' can potentially get evaluated multiple times.  I looked

Oops!  Good point:

(defmacro multiple-value-acond (&rest clauses)
  (if (null clauses)
      nil
      (destructuring-bind (test &rest consequents) (first clauses)
	(if (null consequents)
	    test
	    (let ((it-sym (gensym)) (it-sym-suppliedp (gensym)))
	      `(let (,it-sym ,it-sym-suppliedp)
		 (symbol-macrolet
		     ((it (funcall (lambda ()
				     (if ,it-sym-suppliedp
					 ,it-sym
					 (setf ,it-sym ,test
					       ,it-sym-suppliedp t))))))
		   (if it
		       (progn
			 ,@consequents)
		       (multiple-value-acond ,@(rest clauses))))))))))
CL-USER> (multiple-value-acond
 ((prog1 (round 34.5) (princ 'me-printed))
  (nth-value 1 it)))
ME-PRINTED
NIL
CL-USER> 
From: Pascal Bourguignon
Subject: Re: multiple-value-bind and cond
Date: 
Message-ID: <877j044oy0.fsf@thalassa.informatimago.com>
Bill Atkins <······@rpi.edu> writes:

> "bradb" <··············@gmail.com> writes:
>
>> Of course 'IT' can potentially get evaluated multiple times.  I looked
>
> Oops!  Good point:
>
> (defmacro multiple-value-acond (&rest clauses)
>   (if (null clauses)
>       nil
>       (destructuring-bind (test &rest consequents) (first clauses)
> 	(if (null consequents)
> 	    test
> 	    (let ((it-sym (gensym)) (it-sym-suppliedp (gensym)))
> 	      `(let (,it-sym ,it-sym-suppliedp)
> 		 (symbol-macrolet
> 		     ((it (funcall (lambda ()
> 				     (if ,it-sym-suppliedp
> 					 ,it-sym
> 					 (setf ,it-sym ,test
> 					       ,it-sym-suppliedp t))))))
> 		   (if it
> 		       (progn
> 			 ,@consequents)
> 		       (multiple-value-acond ,@(rest clauses))))))))))
> CL-USER> (multiple-value-acond
>  ((prog1 (round 34.5) (princ 'me-printed))
>   (nth-value 1 it)))
> ME-PRINTED
> NIL
> CL-USER> 


(defmacro acond (&rest clauses)
  (if (null clauses)
      nil
      (destructuring-bind (test &rest consequents) (first clauses)
        (if (null consequents)
            test
            (let ((them-sym (gensym)) (them-sym-suppliedp (gensym)))
              `(let (,them-sym ,them-sym-suppliedp)
                 (symbol-macrolet
                     ((them
                       ((lambda ()
                          (if ,them-sym-suppliedp
                              ,them-sym
                              (setf ,them-sym-suppliedp t
                                    ,them-sym (multiple-value-list ,test))))))
                      (it (first them)))
                   (if it
                       (progn ,@consequents)
                       (acond ,@(rest clauses))))))))))


(acond
      ((multiple-value-prog1 (round 34.5) (princ 'me-printed))
       (print it) (print them)))
ME-PRINTED
34 
(34 0.5) 
--> (34 0.5)



-- 
__Pascal Bourguignon__                     http://www.informatimago.com/
Until real software engineering is developed, the next best practice
is to develop with a dynamic system that has extreme late binding in
all aspects. The first system to really do this in an important way
is Lisp. -- Alan Kay
From: bradb
Subject: Re: multiple-value-bind and cond
Date: 
Message-ID: <1158368963.637509.29540@e3g2000cwe.googlegroups.com>
Pascal Bourguignon wrote:
> Bill Atkins <······@rpi.edu> writes:
>
> > "bradb" <··············@gmail.com> writes:
> >
> >> Of course 'IT' can potentially get evaluated multiple times.  I looked
> >
> > Oops!  Good point:
> >
> > (defmacro multiple-value-acond (&rest clauses)
> >   (if (null clauses)
> >       nil
> >       (destructuring-bind (test &rest consequents) (first clauses)
> > 	(if (null consequents)
> > 	    test
> > 	    (let ((it-sym (gensym)) (it-sym-suppliedp (gensym)))
> > 	      `(let (,it-sym ,it-sym-suppliedp)
> > 		 (symbol-macrolet
> > 		     ((it (funcall (lambda ()
> > 				     (if ,it-sym-suppliedp
> > 					 ,it-sym
> > 					 (setf ,it-sym ,test
> > 					       ,it-sym-suppliedp t))))))
> > 		   (if it
> > 		       (progn
> > 			 ,@consequents)
> > 		       (multiple-value-acond ,@(rest clauses))))))))))
> > CL-USER> (multiple-value-acond
> >  ((prog1 (round 34.5) (princ 'me-printed))
> >   (nth-value 1 it)))
> > ME-PRINTED
> > NIL
> > CL-USER>
>
>
> (defmacro acond (&rest clauses)
>   (if (null clauses)
>       nil
>       (destructuring-bind (test &rest consequents) (first clauses)
>         (if (null consequents)
>             test
>             (let ((them-sym (gensym)) (them-sym-suppliedp (gensym)))
>               `(let (,them-sym ,them-sym-suppliedp)
>                  (symbol-macrolet
>                      ((them
>                        ((lambda ()
>                           (if ,them-sym-suppliedp
>                               ,them-sym
>                               (setf ,them-sym-suppliedp t
>                                     ,them-sym (multiple-value-list ,test))))))
>                       (it (first them)))
>                    (if it
>                        (progn ,@consequents)
>                        (acond ,@(rest clauses))))))))))
>
>
> (acond
>       ((multiple-value-prog1 (round 34.5) (princ 'me-printed))
>        (print it) (print them)))
> ME-PRINTED
> 34
> (34 0.5)
> --> (34 0.5)
Oh wow!  Thanks for that.  I think I've got another few years of Lisp
practice before I can get to that level of macrology, but I really like
that!

Cheers
Brad
From: Bill Atkins
Subject: Re: multiple-value-bind and cond
Date: 
Message-ID: <1158369154.949281.268470@k70g2000cwa.googlegroups.com>
Pascal Bourguignon wrote:
>
> (acond
>       ((multiple-value-prog1 (round 34.5) (princ 'me-printed))
>        (print it) (print them)))
> ME-PRINTED
> 34 
> (34 0.5) 
> --> (34 0.5)

Nice!
From: Alan Crowe
Subject: Re: multiple-value-bind and cond
Date: 
Message-ID: <86r6ybke1e.fsf@cawtech.freeserve.co.uk>
"bradb" <··············@gmail.com> writes:
> I'm definately going with the LOOP solution, but the anaphoric cond is
> an interesting diversion - though I guess all anaphors would have
> problems when multiple values come into the picture.

I think this is the wrong place for an anaphor. In general
you want to capture all of the values returned by your
predicate, so you want a binding form. Here is a
multiple-value-cond which is a binding form. You supply
names for variables to capture the multiple values returned
by the guards of the cond clauses and their bodies are
executed inside those bindings.


(defmacro multiple-value-cond (variables &body clauses)
  (reduce (lambda(clause nest)
            `(multiple-value-bind ,variables ,(first clause)
              (if ,(first variables)
                  ,(if (rest clause)
                       `(progn ,@(rest clause))
                       `(values ,@variables))
                  ,nest)))
          clauses
          :from-end t
          :initial-value (cons 'values 
                               ;; return same number of values
                               ;; as predicate only clause
                               (make-list (length variables)))))

Example use:


CL-USER> (defun scan (char string)
           "We need a predicate that returns multiple 
            useful values to make the example go" 
           (let ((first (position char string)))
             (if first
                 (values first
                         (or (position char 
                                       string 
                                       :start first
                                       :test-not #'eql)
                             (length string)))
                 nil)))
SCAN

CL-USER> (defun scan2 (main-char reserve-char string)
           (multiple-value-cond (start end)
             ((scan main-char string))
             ((scan reserve-char string)
              (format t "Falling back on ~C" reserve-char)
              (values start end))))
SCAN2

CL-USER> (scan2 #\a #\b "bbbaaa")
3
6

CL-USER> (scan2 #\a #\b "bbb")
Falling back on b

0
3

CL-USER> (scan2 #\a #\b "ccc")
NIL
NIL

Alan Crowe
Edinburgh
Scotland
From: Bill Atkins
Subject: Re: multiple-value-bind and cond
Date: 
Message-ID: <m2r6ybzp45.fsf@joshu.local>
Alan Crowe <····@cawtech.freeserve.co.uk> writes:

> "bradb" <··············@gmail.com> writes:
>> I'm definately going with the LOOP solution, but the anaphoric cond is
>> an interesting diversion - though I guess all anaphors would have
>> problems when multiple values come into the picture.
>
> I think this is the wrong place for an anaphor. In general
> you want to capture all of the values returned by your
> predicate, so you want a binding form. Here is a
> multiple-value-cond which is a binding form. You supply
> names for variables to capture the multiple values returned
> by the guards of the cond clauses and their bodies are
> executed inside those bindings.
>
>
> (defmacro multiple-value-cond (variables &body clauses)
>   (reduce (lambda(clause nest)
>             `(multiple-value-bind ,variables ,(first clause)
>               (if ,(first variables)
>                   ,(if (rest clause)
>                        `(progn ,@(rest clause))
>                        `(values ,@variables))
>                   ,nest)))
>           clauses
>           :from-end t
>           :initial-value (cons 'values 
>                                ;; return same number of values
>                                ;; as predicate only clause
>                                (make-list (length variables)))))
>
> Example use:
>
>
> CL-USER> (defun scan (char string)
>            "We need a predicate that returns multiple 
>             useful values to make the example go" 
>            (let ((first (position char string)))
>              (if first
>                  (values first
>                          (or (position char 
>                                        string 
>                                        :start first
>                                        :test-not #'eql)
>                              (length string)))
>                  nil)))
> SCAN
>
> CL-USER> (defun scan2 (main-char reserve-char string)
>            (multiple-value-cond (start end)
>              ((scan main-char string))
>              ((scan reserve-char string)
>               (format t "Falling back on ~C" reserve-char)
>               (values start end))))
> SCAN2
>
> CL-USER> (scan2 #\a #\b "bbbaaa")
> 3
> 6
>
> CL-USER> (scan2 #\a #\b "bbb")
> Falling back on b
>
> 0
> 3
>
> CL-USER> (scan2 #\a #\b "ccc")
> NIL
> NIL
>
> Alan Crowe
> Edinburgh
> Scotland

Well, I disagree.  I think the advantage of the original M-V-C is that
you can use it with forms that return differing numbers of values.  I
don't think it makes any sense to specify up front what values you're
expecting, because it's likely that not all of your forms will be in
that format (and if they are, why not just make a label that calls
that function and returns the appropriate value?).  For example:

(atkins/bourguignon-multiple-value-acond
  ((scan input pattern1) (cons (second them) 'foo))
  ((= (length input) 4) 'no-such-pattern)
  (t 'empty-pattern))

How do the second two clauses fit into the code you gave?  I prefer
the flexibility of IT/THEM to assuming that all clauses will return
the same number of clauses with the same corresponding meanings.  I
think your M-V-C more closely resembles a CASE than a COND, since it's
expecting that the values being considered will have more or less the
same meaning.

Bill
From: Alan Crowe
Subject: Re: multiple-value-bind and cond
Date: 
Message-ID: <86odtfjxso.fsf@cawtech.freeserve.co.uk>
Bill Atkins <······@rpi.edu> writes:

> Alan Crowe <····@cawtech.freeserve.co.uk> writes:
> > (defmacro multiple-value-cond (variables &body clauses)
> >   (reduce (lambda(clause nest)
> >             `(multiple-value-bind ,variables ,(first clause)
> >               (if ,(first variables)
> >                   ,(if (rest clause)
> >                        `(progn ,@(rest clause))
> >                        `(values ,@variables))
> >                   ,nest)))
> >           clauses
> >           :from-end t
> >           :initial-value (cons 'values 
> >                                ;; return same number of values
> >                                ;; as predicate only clause
> >                                (make-list (length variables)))))
> 
> Well, I disagree.  I think the advantage of the original M-V-C is that
> you can use it with forms that return differing numbers of values.  I
> don't think it makes any sense to specify up front what values you're
> expecting, because it's likely that not all of your forms will be in
> that format (and if they are, why not just make a label that calls
> that function and returns the appropriate value?).  For example:
> 
> (atkins/bourguignon-multiple-value-acond
>   ((scan input pattern1) (cons (second them) 'foo))
>   ((= (length input) 4) 'no-such-pattern)
>   (t 'empty-pattern))
> 
> How do the second two clauses fit into the code you gave?  I prefer
> the flexibility of IT/THEM to assuming that all clauses will return
> the same number of clauses with the same corresponding meanings.  I
> think your M-V-C more closely resembles a CASE than a COND, since it's
> expecting that the values being considered will have more or less the
> same meaning.

My multiple-value-cond inherits from multiple-value-bind the
property of not minding whether the value-form returns the
"correct" number of values.

From the spec

    Values-form is evaluated, and each of the vars is bound
    to the respective value returned by that form. If there
    are more vars than values returned, extra values of nil
    are given to the remaining vars. If there are more
    values than vars, the excess values are discarded.

On the other hand, my multiple-value-cond does rather
suggest that all the values being considered will have more
or less the same meaning; that is where their mnemonic names
are supposed to come from. If they don't then my macro is
not really doing its job of making the code clearer.

Also if the names are not always used, I need an ignorable
declaration to suppress spurious warnings.

             `(multiple-value-bind ,variables ,(first clause)
add --->       (declare (ignorable ,@variables))
               (if ,(first variables)
 
or perhaps atkins/bourguignon-multiple-value-acond instead.

Alan Crowe
Edinburgh
Scotland
From: Pascal Bourguignon
Subject: Re: multiple-value-bind and cond
Date: 
Message-ID: <87k6444um5.fsf@thalassa.informatimago.com>
"bradb" <··············@gmail.com> writes:

> I'm using CL-PPCRE (thanks a lot Edi!) to parse a string multiple times
> looking for a match.  I may need to parse the same input string against
> a couple of different patterns in order to classify the input string.
> I also want to know where that match ends.  I really want to do
> something like:
>
> (cond
>  ((scan pattern1 input) (values 'pattern1 end))
>  ((scan pattern2 input) (values 'pattern2 end)))
>
> Normally you would obtain end by doing:
> (multiple-value-bind (start end) (scan pattern1 input)) if pattern1 is
> not matched, then start and end will be NIL.
> I can't think of any nice way to code this, you either need to
> (multiple-value-bind (start end) (scan pattern1 input)
>  (when start (return (values 'pattern1 end))))
> (multiple-value-bind (start end) (scan pattern2 input)
>  (when start (return (values 'pattern2 end))))
>
> Which is a bit ugly and imperative.
>
> or do the scan twice as
> (cond
>  ((scan pattern1 input) (multiple-value-bind (start end) (scan pattern1
> input) (values 'pattern1 end))))
> Which is just plain ugly.
>
> Any thoughts on a nice way to structure this kind of problem?

Two solutions:

1- define a macro.

    (exercise left to the reader), or


2- use loops.

   (loop :named scan
         :for pattern :in (list pattern1 pattern2 ...)
         :do (multiple-value-bind (start end) (scan pattern input)
                  (when start (return-from scan (values pattern end)))))



-- 
A: Because it messes up the order in which people normally read text.
Q: Why is top-posting such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
__Pascal Bourguignon__                     http://www.informatimago.com/