From: Jonathan Gardner
Subject: do-random-weighted macro
Date: 
Message-ID: <c7f6bf08-fe3a-4cda-aff1-e2c10b583ebd@l42g2000hsc.googlegroups.com>
I am in the process of learning lisp. Please correct me if I am doing
something wrong.

It all started with a function to calculate how many hits a side gets
in the Attack! combat system. Anyway, I was beating the heck out of
this problem, trying to find something elegant. I was just trying to
learn lisp and see how simple I could make this problem.

I ended up writing a function that, given an indefinite number of
pairs, where the first item in the pair is the weight, and the second
item is a function representing the action, select one of the items
randomly and do it.

Since this function required me to write a lot of lambda expressions,
I thought maybe there was a better way to do it. So I started to learn
how to do macros.

I ended up with the following set of two macros that do basically the
same thing as the function I described above. These behave roughly
like cond.

(defmacro do-nth-weighted (n items)
  (if items
    (let ((var (gensym)) (next (gensym)))
      `(let ((,var ,n) (,next ,(caar items)))
        (if (< ,var ,next) ,(cons 'progn (cdar items))
          (do-nth-weighted (- ,var ,next) ,(cdr items)))))
    nil))

This one will do the following:

(dotimes (n 6) (print (do-nth-weighted n ((1 :a) (2 :b) (2 :c)
(1 :d)))))
:a
:b
:b
:c
:c
:d

This is pretty useless on its own. However, it is extremely useful
when you want to randomize the n:

(defmacro do-random-weighted (&rest args)
  (let ((newargs (map 'list
                      (lambda (a)
                        (cons (eval (car a)) (cdr a)))
                      args)))
    (let ((total (apply #'+ (map 'list #'car newargs))))
    `(do-nth-weighted (random ,total) ,newargs))))

This works as follows:

(do-random-weighted (1 nil) (1 :s) (1 :a) (1 :t) (2 :p))
=> :p shows up 2/6 of the time, nil, :s, :a, and :t 1/6 each.

And this is how I used it:

(defun roll-hits (soldiers tanks artillery planes)
  (let ((hits 0))
    (dotimes (n (+ soldiers (* 2 tanks) artillery planes) hits)
      (do-random-weighted (1 (print "Rolled a miss"))

                          (1 (print "Rolled soldier")
                             (when (plusp soldiers)
                               (print "   ...hit")
                               (incf hits)
                               (decf soldiers)))

                          (1 (print "Rolled tank")
                             (when (plusp tanks)
                               (print "   ...hit")
                               (incf hits)
                               (decf tanks)))

                          (1 (print "Rolled artillery")
                             (when (plusp artillery)
                               (print "   ...hit")
                               (incf hits)
                               (decf artillery)))

                          (2 (print "Rolled plane")
                             (when (plusp planes)
                               (print "   ...hit")
                               (incf hits)
                               (decf planes)))))))

(roll-hits 1 1 1 1)
(roll-hits 2 0 2 0)

Comments? Suggestions?

Is there a way to get rid of do-nth-weighted as it is only really
useful for do-random-weighted?

Note that I couldn't see a way around eval. I need to evaluate the
weights exactly once each, and I need their sum to figure out what to
put as the parameter to random. I also need their values to pass on to
do-nth-weighted. Is this a legitimate use of eval or do I need to do
something else?

From: danb
Subject: Re: do-random-weighted macro
Date: 
Message-ID: <55ab5ea5-967d-44b9-94f6-67560f710130@e10g2000prf.googlegroups.com>
> I am in the process of learning lisp.

Welcome to c.l.l.

> It all started with a function to calculate how many hits
> a side gets in the Attack! combat system.

So presumably a player rolls the dice, and something happens
depending on the value.

> I was just trying to learn lisp and see how simple
> I could make this problem.

Good idea.

> I ended up writing a function that, given an indefinite number of
> pairs, where the first item in the pair is the weight, and the second
> item is a function representing the action, select one of the items
> randomly and do it.

> (do-random-weighted (1 nil) (1 :s) (1 :a) (1 :t) (2 :p))
> => :p shows up 2/6 of the time, nil, :s, :a, and :t 1/6 each.

Separate the search from the randomization:

(defun weighted-nth (n-trigr pairs)
  (let ((n-accum 0))
    (dolist (pair pairs)
      (if (>= n-accum n-trigr)
	  (return-from weighted-nth (cdr pair))
	  (incf n-accum (car pair))))))

