From: Tamas
Subject: please help making my Lisp more idiomatic
Date: 
Message-ID: <1175973179.548409.233140@l77g2000hsb.googlegroups.com>
Hi,

This is my first day programming Lisp - I decided to use it for my new
research project (I am an economist, solving an economic model).  The
code I have written does what I want, but doesn't look very
idiomatic.  If somebody on this list has the time (and patience),
please suggest ways how I could improve it.  The goal is not
necessarily speed but that I learn to use Lisp properly.  The code
below looks ugly to me (compared to the beautiful code I have seen in
Graham's or Seibel's books).

First, weighted-draw draws an integer from a distribution with given
probability weights (normalized if necessary, I do need the
normalization, see next function):

(defun weighted-draw (probs)
  "Draws a nonnegative integer less than (length probs) using the
weights in probs.
  If they don't sum to one, they are normalized (this involves no
extra cost)."
  (let ((x (random (apply #'+ probs)))
	(n 0))
    (dolist (p probs)
      (if (< x p)
	  (return n))
      (incf n)
      (decf x p))))

In the simulation I am running, agents each have a (discrete) state
that follows a Markov chain (independent for each agent), so here the
state is a vector of integers.  I need to draw a new state, but I want
to enforce the constraint that at the end the number of agents in each
state sums to the stationary values in statcount (if the Markov chain
has stationary distribution p1,...pn, and there are N agents, this is
something like p1*N, ..., pn*N, rounded).  To achieve this, I correct
the probability weights by the ratio of how many draws I need for each
state (needed) and statcount.

(defun next-stat-state (state transprobs statcount)
  "Draws a new state with transition probabilities and statcount
occurrences of
each value.")
  (do* ((needed (copy-list statcount))
	(len (length state))
	(nextstate (make-array len :element-type 'fixnum))
	(i 0 (+ i 1)))
       ((= i len) nextstate)
    (let ((z (weighted-draw (map 'list #'(lambda (n s p) (* (/ n s)
p))
				 needed statcount
				 (nth (aref state i) transprobs)))))
      (setf (aref nextstate i) z)
      (decf (nth z needed)))))

Example with 2 states, 8 agents and symmetric transition matrix:

(next-stat-state #(0 0 1 1 0 0 1 1) '((.25 .75) (.75 .25)) '(4 4))

Thanks,

Tamas

From: Dan Bensen
Subject: Re: please help making my Lisp more idiomatic
Date: 
Message-ID: <ev94k6$49q$1@wildfire.prairienet.org>
Tamas wrote:
> code I have written does what I want, but doesn't look very
> idiomatic. 

Tamas, your code is pretty good.  As Rainer mentioned,
it would be helpful to use more descriptive names.
Don't be afraid to use long-hyphenated-identifiers.

>   (do* ((needed (copy-list statcount))
> 	(len (length state))
> 	(nextstate (make-array len :element-type 'fixnum))
> 	(i 0 (+ i 1)))
>        ((= i len) nextstate)

Rainer suggested replacing do* with let* and loop.
I would use let* and dotimes.  It's very standard.

> (map 'list #'(lambda (n s p) (* (/ n s) p))

mapcar would normally be used for map 'list, and
you don't need the #' before a lambda.

> 	(nth (aref state i) transprobs)))))
>       (setf (aref nextstate i) z)
>       (decf (nth z needed)))))

A standard alternative to your state vectors would be lists.
You would pop states off the old list and push states onto
the new one.  Just remember to reverse the new list before
returning it.

-- 
Dan
www.prairienet.org/~dsb/
From: Tamas
Subject: Re: please help making my Lisp more idiomatic
Date: 
Message-ID: <1176000985.154614.76580@o5g2000hsb.googlegroups.com>
On Apr 7, 6:10 pm, Dan Bensen <··········@cyberspace.net> wrote:
> Tamas wrote:
> > code I have written does what I want, but doesn't look very
> > idiomatic.
>
> Tamas, your code is pretty good.  As Rainer mentioned,
> it would be helpful to use more descriptive names.
> Don't be afraid to use long-hyphenated-identifiers.
>
> >   (do* ((needed (copy-list statcount))
> >    (len (length state))
> >    (nextstate (make-array len :element-type 'fixnum))
> >    (i 0 (+ i 1)))
> >        ((= i len) nextstate)
>
> Rainer suggested replacing do* with let* and loop.
> I would use let* and dotimes.  It's very standard.
>
> > (map 'list #'(lambda (n s p) (* (/ n s) p))
>
> mapcar would normally be used for map 'list, and
> you don't need the #' before a lambda.
>
> >    (nth (aref state i) transprobs)))))
> >       (setf (aref nextstate i) z)
> >       (decf (nth z needed)))))
>
> A standard alternative to your state vectors would be lists.
> You would pop states off the old list and push states onto
> the new one.  Just remember to reverse the new list before
> returning it.
>
> --
> Danwww.prairienet.org/~dsb/

