I am an avid contract bridge player and Common Lisp programmer. In some
bridge programming I am doing, I use the following kind of code in cases
where I may have to generate thousands of deals before finding one which
satisfies requirements, so I am concerned about efficiency and avoiding
consing. I am interested in comments and/or suggestions about my use of the
global I call *deal* in this and related situations.
(defconstant *deal* (make-array '(4 4) :initial-element 0)
"a 4x4 array representing a bridge deal, where each element
is a 13-bit integer")
(defun deal ()
(make-new-deal-destructively-modifying-*deal*)
*deal*)
(defun copy-deal (&optional (deal *deal*))
(let ((new-deal (make-array '(4 4))))
(dotimes (hand 4)
(dotimes (suit 4)
(setf (aref new-deal hand suit) (aref deal hand suit))))
new-deal))
(defun find-deal-satisfying-requirements (requirements)
(loop for next-deal = (deal)
until (satisfies-requirements next-deal requirements)
finally return (copy-deal)))
I welcome your ideas. Thanks.
Jim Bushnell
"Jim Bushnell" <···········@comcast.net> wrote in message
····························@bin2.nnrp.aus1.giganews.com...
> I am an avid contract bridge player and Common Lisp programmer. In some
> bridge programming I am doing, I use the following kind of code in cases
> where I may have to generate thousands of deals before finding one which
> satisfies requirements, so I am concerned about efficiency and avoiding
> consing.
Why do you believe that consing is a problem? With appropriate garbage
collector settings, the overhead for consing should be down in the noise.
In article <······················@typhoon.ne.ipsvc.net>,
Joe Marshall <·············@attbi.com> wrote:
>
>"Jim Bushnell" <···········@comcast.net> wrote in message
>····························@bin2.nnrp.aus1.giganews.com...
>> I am an avid contract bridge player and Common Lisp programmer. In some
>> bridge programming I am doing, I use the following kind of code in cases
>> where I may have to generate thousands of deals before finding one which
>> satisfies requirements, so I am concerned about efficiency and avoiding
>> consing.
>
>Why do you believe that consing is a problem? With appropriate garbage
>collector settings, the overhead for consing should be down in the noise.
Agreed. Most implementations have ephemeral (aka generational) garbage
collectors, which are optimized to clean up short-lived data. Your program
matches that model quite well.
--
Barry Margolin, ······@genuity.net
Genuity, Woburn, MA
*** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups.
Please DON'T copy followups to me -- I'll assume it wasn't posted to the group.
From: james anderson
Subject: Re: Is this an appropriate use of globals?
Date:
Message-ID: <3CE3B88B.A3C029F2@setf.de>
an alternative is to permit the caller to determine the extent of the
datum and to default to a new instance with indefinite extent.
something like
(defconstant *deal* (make-array '(4 4) :initial-element 0)
"a 4x4 array representing a bridge deal, where each element
is a 13-bit integer")
(defun deal (&optional (a-deal (copy-deal)))
(make-new-deal-destructively-modifying-deal a-deal)
a-deal)
(defun copy-deal (&optional (deal *deal*))
(let ((new-deal (make-array '(4 4))))
(dotimes (hand 4)
(dotimes (suit 4)
(setf (aref new-deal hand suit) (aref deal hand suit))))
new-deal))
(defun find-deal-satisfying-requirements (requirements &optional (a-deal (copy-deal)))
(if (satisfies-requirements (deal a-deal) requirements)
a-deal
(find-deal-satisfying-requirements requirements a-deal)))
...
Jim Bushnell wrote:
>
> I am an avid contract bridge player and Common Lisp programmer. In some
> bridge programming I am doing, I use the following kind of code in cases
> where I may have to generate thousands of deals before finding one which
> satisfies requirements, so I am concerned about efficiency and avoiding
> consing. I am interested in comments and/or suggestions about my use of the
> global I call *deal* in this and related situations.
>
> (defconstant *deal* (make-array '(4 4) :initial-element 0)
> "a 4x4 array representing a bridge deal, where each element
> is a 13-bit integer")
>
> (defun deal ()
> (make-new-deal-destructively-modifying-*deal*)
> *deal*)
>
> (defun copy-deal (&optional (deal *deal*))
> (let ((new-deal (make-array '(4 4))))
> (dotimes (hand 4)
> (dotimes (suit 4)
> (setf (aref new-deal hand suit) (aref deal hand suit))))
> new-deal))
>
> (defun find-deal-satisfying-requirements (requirements)
> (loop for next-deal = (deal)
> until (satisfies-requirements next-deal requirements)
> finally return (copy-deal)))
>
> I welcome your ideas. Thanks.
>
> Jim Bushnell
Jim Bushnell <···········@comcast.net> wrote in message
····························@bin2.nnrp.aus1.giganews.com...
> I am an avid contract bridge player and Common Lisp programmer. In some
> bridge programming I am doing, I use the following kind of code in cases
> where I may have to generate thousands of deals before finding one which
> satisfies requirements, so I am concerned about efficiency and avoiding
> consing. I am interested in comments and/or suggestions about my use of
the
> global I call *deal* in this and related situations.
I personally don't see any reason in the code below to have any global
variable. If the rest of your code wants it, I would have callers of
find-deal-satisfying-requirements bind it themselves.
>
> (defconstant *deal* (make-array '(4 4) :initial-element 0)
> "a 4x4 array representing a bridge deal, where each element
> is a 13-bit integer")
You are modifying something you have declared as a constant above. You
meant defparameter I think.
>
> (defun deal ()
> (make-new-deal-destructively-modifying-*deal*)
> *deal*)
>
> (defun copy-deal (&optional (deal *deal*))
> (let ((new-deal (make-array '(4 4))))
> (dotimes (hand 4)
> (dotimes (suit 4)
> (setf (aref new-deal hand suit) (aref deal hand suit))))
> new-deal))
>
> (defun find-deal-satisfying-requirements (requirements)
> (loop for next-deal = (deal)
> until (satisfies-requirements next-deal requirements)
> finally return (copy-deal)))
>
How about:
(defun find-deal-satisfying-requirements (requirements)
(loop for next-deal = (make-new-deal-that-destructively-modifies-nothing)
until (satisfies-requirements next-deal requirements)
finally (return next-deal)))
--
Coby Beck
(remove #\Space "coby 101 @ bigpond . com")
From: Drew McDermott
Subject: Re: Is this an appropriate use of globals?
Date:
Message-ID: <3CE3F56F.8020309@yale.edu>
Coby Beck wrote:
> Jim Bushnell <···········@comcast.net> wrote in message
> ····························@bin2.nnrp.aus1.giganews.com...
>
>>I am an avid contract bridge player and Common Lisp programmer. In some
>>bridge programming I am doing, I use the following kind of code in cases
>>where I may have to generate thousands of deals before finding one which
>>satisfies requirements, so I am concerned about efficiency and avoiding
>>consing. I am interested in comments and/or suggestions about my use of
>> the global I call *deal* in this and related situations.
>
>
> I personally don't see any reason in the code below to have any global
> variable. If the rest of your code wants it, I would have callers of
> find-deal-satisfying-requirements bind it themselves.
I agree with Mr. Beck's comments. Not taking any position on whether
destructive modification saves anything non-neglible here, you could
just as well write
(defun find-deal-satisfying-requirements (requirements)
(let ((deal (make-new-deal)))
(loop for next-deal =
(make-new-deal-destructively-modifying deal)
until (satisfies-requirements next-deal requirements)
finally (return next-deal))))
In general, special variables, especially global variables, are a bad
idea, and I almost always find myself having to get rid of them some
time after I design them in. The problem is that the use of a special
variable assumes that just one sort of program is going to be looking at
it, and sooner or later you find yourself wanting two. For instance,
you design an exensible parser, and create a special variable
*syntax-table* to record its syntax. It is inevitable (trust me) that
you are eventually going to want to parse two languages simultaneously,
and you are going to have to do away with the global variable and
replace it with an argument passed down to the subroutines of the parser.
-- Drew McDermott
On Thu, 16 May 2002 14:07:43 -0400, Drew McDermott <··············@yale.edu>
wrote:
>
>In general, special variables, especially global variables, are a bad
>idea, and I almost always find myself having to get rid of them some
>time after I design them in. The problem is that the use of a special
>variable assumes that just one sort of program is going to be looking at
>it, and sooner or later you find yourself wanting two. For instance,
>you design an exensible parser, and create a special variable
>*syntax-table* to record its syntax. It is inevitable (trust me) that
>you are eventually going to want to parse two languages simultaneously,
>and you are going to have to do away with the global variable and
>replace it with an argument passed down to the subroutines of the parser.
It seems that you're ignoring the "special" in "special variables" i.e.,
special binding. What's wrong with:
(defvar *syntax-table* nil)
(defun parse-language (stream language)
(let ((*syntax-table* (syntax-table language)))
(parse stream)))
Tim
From: Drew McDermott
Subject: Re: Is this an appropriate use of globals?
Date:
Message-ID: <3CE585F4.1010102@yale.edu>
Tim Moore wrote:
> On Thu, 16 May 2002 14:07:43 -0400, Drew McDermott <··············@yale.edu>
> wrote:
>
>
>>In general, special variables, especially global variables, are a bad
>>idea, and I almost always find myself having to get rid of them some
>>time after I design them in. The problem is that the use of a special
>>variable assumes that just one sort of program is going to be looking at
>>it, and sooner or later you find yourself wanting two. For instance,
>>you design an exensible parser, and create a special variable
>>*syntax-table* to record its syntax. It is inevitable (trust me) that
>>you are eventually going to want to parse two languages simultaneously,
>>and you are going to have to do away with the global variable and
>>replace it with an argument passed down to the subroutines of the parser.
>
>
> It seems that you're ignoring the "special" in "special variables" i.e.,
> special binding. What's wrong with:
>
> (defvar *syntax-table* nil)
>
> (defun parse-language (stream language)
> (let ((*syntax-table* (syntax-table language)))
> (parse stream)))
>
> Tim
>
How about the following (nonhypothetical) case: You design a type system
suitable for underpinning both a typed version of Lisp (implemented
with macros) and a typed KR system with Lisp syntax. Now you want to
write a typed KR system using your typed Lisp. Are you *sure* you can
keep the scopes of the specials straight? Even if you're Kent P., and
can understand the difference between all three environments in
existence during compilation, isn't there some chance a macro in your
typed Lisp will be expanded when *type-bindings* is bound to the value
for the KR system?
In any case, why work so hard to use specials? It's almost always
pretty easy to get rid of them. I have seen many an AI program that
binds a global variable called, say, *knowledge-base*, with the
intention that some axiom set is going to be loaded in. Then they have
files that look like
(define-piece-of-knowledge "roses are red")
(define-piece-of-knowledge "violets are vile")
...
In this primitive, exploratory phase, this is okay, but very quickly
they find it a nuisance to have to restart Lisp when they want to
refresh the KB. At that point, if they find themselves defining a
function (purge-KB) that erases everything so they can load it in again,
they are making a mistake. The correct tack is to define an abstraction
called 'knowledge-base', and introduce a form
(define-knowledge-base test-1
(define-piece-of-knowledge "roses are red")
(define-piece-of-knowledge "violets are vile")
...)
Now they need just one global variable, *knowledge-base-table*, which
implements a global namespace for names such as 'test-1'. In all other
contexts, they pass the current KB as an argument to whatever function
wants to do something with a KB.
From then on, they can have as many KBs in existence simultaneously as
they want (and, as I said before, they will inevitably want more than
one eventually). To reload one, they just re-evaluate the
'define-knowledge-base' form, and it knows how to purge test-1 and
rebuild it.
This solution works fine until you realize that you want names to have
Internet-wide scope, when you have to switch to using URIs, but that's a
different story ....
-- Drew McDermott