(defmacro do-randoms ((n max num-vals) &body body)
  (let ((_ (gensym)))
  `(dotimes (,_ ,num-vals)
    (let ((,n (random ,max))) ,@body))))

> Since this function required me to write a lot of
> lambda expressions, I thought maybe there was a better
> way to do it. So I started to learn how to do macros.

> (1 (print "Rolled soldier")
>    (when (plusp soldiers)
>       (print "   ...hit")
>        (incf hits)
>        (decf soldiers)))
> ...

There's a lot of duplication here.  You might as well
wrap the code in a function and the data  in some
variables:

(defun process-roll (symbol string countable)
  (cond ((plusp (symbol-value symbol))
	 (format t "You hit ~:[a~;~] ~A!~%" countable string)
	 (incf *hits*)
	 (decf (symbol-value symbol)))
	(t
	 (if countable
	     (format t "Sorry, there are no ~As left.~%" string)
	     (format t "Sorry, there's no ~A left.~%" string)))))

(defvar *hits*)
(setf *hits* 0)
(defvar *weighted-forces*)
(setf  *weighted-forces*
       '((1 soldiers  "soldier"    t )
	 (1 tanks     "tank"       t )
	 (1 artillery "artillery" nil)
	 (2 planes    "plane"      t )))

> (roll-hits 1 1 1 1)
> (roll-hits 2 0 2 0)

Now you can have the program generate random numbers
for you, and feed them to the function that uses them:

(defun init-forces ()
  (setf *hits* 0)
  (set 'soldiers 10)
  (set 'tanks     1)
  (set 'artillery 2)
  (set 'planes    1))

(defun total-weight (pairs) (reduce #'+ pairs :key #'car))

(defun multiroll (num-rolls pairs)
  (do-randoms (n (total-weight pairs) num-rolls)
    (let ((val (weighted-nth n pairs)))
      (if val
	  (process-roll (car val) (cadr val) (caddr val))
	  (write-line "Sorry, no dice.")))))

And test the code:

(defun mr ()
  (init-forces)
  (multiroll 20 *weighted-forces*))

CL-USER> (mr)
You hit a tank!
You hit a plane!
You hit artillery!
Sorry, there are no tanks left.
You hit artillery!
Sorry, there's no artillery left.
Sorry, there are no tanks left.
Sorry, there's no artillery left.
Sorry, there are no planes left.
Sorry, there are no planes left.
Sorry, there are no planes left.
Sorry, there are no planes left.
Sorry, there are no planes left.
You hit a soldier!
You hit a soldier!
Sorry, there are no planes left.
Sorry, there are no tanks left.
You hit a soldier!
You hit a soldier!
Sorry, there are no tanks left.

> Comments? Suggestions?

Don't use macros to operate on data.  If the code you want
to call varies enough, and isn't selected until runtime, use
lambdas and funcall to call the right function.  Macros are used
to hide code patterns that you would otherwise have to type
or paste repeatedly into your source file(s).

> Note that I couldn't see a way around eval.
> Is this a legitimate use of eval or do I need to do
> something else?

Something else.  EVAL is almost never necessary.

--Dan

------------------------------------------------
Dan Bensen
http://www.prairienet.org/~dsb/
From: ···········@yahoo.com
Subject: Re: do-random-weighted macro
Date: 
Message-ID: <d760d3b2-c364-425f-8332-81ae841a447c@8g2000hse.googlegroups.com>
Well, in the process of typing this,
Dan posted a more complete answer, but I'll add my two cents.


Your code works, so I wouldn't go so far as to call it wrong,
but it is a rather heavyweight solution.  I don't see the need
to pass nearly so much code to a random number generator.

(defun nth-unit-index (n units)
  (loop
     for i from 0
     for u in units
     summing u into count
     when (< n count) return i))

(defun random-unit-index (units)
  (nth-index (random (reduce #'+ units)) units))

The above code does the same weighted random number generation.
And (target-index) as defined below gives an index with the same
distribution as your call in (defun roll-hits ...).

(defun target-index ()
  (random-unit-index '(1 1 1 1 2)))

It seems that planes are easier to hit than tanks or artillery...

If you don't plan on using (do-weighted-random ...) in
any other code, you can just get rid off all three
functions above in favor of:

(defun target-index ()
  (let ((n (random 6)))
    (if (= n 5) 4 n)))


Then the action taken (print what got hit, or missed) can be
put into a small function to avoid the unsightly code duplication.
I was leaving that as an exercise, but looks like you already
got a more complete solution.

Good luck.
From: Jonathan Gardner
Subject: Re: do-random-weighted macro
Date: 
Message-ID: <f97a5e5f-dd02-4bb8-80d6-d1100d914fac@i12g2000prf.googlegroups.com>
On Mar 28, 2:02 am, ···········@yahoo.com wrote:
> Your code works, so I wouldn't go so far as to call it wrong,
> but it is a rather heavyweight solution.  I don't see the need
> to pass nearly so much code to a random number generator.
>

Compared to your answers, yes, I see how heavyweight it was.

> (defun nth-unit-index (n units)
>   (loop
>      for i from 0
>      for u in units
>      summing u into count
>      when (< n count) return i))
>
> (defun random-unit-index (units)
>   (nth-index (random (reduce #'+ units)) units))
>
> (defun target-index ()
>   (random-unit-index '(1 1 1 1 2)))
>

This is beautiful. Although I don't like mucking with indexes, this is
very beautiful.

> It seems that planes are easier to hit than tanks or artillery...
>

Really, it is that planes score hits easier than tanks or artillery.
Somehow I left the impression that I was trying to kill the units
rather than count how many kills the units scored.

>
> (defun target-index ()
>   (let ((n (random 6)))
>     (if (= n 5) 4 n)))
>

This is a beautiful hack for this particular problem.
From: Jonathan Gardner
Subject: Re: do-random-weighted macro
Date: 
Message-ID: <b87dcd05-1e46-46c0-b97c-f6a673fe9eb0@h11g2000prf.googlegroups.com>
On Mar 28, 1:30 am, danb <·········@gmail.com> wrote:
>
> (defun weighted-nth (n-trigr pairs)
>   (let ((n-accum 0))
>     (dolist (pair pairs)
>       (if (>= n-accum n-trigr)
>           (return-from weighted-nth (cdr pair))
>           (incf n-accum (car pair))))))
>

I'm curious--why do you have an accumulator when you can decrement n-
trigr?
Isn't the n-trigr that is bound in weighted-nth separate from the one
that is
passed in? Or am I confusing Python with Lisp?

Also, I think it doesn't quite behave the way you expect. It seems to
be
returning the next pair after the one that should be returned.

 (defun weighted-nth (n-trigr pairs)
   (dolist (pair pairs)
     (if (< n-trigr (car pair))
         (return-from weighted-nth (cdr pair))
         (decf n-trigr (car pair)))))

> (defmacro do-randoms ((n max num-vals) &body body)
>   (let ((_ (gensym)))
>   `(dotimes (,_ ,num-vals)
>     (let ((,n (random ,max))) ,@body))))