Dear Rainer and Dan,

Thank you for your help.  Since I don't have a good mental model of
all the intricacies of loop at the moment, I decided to use dotimes.
The final version (for the time being) code is below.  A question: is
there a "standard" way to write the documentation string?

Tamas


(defun weighted-draw (probs)
  "Draws a nonnegative integer using the weights given.

Parameters
  probs -- the probabilities, if they don't sum to one, they will be
normalized

Value
  an integer between 0 (inclusive) and (length probs) (exclusive)"
  (let ((x (random (reduce #'+ probs)))
	(n 0))
    (dolist (p probs)
      (if (< x p)
	  (return n))
      (incf n)
      (decf x p))))

(defun next-stat-state (state transprobs statcount)
  "Draws a new state with and a given number of occurrences of each
value.

Parameters
  state -- the old state, a vector
  transprobs -- transition matrix (given as list of lists)
  statcount -- desired count of each value

Value
  a vector of (length state), contains (nth i statcount) of each i"
  (let* ((needed (copy-list statcount))
	 (nextstate (make-array (length state) :element-type 'fixnum)))
    (dotimes (i (length state))
      (let ((draw
	     (weighted-draw
	      (mapcar #'(lambda (needed-count stat-count probability)
			  (* (/ needed-count stat-count) probability))
		      needed statcount
		      (nth (aref state i) transprobs)))))
	(setf (aref nextstate i) draw)	; save new draw
	(decf (nth draw needed))))	; we need one less of these
    nextstate))
From: Ken Tilton
Subject: Re: please help making my Lisp more idiomatic
Date: 
Message-ID: <_v_Rh.569$Dh.162@newsfe12.lga>
Tamas wrote:
> On Apr 7, 6:10 pm, Dan Bensen <··········@cyberspace.net> wrote:
> 
>>Tamas wrote:
>>
>>>code I have written does what I want, but doesn't look very
>>>idiomatic.
>>
>>Tamas, your code is pretty good.  As Rainer mentioned,
>>it would be helpful to use more descriptive names.
>>Don't be afraid to use long-hyphenated-identifiers.
>>
>>
>>>  (do* ((needed (copy-list statcount))
>>>   (len (length state))
>>>   (nextstate (make-array len :element-type 'fixnum))
>>>   (i 0 (+ i 1)))
>>>       ((= i len) nextstate)
>>
>>Rainer suggested replacing do* with let* and loop.
>>I would use let* and dotimes.  It's very standard.
>>
>>
>>>(map 'list #'(lambda (n s p) (* (/ n s) p))
>>
>>mapcar would normally be used for map 'list, and
>>you don't need the #' before a lambda.
>>
>>
>>>   (nth (aref state i) transprobs)))))
>>>      (setf (aref nextstate i) z)
>>>      (decf (nth z needed)))))
>>
>>A standard alternative to your state vectors would be lists.
>>You would pop states off the old list and push states onto
>>the new one.  Just remember to reverse the new list before
>>returning it.
>>
>>--
>>Danwww.prairienet.org/~dsb/
> 
> 
> Dear Rainer and Dan,
> 
> Thank you for your help.  Since I don't have a good mental model of
> all the intricacies of loop at the moment, I decided to use dotimes.

That's fine, but loop is worth an hour of study:

>   (let ((x (random (reduce #'+ probs)))
> 	(n 0))
>     (dolist (p probs)
>       (if (< x p)

If there is no else then I use when else I use if.
When there is no else I use when.

> 	  (return n))
>       (incf n)
>       (decf x p))))

(loop for x = (random (reduce #'+ probs)) then (- x p)
       for n upfrom 0
       for p in probs
       when (< x p) return n)

At least i think that is the same.

kzo


-- 

http://www.theoryyalgebra.com/
From: Rainer Joswig
Subject: Re: please help making my Lisp more idiomatic
Date: 
Message-ID: <joswig-0B61C3.09411108042007@news-europe.giganews.com>
In article <·······················@o5g2000hsb.googlegroups.com>,
 "Tamas" <······@gmail.com> wrote:

> On Apr 7, 6:10 pm, Dan Bensen <··········@cyberspace.net> wrote:
> > Tamas wrote:
> > > code I have written does what I want, but doesn't look very
> > > idiomatic.
> >
> > Tamas, your code is pretty good.  As Rainer mentioned,
> > it would be helpful to use more descriptive names.
> > Don't be afraid to use long-hyphenated-identifiers.
> >
> > >   (do* ((needed (copy-list statcount))
> > >    (len (length state))
> > >    (nextstate (make-array len :element-type 'fixnum))
> > >    (i 0 (+ i 1)))
> > >        ((= i len) nextstate)
> >
> > Rainer suggested replacing do* with let* and loop.
> > I would use let* and dotimes.  It's very standard.
> >
> > > (map 'list #'(lambda (n s p) (* (/ n s) p))
> >
> > mapcar would normally be used for map 'list, and
> > you don't need the #' before a lambda.
> >
> > >    (nth (aref state i) transprobs)))))
> > >       (setf (aref nextstate i) z)
> > >       (decf (nth z needed)))))
> >
> > A standard alternative to your state vectors would be lists.
> > You would pop states off the old list and push states onto
> > the new one.  Just remember to reverse the new list before
> > returning it.
> >
> > --
> > Danwww.prairienet.org/~dsb/
> 
> Dear Rainer and Dan,
> 
> Thank you for your help.  Since I don't have a good mental model of
> all the intricacies of loop at the moment, I decided to use dotimes.
> The final version (for the time being) code is below.  A question: is
> there a "standard" way to write the documentation string?

No, not really. There are/were some tools that
are using special formatting in doc strings. The
syntax for markup languages has changed over
time (Scribe, TeX, Texinfo, HTML, XML, Wiki Markup, ...) that
made it a bit difficult to settle on a 'standard'.

Though it would be useful to have a 'standard' like
Java has Javadoc and its tags.

Here is an older example in Lisp: Inverse Literate Programming,
Heiko Kirschke, 1995, Hamburg, Germany.

http://lki-www.informatik.uni-hamburg.de/~kirschke/invlit.html


> 
> Tamas
> 
> 
> (defun weighted-draw (probs)
>   "Draws a nonnegative integer using the weights given.
> 
> Parameters
>   probs -- the probabilities, if they don't sum to one, they will be
> normalized
> 
> Value
>   an integer between 0 (inclusive) and (length probs) (exclusive)"
>   (let ((x (random (reduce #'+ probs)))
> 	(n 0))
>     (dolist (p probs)
>       (if (< x p)
> 	  (return n))
>       (incf n)
>       (decf x p))))
> 
> (defun next-stat-state (state transprobs statcount)
>   "Draws a new state with and a given number of occurrences of each
> value.
> 
> Parameters
>   state -- the old state, a vector
>   transprobs -- transition matrix (given as list of lists)
>   statcount -- desired count of each value
> 
> Value
>   a vector of (length state), contains (nth i statcount) of each i"
>   (let* ((needed (copy-list statcount))
> 	 (nextstate (make-array (length state) :element-type 'fixnum)))
>     (dotimes (i (length state))
>       (let ((draw
> 	     (weighted-draw
> 	      (mapcar #'(lambda (needed-count stat-count probability)
> 			  (* (/ needed-count stat-count) probability))
> 		      needed statcount
> 		      (nth (aref state i) transprobs)))))
> 	(setf (aref nextstate i) draw)	; save new draw
> 	(decf (nth draw needed))))	; we need one less of these
>     nextstate))

-- 
http://lispm.dyndns.org
From: Vassil Nikolov
Subject: Re: please help making my Lisp more idiomatic
Date: 
Message-ID: <m37ism6bof.fsf@localhost.localdomain>
On 7 Apr 2007 19:56:25 -0700, "Tamas" <······@gmail.com> said:
| ...
| A question: is
| there a "standard" way to write the documentation string?

  There is one quasi-standard prescription well worth following: make
  the first line of each doc-string a self-contained sentence that
  summarizes the description, and as a rule start that sentence with a
  verb in the imperative.

  Your examples are practically there:

| ...
| (defun weighted-draw (probs)
|   "Draws a nonnegative integer using the weights given.
| ..."

  The above calls for s/Draws/Draw/.

| ...
| (defun next-stat-state (state transprobs statcount)
|   "Draws a new state with and a given number of occurrences of each
| value.
| ..."

    "Draw a new state with a given number of occurrences of each value.
  ..."

  ---Vassil.


-- 
The truly good code is the obviously correct code.
From: thorne
Subject: Re: please help making my Lisp more idiomatic
Date: 
Message-ID: <m33b3bwokf.fsf@timbral.net>
"Tamas" <······@gmail.com> writes:

> This is my first day programming Lisp - I decided to use it for my new

You learned this much in one day?  That's it.  I give up.

-- 
þ    theron tlåx    þ
(compose-mail (concat ·······@" (rot13 "gvzoeny") ".net"))
From: James
Subject: Re: please help making my Lisp more idiomatic
Date: 
Message-ID: <1176037865.949703.161210@n59g2000hsh.googlegroups.com>
> You learned this much in one day?  That's it.  I give up.

I feel ya.
From: Rainer Joswig
Subject: Re: please help making my Lisp more idiomatic
Date: 
Message-ID: <joswig-17B5E7.22440507042007@news-europe.giganews.com>
In article <························@l77g2000hsb.googlegroups.com>,
 "Tamas" <······@gmail.com> wrote:

> Hi,
> 
> This is my first day programming Lisp - I decided to use it for my new
> research project (I am an economist, solving an economic model).  The
> code I have written does what I want, but doesn't look very
> idiomatic.  If somebody on this list has the time (and patience),
> please suggest ways how I could improve it.  The goal is not
> necessarily speed but that I learn to use Lisp properly.  The code
> below looks ugly to me (compared to the beautiful code I have seen in
> Graham's or Seibel's books).
> 
> First, weighted-draw draws an integer from a distribution with given
> probability weights (normalized if necessary, I do need the
> normalization, see next function):
> 
> (defun weighted-draw (probs)
>   "Draws a nonnegative integer less than (length probs) using the
> weights in probs.
>   If they don't sum to one, they are normalized (this involves no
> extra cost)."
>   (let ((x (random (apply #'+ probs)))
> 	(n 0))
>     (dolist (p probs)
>       (if (< x p)
> 	  (return n))
>       (incf n)
>       (decf x p))))

It would help if you document the input and output.
Sometimes it is sufficient to write prose. Sometimes
you want to check it at runtime (CHECK-TYPE, ASSERT).

Replace APPLY with REDUCE. APPLY is only working on
lists of a limited length. REDUCE works on sequences
of any size. The IF could be replaced by WHEN
(minor issue).

> 
> In the simulation I am running, agents each have a (discrete) state
> that follows a Markov chain (independent for each agent), so here the
> state is a vector of integers.  I need to draw a new state, but I want
> to enforce the constraint that at the end the number of agents in each
> state sums to the stationary values in statcount (if the Markov chain
> has stationary distribution p1,...pn, and there are N agents, this is
> something like p1*N, ..., pn*N, rounded).  To achieve this, I correct
> the probability weights by the ratio of how many draws I need for each
> state (needed) and statcount.
> 
> (defun next-stat-state (state transprobs statcount)
>   "Draws a new state with transition probabilities and statcount
> occurrences of
> each value.")

Above is an extra parenthesis.


>   (do* ((needed (copy-list statcount))
> 	(len (length state))
> 	(nextstate (make-array len :element-type 'fixnum))
> 	(i 0 (+ i 1)))
>        ((= i len) nextstate)
>     (let ((z (weighted-draw (map 'list #'(lambda (n s p) (* (/ n s)
> p))
> 				 needed statcount
> 				 (nth (aref state i) transprobs)))))
>       (setf (aref nextstate i) z)
>       (decf (nth z needed)))))

Again, document/check the input and outputs.
I would also decide on variable naming. You have
mathematical variables like i, z, n, s, p, ....
long variables like nextstate and abbreviated
variables like len. I would let the abbreviated
variables go and use longer descriptive variables
where useful. Most Lisp systems allow you to use
completion of known symbols to help write code
with descriptive symbols.

With LOOP you may write:

(defun next-stat-state (state transprobs statcount)
  "Draws a new state with transition probabilities and statcount
occurrences of
each value."
  (let* ((needed (copy-list statcount))
         (nextstate (make-array (length state) :element-type 'fixnum)))
    (loop for i below (length state)
          for z = (weighted-draw (mapcar #'(lambda (n s p) (* (/ n s) p))
                                         needed statcount (nth (aref state i) transprobs)))
          do (setf (aref nextstate i) z)
          do (decf (nth z needed)))
    nextstate))

or

(defun next-stat-state (state transprobs statcount)
  "Draws a new state with transition probabilities and statcount
occurrences of
each value."
    (loop with needed = (copy-list statcount)
          with nextstate = (make-array (length state) :element-type 'fixnum)
          for i below (length state)
          for z = (weighted-draw (mapcar #'(lambda (n s p) (* (/ n s) p))
                                         needed statcount (nth (aref state i) transprobs)))
          do (setf (aref nextstate i) z)
          do (decf (nth z needed))
          finally do (return nextstate)))


> 
> Example with 2 states, 8 agents and symmetric transition matrix:
> 
> (next-stat-state #(0 0 1 1 0 0 1 1) '((.25 .75) (.75 .25)) '(4 4))
> 
> Thanks,
> 
> Tamas

-- 
http://lispm.dyndns.org