[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.lisp

ignoring values using destructuring-bind

Jim Newton

10/22/2015 8:36:00 AM

Is there an automatic way using destructuring-bind to ignore certain values?
Here is a pattern that I use too often.

(destructing-bind ((a (b x1) x2 c)) data
(declare (ignore x1 x2))
...)

I'd like to write this something like the following.

(destructing-bind ((a (b :ignore) :ignore c)) data
...)

or

(destructing-bind ((a (b t) t c)) data
...)

14 Answers

Antsan

10/22/2015 10:26:00 AM

0

Am Donnerstag, 22. Oktober 2015 10:36:08 UTC+2 schrieb Jim Newton:
> Is there an automatic way using destructuring-bind to ignore certain values?
> Here is a pattern that I use too often.
>
> (destructing-bind ((a (b x1) x2 c)) data
> (declare (ignore x1 x2))
> ...)
>
> I'd like to write this something like the following.
>
> (destructing-bind ((a (b :ignore) :ignore c)) data
> ...)
>
> or
>
> (destructing-bind ((a (b t) t c)) data
> ...)

It's probably not the prettiest solution, but this is what I came up with:
(defmacro destructuring-bind? ((&rest lambda-list)
expression &body body)
(labels ((_make-ignored (obj) ; replaces every symbol beginning with an
; underscore with a gensym and collects these
; replaced symbols for ignoring
(etypecase obj
(symbol
(let ((name (symbol-name obj)))
(if (and (> (length name)
0)
(string= (elt name 0)
"_"))
(let ((gsym (gensym name)))
(values gsym (list gsym)))
(values obj '()))))
(cons
(loop with lambda-list = '()
with ignored = '()
for sub in obj
do
(multiple-value-bind (rep ign)
(_make-ignored sub)
(push rep lambda-list)
(setf ignored
(append ignored ign)))
finally (return
(values (nreverse lambda-list)
ignored))))
(null
(values nil nil)))))
(multiple-value-bind (lambda-list ignored)
(_make-ignored lambda-list)
`(destructuring-bind (,@lambda-list)
,expression
(declare (ignore ,@ignored))
,@body))))

Dimitri Fontaine

10/22/2015 11:57:00 AM

0

Jim Newton <jimka.issy@gmail.com> writes:
> Is there an automatic way using destructuring-bind to ignore certain values?
> Here is a pattern that I use too often.
>
> (destructing-bind ((a (b x1) x2 c)) data
> (declare (ignore x1 x2))
> ...)

See Metabang-bind lib, available in Quicklisp.

https://common-lisp.net/project/metabang-bind/user-...

Regards,
--
dim

Pascal Costanza

10/24/2015 10:03:00 AM

0

On 22/10/2015 10:36, Jim Newton wrote:
> Is there an automatic way using destructuring-bind to ignore certain values?
> Here is a pattern that I use too often.
>
> (destructing-bind ((a (b x1) x2 c)) data
> (declare (ignore x1 x2))
> ...)
>
> I'd like to write this something like the following.
>
> (destructing-bind ((a (b :ignore) :ignore c)) data
> ...)
>
> or
>
> (destructing-bind ((a (b t) t c)) data
> ...)
>

Not sure if it will work elegantly, but you could try to abuse the
destructuring feature of LOOP, which allows you to use NIL as a
pseudo-variable name for values you want to ignore.

(loop with ((a (b nil) nil c) data do ...)

See http://www.lispworks.com/documentation/HyperSpec/Body/...

You would have to prevent loop from looping, though, I guess... ;)

Pascal

--
My website: http:/...
Common Lisp Document Repository: http://cdr.eu...
Closer to MOP & ContextL: http://common-lisp.net/proje...
The views expressed are my own, and not those of my employer.

rpw3

10/24/2015 2:26:00 PM

0

Pascal Costanza <pc@p-cos.net> wrote:
+---------------
| Jim Newton wrote:
| > Is there an automatic way using destructuring-bind to ignore
| > certain values?
....
| Not sure if it will work elegantly, but you could try to abuse
| the destructuring feature of LOOP, which allows you to use NIL
| as a pseudo-variable name for values you want to ignore.
|
| (loop with ((a (b nil) nil c) data do ...)
|
| See http://www.lispworks.com/documentation/HyperSpec/Body/...
|
| You would have to prevent loop from looping, though, I guess... ;)
+---------------

Simple, just use RETURN instead of DO, e.g.:

> (let ((data '(1 (2 3) 4 5 6 7)))
(loop with (a (b nil) nil c) = data
return (list a b c)))

(1 2 5)
>

The above example also illustrates that extra source values
are ignored when destructuring, as per "6.1.1.7 Destructuring":

If there are more values than variables listed,
the extra values are discarded.

And you can also use "." for &REST:

> (let ((data '(1 (2 3) 4 5 6 7)))
(loop with (a (b nil) nil c . d) = data
return (list a b c d)))

(1 2 5 (6 7))
>


-Rob

p.s. Hmmm... In playing around with this, I noticed
that [much like MULTIPLE-VALUE-BIND] 6.1.1.7 also says:

If there are more variables in the variable list than there
are values in the values list, the remaining variables are
given a value of NIL.

But CMUCL-19c & CCL-1.6, at least, barf on that if you
use WITH, e.g.:

> (let ((data '(1 (2 3) 4 5)))
(loop with (a (b nil) nil c d e) = data
return (list a b c d e)))

Error while parsing arguments to DESTRUCTURING-BIND in LISP::DO-ARG-COUNT-ERROR:
Invalid number of elements in:
(1 (2 3) 4 5)
to satisfy lambda-list:
(A (B #:G1 . #:G2) #:G3 C D E &REST #:G4)
Expected at least 6, but got 4.
...[then into the debugger]...

Fortunately, using FOR instead of WITH works fine:

> (let ((data '(1 (2 3) 4 5)))
(loop for (a (b nil) nil c d e) = data
return (list a b c d e)))

(1 2 5 NIL NIL)
>

Not sure whether that's just a bug in CMUCL & CCL [common
anscestor in the LOOP source?] or if other implementations
have the same issue. From reading CLHS "Macro LOOP" I
thought that WITH & FOR *should* destructure exactly
the same...

-----
Rob Warnock <rpw3@rpw3.org>
627 26th Avenue <http://rpw...
San Mateo, CA 94403

Kaz Kylheku

10/24/2015 2:40:00 PM

0

On 2015-10-24, Rob Warnock <rpw3@rpw3.org> wrote:
> Pascal Costanza <pc@p-cos.net> wrote:
> +---------------
>| Jim Newton wrote:
>| > Is there an automatic way using destructuring-bind to ignore
>| > certain values?
> ...
>| Not sure if it will work elegantly, but you could try to abuse
>| the destructuring feature of LOOP, which allows you to use NIL
>| as a pseudo-variable name for values you want to ignore.
>|
>| (loop with ((a (b nil) nil c) data do ...)
>|
>| See http://www.lispworks.com/documentation/HyperSpec/Body/...
>|
>| You would have to prevent loop from looping, though, I guess... ;)
> +---------------
>
> Simple, just use RETURN instead of DO, e.g.:
>
> > (let ((data '(1 (2 3) 4 5 6 7)))
> (loop with (a (b nil) nil c) = data
> return (list a b c)))
>
> (1 2 5)
> >

(defmacro loop-style-bind (pattern form &body body)
`(loop with ,pattern = ,form
return (progn ,@body)))

rpw3

10/24/2015 3:43:00 PM

0

Kaz Kylheku <kaz@kylheku.com> wrote:
+---------------
| Rob Warnock <rpw3@rpw3.org> wrote:
| > Pascal Costanza <pc@p-cos.net> wrote:
| > > You would have to prevent loop from looping, though, I guess... ;)
| >
| > Simple, just use RETURN instead of DO, e.g.:
| >
| > > (let ((data '(1 (2 3) 4 5 6 7)))
| > (loop with (a (b nil) nil c) = data
| > return (list a b c)))
| >
| > (1 2 5)
| > >
|
| (defmacro loop-style-bind (pattern form &body body)
| `(loop with ,pattern = ,form
| return (progn ,@body)))
+---------------

Fun! ;-}

But for the reasons mentioned later in my reply,
it probably does need to use FOR instead of WITH...


-Rob

-----
Rob Warnock <rpw3@rpw3.org>
627 26th Avenue <http://rpw...
San Mateo, CA 94403

Pascal Costanza

10/24/2015 3:53:00 PM

0

On 24/10/2015 16:39, Kaz Kylheku wrote:
> On 2015-10-24, Rob Warnock <rpw3@rpw3.org> wrote:
>> Pascal Costanza <pc@p-cos.net> wrote:
>> +---------------
>> | Jim Newton wrote:
>> | > Is there an automatic way using destructuring-bind to ignore
>> | > certain values?
>> ...
>> | Not sure if it will work elegantly, but you could try to abuse
>> | the destructuring feature of LOOP, which allows you to use NIL
>> | as a pseudo-variable name for values you want to ignore.
>> |
>> | (loop with ((a (b nil) nil c) data do ...)
>> |
>> | See http://www.lispworks.com/documentation/HyperSpec/Body/...
>> |
>> | You would have to prevent loop from looping, though, I guess... ;)
>> +---------------
>>
>> Simple, just use RETURN instead of DO, e.g.:
>>
>> > (let ((data '(1 (2 3) 4 5 6 7)))
>> (loop with (a (b nil) nil c) = data
>> return (list a b c)))
>>
>> (1 2 5)
>> >
>
> (defmacro loop-style-bind (pattern form &body body)
> `(loop with ,pattern = ,form
> return (progn ,@body)))
>

....unfortunately, this macro is not very hygienic... :(

Pascal

--
My website: http:/...
Common Lisp Document Repository: http://cdr.eu...
Closer to MOP & ContextL: http://common-lisp.net/proje...
The views expressed are my own, and not those of my employer.

Marco Antoniotti

10/24/2015 5:48:00 PM

0

On Saturday, October 24, 2015 at 12:03:21 PM UTC+2, Pascal Costanza wrote:
> On 22/10/2015 10:36, Jim Newton wrote:
> > Is there an automatic way using destructuring-bind to ignore certain values?
> > Here is a pattern that I use too often.
> >
> > (destructing-bind ((a (b x1) x2 c)) data
> > (declare (ignore x1 x2))
> > ...)
> >
> > I'd like to write this something like the following.
> >
> > (destructing-bind ((a (b :ignore) :ignore c)) data
> > ...)
> >
> > or
> >
> > (destructing-bind ((a (b t) t c)) data
> > ...)
> >
>
> Not sure if it will work elegantly, but you could try to abuse the
> destructuring feature of LOOP, which allows you to use NIL as a
> pseudo-variable name for values you want to ignore.
>
> (loop with ((a (b nil) nil c) data do ...)
>
> See http://www.lispworks.com/documentation/HyperSpec/Body/...
>
> You would have to prevent loop from looping, though, I guess... ;)
>
> Pascal

I second that. It would be very nice if D-B (*) would use the NIL convention of LOOP.

Cheers
--
MA

(*) Of course then we can veer off a tangent and use NIL in macro lambda lists :)

Kaz Kylheku

10/25/2015 1:10:00 AM

0

On 2015-10-24, Marco Antoniotti <marcoxa@gmail.com> wrote:
> On Saturday, October 24, 2015 at 12:03:21 PM UTC+2, Pascal Costanza wrote:
>> On 22/10/2015 10:36, Jim Newton wrote:
>> > Is there an automatic way using destructuring-bind to ignore certain values?
>> > Here is a pattern that I use too often.
>> >
>> > (destructing-bind ((a (b x1) x2 c)) data
>> > (declare (ignore x1 x2))
>> > ...)
>> >
>> > I'd like to write this something like the following.
>> >
>> > (destructing-bind ((a (b :ignore) :ignore c)) data
>> > ...)
>> >
>> > or
>> >
>> > (destructing-bind ((a (b t) t c)) data
>> > ...)
>> >
>>
>> Not sure if it will work elegantly, but you could try to abuse the
>> destructuring feature of LOOP, which allows you to use NIL as a
>> pseudo-variable name for values you want to ignore.
>>
>> (loop with ((a (b nil) nil c) data do ...)
>>
>> See http://www.lispworks.com/documentation/HyperSpec/Body/...
>>
>> You would have to prevent loop from looping, though, I guess... ;)
>>
>> Pascal
>
> I second that. It would be very nice if D-B (*) would use the NIL convention of LOOP.

Sorta. There are issues.

Firstly (a . nil) is exactly the same thing as (a).

And (a) must not match (1 . 2). Whereas (a . nil) expresses "match (1 . 2),
and don't give me the 2".

And of course nil is (). Really, it should match the () object.

It already does:

(destructuring-bind nil nil) -> nil

(destructuring-bind nil '(a)) ;; error

Using nil as a match all is inconsistent with the type system.

Basically, all arrows point to T. Figuratively and literally.

The match-anything and ignore it variable ought to be T.
(I note that the idea had already been given upthread.)

Thus (a . t) matches any cons cell, and a gets the car.

Used alone, t matches anything and ignores it; so

(destructuring-bind t form forms...)

is the NOOP form: t matches anything and doesn't bind a variable,
so this is equivalent to:

(progn form forms...)

Really, though, you just want a decent pattern matching macro
with some bells and whistles for matching constant structure,
and by type or arbitrary predicates and such.

Marco Antoniotti

10/25/2015 11:16:00 AM

0

On Sunday, October 25, 2015 at 2:10:02 AM UTC+1, Kaz Kylheku wrote:
> On 2015-10-24, Marco Antoniotti <marcoxa@gmail.com> wrote:
> > On Saturday, October 24, 2015 at 12:03:21 PM UTC+2, Pascal Costanza wrote:
> >> On 22/10/2015 10:36, Jim Newton wrote:
> >> > Is there an automatic way using destructuring-bind to ignore certain values?
> >> > Here is a pattern that I use too often.
> >> >
> >> > (destructing-bind ((a (b x1) x2 c)) data
> >> > (declare (ignore x1 x2))
> >> > ...)
> >> >
> >> > I'd like to write this something like the following.
> >> >
> >> > (destructing-bind ((a (b :ignore) :ignore c)) data
> >> > ...)
> >> >
> >> > or
> >> >
> >> > (destructing-bind ((a (b t) t c)) data
> >> > ...)
> >> >
> >>
> >> Not sure if it will work elegantly, but you could try to abuse the
> >> destructuring feature of LOOP, which allows you to use NIL as a
> >> pseudo-variable name for values you want to ignore.
> >>
> >> (loop with ((a (b nil) nil c) data do ...)
> >>
> >> See http://www.lispworks.com/documentation/HyperSpec/Body/...
> >>
> >> You would have to prevent loop from looping, though, I guess... ;)
> >>
> >> Pascal
> >
> > I second that. It would be very nice if D-B (*) would use the NIL convention of LOOP.
>
> Sorta. There are issues.
>
> Firstly (a . nil) is exactly the same thing as (a).
>
> And (a) must not match (1 . 2). Whereas (a . nil) expresses "match (1 . 2),
> and don't give me the 2".
>
> And of course nil is (). Really, it should match the () object.

D-B does not do "matching". It destructures lists that are Destructuring-Bind Lambda Lists.

The issue is the naming of the variables inside it and the NIL convention used in LOOP may be a good one. AFAIU, the NIL convention may in fact be more problematic in LOOP (for the joy of WJ's 3:) ) than it would be in D-B.

Cheers
--
MA