From reading cltl, &body seems to be the same as &rest. What is the
real
difference, or is it just for lisp editors?

What does ,@body do? Where can I look up the meaning of all the funny
symbols?
This is a major hangup for me in Lisp.

Let me see if I understand the params.
 - 'n' is a variable name that will be set to whatever was rolled.
 - 'max' is the maximum roll. 'num-vals' is the number of times you
want to
   roll.
 - 'body' is what you want to do for each roll.

It seems like this is a macro that will roll n, from 0 to max - 1, num-
vals
times and run body.

> (defun process-roll (symbol string countable)
>   (cond ((plusp (symbol-value symbol))
>          (format t "You hit ~:[a~;~] ~A!~%" countable string)
>          (incf *hits*)
>          (decf (symbol-value symbol)))
>         (t
>          (if countable
>              (format t "Sorry, there are no ~As left.~%" string)
>              (format t "Sorry, there's no ~A left.~%" string)))))
>

This is neat. I didn't know about symbol-value. Very useful.

This won't handle a complete miss, however.

Of course, I would probably replace all of the format bits with a
callback or
something else. Down the road, I'll need it to do different things
when it
gets a hit or a miss.

> (defvar *hits*)
> (setf *hits* 0)
> (defvar *weighted-forces*)
> (setf  *weighted-forces*
>        '((1 soldiers  "soldier"    t )
>          (1 tanks     "tank"       t )
>          (1 artillery "artillery" nil)
>          (2 planes    "plane"      t )))
>

You've got five faces of the die, but you are missing one--a complete
miss.

The idea of using a variable name in a quoted list, so that when it is
"activated" it will pull in the lexically scope variable of that
name... that
is interesting.

> (defun init-forces ()
>   (setf *hits* 0)
>   (set 'soldiers 10)
>   (set 'tanks     1)
>   (set 'artillery 2)
>   (set 'planes    1))
>

I assume you are doing globals here for testing and experimentation,
right?
After all, I would prefer to have these variable passed around between
functions in the end product.

> (defun total-weight (pairs) (reduce #'+ pairs :key #'car))

I like :key a lot. I forgot that reduce had it.

> (defun multiroll (num-rolls pairs)
>   (do-randoms (n (total-weight pairs) num-rolls)
>     (let ((val (weighted-nth n pairs)))
>       (if val
>           (process-roll (car val) (cadr val) (caddr val))
>           (write-line "Sorry, no dice.")))))
>

Question about the process-roll line. Correct me if I go wrong:

- val is the cdr of the item in *weighted-forces*: '(soldiers
  "soldier" t)
- (car val) is the symbol: 'soldiers
- (cadr val) is the string: "soldiers"
- (caddr val) is the countable: t

When process-roll runs, it will execute (symbol-value symbol), where
symbol is
'soldiers. This is a global variable.

Down the road, if I do something like this:

(let ((soldiers 5) (artillery 2) (tanks 3)...)
  ...
  (process-roll (car val) (cadr val) (caddr val))
  ...

Then we have a problem. process-roll won't know about the lexically
bound
soldiers, tanks, artillery, etc...

So don't I have to define the method process-roll inside of the let so
it will
bind properly?

>
> Don't use macros to operate on data.  If the code you want
> to call varies enough, and isn't selected until runtime, use
> lambdas and funcall to call the right function.  Macros are used
> to hide code patterns that you would otherwise have to type
> or paste repeatedly into your source file(s).
>

This is very good advice. I like the data-driven approach you have.
I'm upset
with myself that I didn't try that first. (I guess I am not used to
doing
things like locals()[varname] in Python, whereas in lisp, symbol-value
does it
much easier and correctly.)

Maybe down the road when rolling dice will produce very different
behavior,
then do-random-weighted may be useful.
From: danb
Subject: Re: do-random-weighted macro
Date: 
Message-ID: <a82f18c4-b0c5-4219-af07-b814d63f7b15@2g2000hsn.googlegroups.com>
> On Mar 28, 1:30 am, danb <·········@gmail.com> wrote:
> > (defun weighted-nth (n-trigr pairs)
> >   (let ((n-accum 0))
> >     ...

On Mar 28, 1:27 pm, Jonathan Gardner <········@jonathangardner.net>
wrote:
> why do you have an accumulator when you can decrement n-trigr?

No good reason.

> Isn't the n-trigr that is bound in weighted-nth separate from
> the one that is passed in? Or am I confusing Python with Lisp?

The binding is separate, so you can setf it to another value.
But compound objects are passed by reference, so you should copy
them first if you want to change their internal structure without
affecting the original.

> Also, I think it doesn't quite behave the way you expect.
> It seems to be returning the next pair after the one
> that should be returned.

Maybe so.

> From reading cltl, &body seems to be the same as &rest.
> What is the real difference, or is it just for lisp editors?

Exactly, it's identical except for indentation.
Also, the Hyperspec is more authoritative now:
http://www.lisp.org/HyperSpec/FrontMatter/index.html

> Where can I look up the meaning of all the funny symbols?

Syntax is covered here:
http://www.lisp.org/HyperSpec/Body/chap-2.html

> What does ,@body do?

body is expected to be a list.  ,@body splices body into
the surrounding list.  See section 2.4.6 Backquote in
the Syntax chapter.

> This is a major hangup for me in Lisp.

Yea, the reader can be tricky.  Any time you use
an identifier in your source code, the reader creates
a symbol with that print name in the current package
if there's not one there already.  That can be
annoying sometimes.  You have to use :keywords in
defpackage, otherwise lots of bad things happen.

> > (defmacro do-randoms ((n max num-vals) &body body)

> Let me see if I understand the params.
>  ...
> this is a macro that will roll n, from 0 to max - 1,
> num-vals times and run body.

Correctomundo :^)

> > (defun process-roll (symbol string countable)
> >   (cond ((plusp (symbol-value symbol))
> >           ...
> >          (decf (symbol-value symbol)))
> > ...

> This is neat. I didn't know about symbol-value. Very useful.

Some of the local gurus might be able to tell you more about
that subject.  I used symbols because you mentioned them, but
I would probably use something else otherwise, like special
variables, closed-over lexical variables, or maybe fields in
an app-specific structure.

> I would probably replace all of the format bits with
> a callback or something else.

Whatever.  Like you said in your first post, just make it
as simple as possible.

> This won't handle a complete miss, however.
> You've got five faces of the die, but you are missing one--
> a complete miss.

So fix it! :^)

> using a variable name in a quoted list, so that when it is
> "activated" it will pull in the lexically scope variable
> of that name...

No, it's really just a global data structure.  SET doesn't
set a lexical value, that's what SETQ does.  It's an unfortunate
historical naming convention.  (set symbol val) is equivalent to
(setf (symbol-value symbol) value).  The symbol just acts as a
global struct.

> I assume you are doing globals here for testing and experimentation,
> right?

Pretty much.  It depends on how the data is used by your program.

> > (defun total-weight (pairs) (reduce #'+ pairs :key #'car))

> I like :key a lot. I forgot that reduce had it.

A lot of CL functions do.  I like how the functional approach
can make the code shorter.

> >   (process-roll (car val) (cadr val) (caddr val))

> - val is the cdr of the item in *weighted-forces*:
> '(soldiers "soldier" t)
> - (car val) is the symbol: 'soldiers
> - (cadr val) is the string: "soldiers"
> - (caddr val) is the countable: t
>
> When process-roll runs, it will execute (symbol-value symbol),
> where symbol is 'soldiers. This is a global variable.

Right, except the symbol 'soldiers is really a fixed
data structure.  Don't worry about it being associated
with variables, it's still a data structure of type SYMBOL.

> Down the road, if I do something like this:
> (let ((soldiers 5) (artillery 2) (tanks 3)...)
>   ...
>   (process-roll (car val) (cadr val) (caddr val))
>   ...
> Then we have a problem.

> So don't I have to define the method process-roll
> inside of the let so it will bind properly?

No, it's okay.  Symbols retain their global value,
function, and other fields even when you create lexical
bindings with the same name. 'FOO refers to the symbol
(global data structure), and FOO refers to the current
(function or value) binding, whatever that is.

> > Don't use macros to operate on data.  ...

> I'm upset with myself that I didn't try that first.

Don't be.  Just have fun, learn the language, and do some
good programming :^)

> Maybe down the road when rolling dice will produce
> very different behavior, then do-random-weighted may be useful.

Whatever works best for your application.  Refactoring is
usually easier when distinct aspects of your program's
functionality are separate, and LAMBDA and FUNCALL provide
lots of customization at run time.

--Dan

------------------------------------------------
Dan Bensen
http://www.prairienet.org/~dsb/
From: Kaz Kylheku
Subject: Re: do-random-weighted macro
Date: 
Message-ID: <0c5f2942-c7c6-4ad4-bf6a-c8c1fdc8964b@i12g2000prf.googlegroups.com>
On Mar 27, 10:37 pm, Jonathan Gardner <········@jonathangardner.net>
wrote:
> I am in the process of learning lisp. Please correct me if I am doing
> something wrong.
>
> It all started with a function to calculate how many hits a side gets
> in the Attack! combat system. Anyway, I was beating the heck out of
> this problem, trying to find something elegant. I was just trying to
> learn lisp and see how simple I could make this problem.
>
> I ended up writing a function that, given an indefinite number of
> pairs, where the first item in the pair is the weight, and the second
> item is a function representing the action, select one of the items
> randomly and do it.

This is quite silly, because all you need is some black-box function
parametrized by a discrete probability distribution histogram, which
produces a random variable in the horizontal axis of that histogram
according to the distribution.

To switch logic on the outcome, you simply plug that discrete random
variable into a CASE form.

  (let ((w (weighted-random-generator 10 20 30)))
    (case (next-value w)
      (0 ...) ;; probability 10/60
      (1 ...) ;; probability 20/60
      (2 ...))) ;; probabiliby 30/60

How to implement the generator? Lots of ways.

One possible algorithm is this:

1. Determine the GCD (greatest common divisor) of all of the numbers.
2. Divide all numbers by the GCD to get their relative proportions
   of the histogram values into the lowest terms. For instance
   (10 20 30) ->  (1 2 3)
4. Now find the sum of these resulting numbers; in our case 6.
5. Create a vector indexed [0, SUM), and fill it with increasing
   values in the domain of the discrete variable (horizontal
   axis of probability histogram) with a multiplicity determined
   by the histogram values from. In our example:
   #(0 1 1 2 2 2)
6. Generate random numbers in the range [0, LCM), and simply
   map these through the array to the output value. E.g. if
   4 is generated, output is (aref #(0 1 1 2 2 2) 4) -> 2.

All of this can be done with a single function that returns a
generator lambda. I.e. in the pseudocode above, (NEXT-VALUE W) could
just be (FUNCALL W), and WEIGHTED-RANDOM-GENERATOR does all of the
steps from 1 to 5, where SUM and the lookup table are local variables
of this function, captured by the lambda that it returns.
From: Jonathan Gardner
Subject: Re: do-random-weighted macro
Date: 
Message-ID: <bb0d5ceb-b828-4f09-ad74-8bf89a792008@d21g2000prf.googlegroups.com>
On Mar 28, 2:42 am, Kaz Kylheku <········@gmail.com> wrote:
>   (let ((w (weighted-random-generator 10 20 30)))
>     (case (next-value w)
>       (0 ...) ;; probability 10/60
>       (1 ...) ;; probability 20/60
>       (2 ...))) ;; probabiliby 30/60
>

Now you've gone and separated the weights from the actions. I wanted
to keep them in the same data structure, like a table you'd find at
the back of a book. So either you are going to build the above code
from a data structure, or you are going to do it some different way.

> How to implement the generator? Lots of ways.

I think ···········@yahoo.com did a pretty good job of writing
something simple and elegant. I don't see the point of doing gcd when
we are simply adding and subtracting numbers.
From: Mike G.
Subject: Re: do-random-weighted macro
Date: 
Message-ID: <fb32cb27-1f3b-4f10-a562-0c876cb77a37@b1g2000hsg.googlegroups.com>
While not specifically what you want, try searching the c.l.l archives
for KOND.

It is coded like an ordinary COND block, but in addition to the
predicate needing to eval to T, randomness is introduced. I used it in
a small game where my KOND had several conditions specified. The
condition determines whether or not the computer applies a certain
strategy, but I wanted some pseudo-non-determinism to help its play.

I never added weights, since I didn't need it for my app, but it
should be easy to modify.

It was fun, at least, and might be instructive to learn from.

-M
From: Ken Tilton
Subject: Aw, Jeez. Another Nooby Infestation
Date: 
Message-ID: <47ecf568$0$25056$607ed4bc@cv.net>
We go through this every spring! Didn't this happen last year at this 
time? Can someone check the last time we had to call in the exterminators?

Back then I concluded it was overflow from RoR, but now it looks 
seasonal. All these questions about Lisp push the great threads about 
the FSF and headscarves offscroll and make it hard to find deals on PayPal.

Is it something about the springtime burst of energy that brought all 
you rugrats out? Arc? Or are you all from the Southern Hemisphere and 
looking for indoor fun?

<sigh>

kenny

ps. Before you ask, the problem is you are mutating '(a literal list). k
From: Jonathan Gardner
Subject: Re: Aw, Jeez. Another Nooby Infestation
Date: 
Message-ID: <d7d71651-bb59-4fb8-8952-d4ff59da9f11@d21g2000prf.googlegroups.com>
On Mar 28, 6:40 am, Ken Tilton <···········@optonline.net> wrote:
> We go through this every spring! Didn't this happen last year at this
> time? Can someone check the last time we had to call in the exterminators?
>
> Back then I concluded it was overflow from RoR, but now it looks
> seasonal. All these questions about Lisp push the great threads about
> the FSF and headscarves offscroll and make it hard to find deals on PayPal.
>
> Is it something about the springtime burst of energy that brought all
> you rugrats out? Arc? Or are you all from the Southern Hemisphere and
> looking for indoor fun?
>
> <sigh>
>

Actually, I believe it is because there is more time in the day to do
stuff. Living near Seattle, I actually get a lot more energy in the
spring and summer than in the winter.

>
> ps. Before you ask, the problem is you are mutating '(a literal list). k

What do you mean by that? I am genuinely interested.

As for the other answers, I am going to need a few days to understand
them. Thanks everyone as you are truly stretching my mind in ways I
haven't been able to stretch it.
From: Ken Tilton
Subject: Re: Aw, Jeez. Another Nooby Infestation
Date: 
Message-ID: <47ed176d$0$5621$607ed4bc@cv.net>
Jonathan Gardner wrote:
> On Mar 28, 6:40 am, Ken Tilton <···········@optonline.net> wrote:
> 
>>We go through this every spring! Didn't this happen last year at this
>>time? Can someone check the last time we had to call in the exterminators?
>>
>>Back then I concluded it was overflow from RoR, but now it looks
>>seasonal. All these questions about Lisp push the great threads about
>>the FSF and headscarves offscroll and make it hard to find deals on PayPal.
>>
>>Is it something about the springtime burst of energy that brought all
>>you rugrats out? Arc? Or are you all from the Southern Hemisphere and
>>looking for indoor fun?
>>
>><sigh>
>>
> 
> Actually, I believe it is because there is more time in the day to do
> stuff. Living near Seattle, I actually get a lot more energy in the
> spring and summer than in the winter.

So the days are longer in Seattle now? Here in NJ we seem stuck at 24 
hours. :) Yeah, the energy thig is big. I definitely am a SAD sufferer, 
and even with the huge full spectrum lamp over my desk the short days 
bring me down.

I have been congratulating myself on my incredible will power for 
resuming work on the algebra software, then I noticed it was just that 
the days were getting longer. :)

> 
> 
>>ps. Before you ask, the problem is you are mutating '(a literal list). k
> 
> 
> What do you mean by that? I am genuinely interested.

Lisp tutorials understandably use things like '(dog cat house barn) in 
their examples, but then noobs think that is how to make any list. Well, 
not if you plan on destructively modifying it. That would be like 
destructively modifying a C string literal -- the Lisp compiler is free 
to assume no one will touch a quoted list compiled into your code.

Use copy-list or copy-tree on any thing you want to beat on yet type in 
conveniently with quote notation.

kt

-- 
http://smuglispweeny.blogspot.com/
http://www.theoryyalgebra.com/

"In the morning, hear the Way;
  in the evening, die content!"
                     -- Confucius
From: Jonathan Gardner
Subject: Re: Aw, Jeez. Another Nooby Infestation
Date: 
Message-ID: <1b0f72fa-b8fb-45f1-83db-c6c74f2fd4d1@s8g2000prg.googlegroups.com>
On Mar 28, 9:06 am, Ken Tilton <···········@optonline.net> wrote:
>
> Lisp tutorials understandably use things like '(dog cat house barn) in
> their examples, but then noobs think that is how to make any list. Well,
> not if you plan on destructively modifying it. That would be like
> destructively modifying a C string literal -- the Lisp compiler is free
> to assume no one will touch a quoted list compiled into your code.
>
> Use copy-list or copy-tree on any thing you want to beat on yet type in
> conveniently with quote notation.
>

(Thanks Pascal for elucidating in more detail on this.)

I assume we are talking about this snippet from the do-random-weighted
macro:

  (map 'list (lambda (a) (cons (eval (car a)) (cdr a))) args)

Was I wrong to assume that map was creating a new list? I thought I
was creating a new cons cell for every item in the list rather than
destructively modifying args.
From: Ken Tilton
Subject: Re: Aw, Jeez. Another Nooby Infestation
Date: 
Message-ID: <47ed5089$0$5628$607ed4bc@cv.net>
Jonathan Gardner wrote:
> On Mar 28, 9:06 am, Ken Tilton <···········@optonline.net> wrote:
> 
>>Lisp tutorials understandably use things like '(dog cat house barn) in
>>their examples, but then noobs think that is how to make any list. Well,
>>not if you plan on destructively modifying it. That would be like
>>destructively modifying a C string literal -- the Lisp compiler is free
>>to assume no one will touch a quoted list compiled into your code.
>>
>>Use copy-list or copy-tree on any thing you want to beat on yet type in
>>conveniently with quote notation.
>>
> 
> 
> (Thanks Pascal for elucidating in more detail on this.)
> 
> I assume we are talking about this snippet from the do-random-weighted
> macro:

No, sorry, I was just off on a nooby rant and jokingly made a 
pre-emptive strike against our FAFAQ.

kt

-- 
http://smuglispweeny.blogspot.com/
http://www.theoryyalgebra.com/

"In the morning, hear the Way;
  in the evening, die content!"
                     -- Confucius
From: Pascal J. Bourguignon
Subject: Re: Aw, Jeez. Another Nooby Infestation
Date: 
Message-ID: <7cod8yrha1.fsf@pbourguignon.anevia.com>
Jonathan Gardner <········@jonathangardner.net> writes:

> On Mar 28, 6:40�am, Ken Tilton <···········@optonline.net> wrote:
>> We go through this every spring! Didn't this happen last year at this
>> time? Can someone check the last time we had to call in the exterminators?
>>
>> Back then I concluded it was overflow from RoR, but now it looks
>> seasonal. All these questions about Lisp push the great threads about
>> the FSF and headscarves offscroll and make it hard to find deals on PayPal.
>>
>> Is it something about the springtime burst of energy that brought all
>> you rugrats out? Arc? Or are you all from the Southern Hemisphere and
>> looking for indoor fun?
>>
>> <sigh>
>>
>
> Actually, I believe it is because there is more time in the day to do
> stuff. Living near Seattle, I actually get a lot more energy in the
> spring and summer than in the winter.
>
>>
>> ps. Before you ask, the problem is you are mutating '(a literal list). k
>
> What do you mean by that? I am genuinely interested.

Lisp compilers and interpreters are allowed to consider quoted data as
read-only.  Therefore they won't make any copy of it, and if you
modify it, your call.

The implementation could store the quoted data on a read only page.  
If you try to modify it, you'd get a SIGSEGV.

Another thing is that you could get surprizing results:

(defun fruits-but (fruit)
  (let ((fruits '(apple apricot ananas banana)))
    (delete fruit fruits)))


C/USER[105]> (fruits-but 'ananas)
(APPLE APRICOT BANANA) ; nice!
C/USER[106]> (fruits-but 'apricot)
(APPLE BANANA) ; oops! I expected (APPLE ANANAS BANANA)
C/USER[107]> 

You are in effect modifying the source of the program!


C/USER[107]> (function-lambda-expression 'fruits-but)
(LAMBDA (FRUIT) (DECLARE (SYSTEM::IN-DEFUN FRUITS-BUT)) (BLOCK FRUITS-BUT (LET ((FRUITS '(APPLE BANANA))) (DELETE FRUIT FRUITS)))) ;
#(NIL NIL NIL NIL
  ((DECLARATION ALSO-USE-PACKAGES XLIB::ARRAY-REGISTER XLIB::INDENTATION XLIB::ARGLIST XLIB::CLX-VALUES OPTIMIZE DECLARATION))) ;
FRUITS-BUT
C/USER[108]> 


> As for the other answers, I am going to need a few days to understand
> them. Thanks everyone as you are truly stretching my mind in ways I
> haven't been able to stretch it.

That's nothing.  When you've adjusted from this nudge-stretching, try reading 
Structure and Interpretation of Computer Programs, a CS textbook using Scheme.
Available gratis from <http://mitpress.mit.edu/sicp/> (HTML), 
<http://www.neilvandyke.org/sicp-texi/> (texinfo) and 
<http://twb.ath.cx/~twb/canon/sicp/> (XHTML, PDF). 
Accompanying video lectures are available gratis at 
<http://swiss.csail.mit.edu/classes/6.001/abelson-sussman-lectures/>

-- 
__Pascal Bourguignon__
From: Jonathan Gardner
Subject: Re: Aw, Jeez. Another Nooby Infestation
Date: 
Message-ID: <4823088b-360b-43cc-835c-6ae423bdce44@u10g2000prn.googlegroups.com>
On Mar 28, 8:37 am, ····@informatimago.com (Pascal J. Bourguignon)
wrote:
>
> That's nothing.  When you've adjusted from this nudge-stretching, try reading
> Structure and Interpretation of Computer Programs, a CS textbook using Scheme.
> Available gratis from <http://mitpress.mit.edu/sicp/> (HTML),
> <http://www.neilvandyke.org/sicp-texi/> (texinfo) and
> <http://twb.ath.cx/~twb/canon/sicp/> (XHTML, PDF).
> Accompanying video lectures are available gratis at
> <http://swiss.csail.mit.edu/classes/6.001/abelson-sussman-lectures/>
>

I'm about halfway through the video lectures. Honestly, up until this
point, coming from my background (C, C++. perl, python), the only
thing that amazed me was showing how you can construct a cons cell
with a lambda function and no data at all. I already had mastered
closures and lexical scoping rules in the other languages (perl,
Python), so I realized that it really wasn't that surprising since you
are creating a frame that is only used by that lambda function.

The rest has been, "This is how you do what you already know how to do
in scheme." Maybe there is more in store for me...
From: Leslie P. Polzer
Subject: Re: do-random-weighted macro
Date: 
Message-ID: <10e97146-ad79-4a53-a5f2-e3d5ec614121@d62g2000hsf.googlegroups.com>
Not sure if this is what you want, but do you know this:

http://www.cl-user.net/asp/libs/alias_method

  Leslie