From: n++k
Subject: If C is like marching, Lisp is like dancing
Date: 
Message-ID: <8c680ad5.0405220248.7446550c@posting.google.com>
Harold Cohen on the relation of technology and art, and his creation
of AARON, his  autonomous painter/program "apprentice."

http://www.tate.org.uk/onlineevents/archive/harold_cohen/
http://www.tate.org.uk/onlineevents/archive/harold_cohen/cohen.ram

There's a mention about why he choose to use lisp as a programming
language.

From: Tayssir John Gabbour
Subject: Re: If C is like marching, Lisp is like dancing
Date: 
Message-ID: <866764be.0405220952.12bcaeae@posting.google.com>
····@free.fr (n++k) wrote in message news:<····························@posting.google.com>...
> Harold Cohen on the relation of technology and art, and his creation
> of AARON, his  autonomous painter/program "apprentice."
> 
> http://www.tate.org.uk/onlineevents/archive/harold_cohen/
> http://www.tate.org.uk/onlineevents/archive/harold_cohen/cohen.ram
> 
> There's a mention about why he choose to use lisp as a programming
> language.

For those who want to disrespect this painter's talk by reducing it to
lisp... ;) (Major themes are about expertise with tools, as well as
foolproof machines for fools in this culture. Interesting tension
between Aaron being a tool to amplify the painter, and being rather
autonomous in its own right.)


41:10 - Wanted Aaron to be a colorist, talked about technological
obstacles like programming language.

"I'd programmed in several languages over the years and settled
eventually on the C language which had become the lingua franca for
anyone programming on PCs. But now i was beginning to find that robust
and workmanlike as C was, it was too inflexible, too inexpressive to
deal with something as conceptually complex as color. I still had no
idea about how a program that can handle color should develop, just a
growing conviction that C was getting in the way."

"If programming in C is like marching, programming in lisp is like
dancing. By the time the other issue was resolved, I found I had an
infinitely preferable, infinitely more expressive language with which
to move forward."


47:10 - "What the computer did have on the other hand, and this was
greatly enhanced by the change from C to lisp, was a remarkable
ability to build and manipulate complex internal representations that
could be extended to color, something the human brain can't do."


57:00 - (Nothing to do with lisp; mentioned printer from the
"nonmainstream face of technology." Which required gaining expertise,
but found it to be the first major advance in color technology since
the Industrial Revolution; surpassed anything he got from oil paint.)


1:25:30 - A questioner pointed out that lisp was a "discontinuous"
language and asked if that imposed that discrete worldview on Aaron.
Someone relayed the question to him, and I don't think the relayer
necessarily carried the implication that lisp was more discontinuous
than other languages. (Or perhaps I simply read it into the question.)

It would be very interesting to me to understand why lisp is
particularly discontinuous. Composition of objects rather than a
stream of text?


1:34:00 - Mentioned Aaron consisted of 2.5 megs of lisp code, 60
modules.
From: Kaz Kylheku
Subject: Re: If C is like marching, Lisp is like dancing
Date: 
Message-ID: <cf333042.0405221554.43b1302b@posting.google.com>
···········@yahoo.com (Tayssir John Gabbour) wrote in message news:<····························@posting.google.com>...
> ····@free.fr (n++k) wrote in message news:<····························@posting.google.com>...
> 1:25:30 - A questioner pointed out that lisp was a "discontinuous"
> language and asked if that imposed that discrete worldview on Aaron.
> Someone relayed the question to him, and I don't think the relayer
> necessarily carried the implication that lisp was more discontinuous
> than other languages. (Or perhaps I simply read it into the question.)
> 
> It would be very interesting to me to understand why lisp is
> particularly discontinuous. Composition of objects rather than a
> stream of text?

I suspect that this questioner is an artsie-fartsie with a mild
philosphy background and perhaps a smattering of introductory level
computer science.

The ``reasoning'' probably goes something like this:

computers -> finite state machines -> discrete representations ->
languages that express discreteness -> programmers adopt discrete view
of world through use of said languages.

Yawn.

A better question might be to what extent does Aaron represents its
knowledge or state with continuous, numeric representations versus
symbolic representations: how much number crunching, computational
geometry, etc is involved. Does it ever flip back and forth between
two very different representations of the same thing? Is it a straight
pipelined process, or is there a lot of tangled feedback going on?
That type of thing.

I'd ask how macro-heavy the Lisp code is, and to what extent it uses
CLOS. Are conditions and restarts relied upon. What is the deepest
level of backquote nesting. :) :)
From: David Steuber
Subject: Re: If C is like marching, Lisp is like dancing
Date: 
Message-ID: <87r7tbg7ws.fsf@david-steuber.com>
···@ashi.footprints.net (Kaz Kylheku) writes:

> I'd ask how macro-heavy the Lisp code is, and to what extent it uses
> CLOS. Are conditions and restarts relied upon. What is the deepest
> level of backquote nesting. :) :)

Backquote nesting?  This intrigues me.  Do you have a short example
that shows the potential usefulness of nesting backquotes two or three
levels?  It's ok if it is contrived so long as it gets the point
across.  What sort of context will ,symbol atoms be evaluated in?

As I typed the last question, the answer, "in the lexical scope of the
macro expansion" came into my head.  Is that right?

-- 
I wouldn't mind the rat race so much if it wasn't for all the damn cats.
From: Coby Beck
Subject: Re: If C is like marching, Lisp is like dancing
Date: 
Message-ID: <RqVrc.5127$L.4641@news-server.bigpond.net.au>
"David Steuber" <·····@david-steuber.com> wrote in message
···················@david-steuber.com...
> Backquote nesting?  This intrigues me.  Do you have a short example
> that shows the potential usefulness of nesting backquotes two or three
> levels?  It's ok if it is contrived so long as it gets the point

Here's a short example that is not contrived but in production code:

(defmacro define-business-rule
   ((name &optional short long) &key field-type formula entities doc)
   `(progn
      ,@(mapcar #'(lambda (context)
                    `(set-formula ',(intern (string-upcase name))
                                  ',context ',formula))
                entities)
      ,@(when formula
          (mapcar #'(lambda (ent)
                      `(merge-fields
                        ,(keywordify ent)
                        ',(list (make-field-syntax
                                 `((,name ,short ,long)
                                   :type ,(keywordify field-type)
                                   :formula ,(expand-formula formula ent)
                                   :note ,doc)
                                   :derived))))
                  entities))))

It allows me to concisely specify calculated fields in a database
application.

eg:
(define-business-rule ("ReqProfitMargin" "Required Profit") :field-type
money
  :formula ($multiply TenderMarkup TenderTotalCost)
  :entities (WorkItem ProjectPart))

and get:

(progn (set-formula 'reqprofitmargin 'workitem
                    '($multiply tendermarkup tendertotalcost))
 (set-formula 'reqprofitmargin 'projectpart
              '($multiply tendermarkup tendertotalcost))
 (merge-fields :workitem
  '((("cReqProfitMargin" "Required Profit") (:money NIL NIL)
     ($multiply (:projectpart . :tendermarkup)
      ($if ($eql :projectpartcode "0.00") 0
       ($add ($add :estimatedengineeringcost ($add :operationalmargin
:corporatemargin)) :onsiteoverheadcost))))))
 (merge-fields :projectpart
  '((("cReqProfitMargin" "Required Profit") (:money NIL NIL)
     ($multiply (:project . :tendermarkup) :tendertotalcost)))))

> across.  What sort of context will ,symbol atoms be evaluated in?
>
> As I typed the last question, the answer, "in the lexical scope of the
> macro expansion" came into my head.  Is that right?

As you can see in the above it is very common to use ',something and with
symbols you are quite right that it would need to be bound in the
environment in which the macro is expanded.

-- 
Coby Beck
(remove #\Space "coby 101 @ big pond . com")
From: Wade Humeniuk
Subject: Re: If C is like marching, Lisp is like dancing
Date: 
Message-ID: <c_Vrc.12641$g71.10118@clgrps13>
David Steuber wrote:

> Backquote nesting?  This intrigues me.  Do you have a short example
> that shows the potential usefulness of nesting backquotes two or three
> levels?  It's ok if it is contrived so long as it gets the point
> across.  What sort of context will ,symbol atoms be evaluated in?

Here is an example: (see deftag)

http://www3.telus.net/public/whumeniu/primitive-html.lisp

The macro defines a html defining macro.  To see how primitive-html
looks in actual use, see:

http://www3.telus.net/public/whumeniu/html-report.lisp

This is part of an application called The Runner's Log.

http://download.com.com/3000-2136-6893549.html?tag=lst-0-1

It's a Lispworks for Windows CAPI based app.

Wade
From: Antonio Menezes Leitao
Subject: Re: If C is like marching, Lisp is like dancing
Date: 
Message-ID: <pan.2004.05.23.07.52.57.641669@evaluator.pt>
On Sat, 22 May 2004 23:05:23 -0400, David Steuber wrote:

> ···@ashi.footprints.net (Kaz Kylheku) writes:
> 
>> I'd ask how macro-heavy the Lisp code is, and to what extent it uses
>> CLOS. Are conditions and restarts relied upon. What is the deepest
>> level of backquote nesting. :) :)
> 
> Backquote nesting?  This intrigues me.  Do you have a short example
> that shows the potential usefulness of nesting backquotes two or three
> levels?  It's ok if it is contrived so long as it gets the point
> across.  What sort of context will ,symbol atoms be evaluated in?
> 

I created this beauty for the Linj language (the macro is entirely
written in Common Lisp):

(defmacro def-swing-constructor (class-superclass (&rest setters) &body body)
  (multiple-value-bind (class superclass)
      (if (consp class-superclass)
	(values-list class-superclass)
	(values class-superclass nil))
    (let ((all-slots 
            (append setters 
                   (gethash superclass *swing-constructors* (list)))))
      (setf (gethash class *swing-constructors*) all-slots)
     `(defmacro ,class (&key ,@(mapcar #'(lambda (setter)
                                           `(,setter nil 
                                                    ,(conc-symbol setter
                                                                  '-p)))
                                       all-slots))
	 `(let ((,',class (new ',',class)))
	    ,@,@(mapcar #'(lambda (setter)
			    `(when ,(conc-symbol setter '-p)
			       `((send ,',class
                                       ,',(conc-symbol 'set- setter)
                                       ,,setter))))
			all-slots)
	    ,@'(,@body)
	    (setf the-component ,',class))))))

To use this, you write something like:

(def-swing-constructor test ;;a class name
  (a                        ;;their 'initialization' args
   b)
  (foo)                     ;;further initialization
  (bar))

and it expands into:

(defmacro test (&key (a () a-p) (b () b-p))
  `(let ((test (new 'test)))
     ,@(when a-p `((send test set-a ,a)))
     ,@(when b-p `((send test set-b ,b)))
     (foo)
     (bar)
     (setf the-component test)))

The idea is that it allows you to replace the initialization of instances
that require setters with initialization using initargs.  For those
who don't know Swing (the Java library for graphical interfaces), it's
enough to say that there are so many configuration options in the
library classes that they just couldn't put then as constructor
parameters (because they don't have keyword parameters in Java). The
result is that they use setters to define those options.  Unfortunately,
this makes the construction of Swing graphical interfaces a real PITA. You
just can't write functional code because everything is done via setters. 

Using Linj, of course, you have other solutions.  The above
little macro is just an experiment I'm doing that allows me to "convert"
setters into initargs.  I'll show just the definition for the JLabel class:

(def-swing-constructor (j-label j-component)
  (text
   icon
   horizontal-alignment
   vertical-alignment
   disabled-icon
   displayed-mnemonic
   horizontal-text-position
   vertical-text-position 
   icon-text-gap
   label-for))

Note that I also had to define something similar for the j-label class
hierarchy, i.e., j-component, container and component.   For example, for
the component class, we have:

(def-swing-constructor component
  (component-orientation
   cursor
   drop-target
   locale
   location
   name
   size))

The j-label definition then composes the initargs for its class hierarchy
and expands into:

(defmacro j-label
          (&key (text () text-p) (icon () icon-p)
           (horizontal-alignment () horizontal-alignment-p)
           (vertical-alignment () vertical-alignment-p)
           (disabled-icon () disabled-icon-p)
           (displayed-mnemonic () displayed-mnemonic-p)
           (horizontal-text-position () horizontal-text-position-p)
           (vertical-text-position () vertical-text-position-p)
           (icon-text-gap () icon-text-gap-p) (label-for () label-for-p)
           (action-map () action-map-p) (alignment-x () alignment-x-p)
           (alignment-y () alignment-y-p) (autoscrolls () autoscrolls-p)
           (background () background-p) (border () border-p)
           (debug-graphics-options () debug-graphics-options-p)
           (double-buffered () double-buffered-p) (enabled () enabled-p)
           (font () font-p) (foreground () foreground-p)
           (input-verifier () input-verifier-p)
           (maximum-size () maximum-size-p) (minimum-size () minimum-size-p)
           (next-focusable-component () next-focusable-component-p)
           (opaque () opaque-p) (preferred-size () preferred-size-p)
           (request-focus-enabled () request-focus-enabled-p)
           (tool-tip-text () tool-tip-text-p) (u-i () u-i-p)
           (verify-input-when-focus-target () verify-input-when-focus-target-p)
           (visible () visible-p) (layout () layout-p) (bounds () bounds-p)
           (component-orientation () component-orientation-p)
           (cursor () cursor-p) (drop-target () drop-target-p)
           (locale () locale-p) (location () location-p) (name () name-p)
           (size () size-p))
  `(let ((j-label (new 'j-label)))
     ,@(when text-p `((send j-label set-text ,text)))
     ,@(when icon-p `((send j-label set-icon ,icon)))
     ,@(when horizontal-alignment-p
         `((send j-label set-horizontal-alignment ,horizontal-alignment)))
     ,@(when vertical-alignment-p
         `((send j-label set-vertical-alignment ,vertical-alignment)))
     ,@(when disabled-icon-p
         `((send j-label set-disabled-icon ,disabled-icon)))
     ,@(when displayed-mnemonic-p
         `((send j-label set-displayed-mnemonic ,displayed-mnemonic)))
     ,@(when horizontal-text-position-p
         `((send j-label set-horizontal-text-position
            ,horizontal-text-position)))
     ,@(when vertical-text-position-p
         `((send j-label set-vertical-text-position ,vertical-text-position)))
     ,@(when icon-text-gap-p
         `((send j-label set-icon-text-gap ,icon-text-gap)))
     ,@(when label-for-p `((send j-label set-label-for ,label-for)))
     ,@(when action-map-p `((send j-label set-action-map ,action-map)))
     ,@(when alignment-x-p `((send j-label set-alignment-x ,alignment-x)))
     ,@(when alignment-y-p `((send j-label set-alignment-y ,alignment-y)))
     ,@(when autoscrolls-p `((send j-label set-autoscrolls ,autoscrolls)))
     ,@(when background-p `((send j-label set-background ,background)))
     ,@(when border-p `((send j-label set-border ,border)))
     ,@(when debug-graphics-options-p
         `((send j-label set-debug-graphics-options ,debug-graphics-options)))
     ,@(when double-buffered-p
         `((send j-label set-double-buffered ,double-buffered)))
     ,@(when enabled-p `((send j-label set-enabled ,enabled)))
     ,@(when font-p `((send j-label set-font ,font)))
     ,@(when foreground-p `((send j-label set-foreground ,foreground)))
     ,@(when input-verifier-p
         `((send j-label set-input-verifier ,input-verifier)))
     ,@(when maximum-size-p `((send j-label set-maximum-size ,maximum-size)))
     ,@(when minimum-size-p `((send j-label set-minimum-size ,minimum-size)))
     ,@(when next-focusable-component-p
         `((send j-label set-next-focusable-component
            ,next-focusable-component)))
     ,@(when opaque-p `((send j-label set-opaque ,opaque)))
     ,@(when preferred-size-p
         `((send j-label set-preferred-size ,preferred-size)))
     ,@(when request-focus-enabled-p
         `((send j-label set-request-focus-enabled ,request-focus-enabled)))
     ,@(when tool-tip-text-p
         `((send j-label set-tool-tip-text ,tool-tip-text)))
     ,@(when u-i-p `((send j-label set-u-i ,u-i)))
     ,@(when verify-input-when-focus-target-p
         `((send j-label set-verify-input-when-focus-target
            ,verify-input-when-focus-target)))
     ,@(when visible-p `((send j-label set-visible ,visible)))
     ,@(when layout-p `((send j-label set-layout ,layout)))
     ,@(when bounds-p `((send j-label set-bounds ,bounds)))
     ,@(when component-orientation-p
         `((send j-label set-component-orientation ,component-orientation)))
     ,@(when cursor-p `((send j-label set-cursor ,cursor)))
     ,@(when drop-target-p `((send j-label set-drop-target ,drop-target)))
     ,@(when locale-p `((send j-label set-locale ,locale)))
     ,@(when location-p `((send j-label set-location ,location)))
     ,@(when name-p `((send j-label set-name ,name)))
     ,@(when size-p `((send j-label set-size ,size)))
     (setf the-component j-label)))

And now, in the appropriate context, you can write:

(label :text "Hi" :name "my-label" :border 2 :opaque nil)

and you get:

(let ((label (new 'label)))
  (send label set-text "hi")
  (send label set-border 2)
  (send label set-opaque nil)
  (send label set-name "my-label")
  (setf the-component label))

Linj then translates this into the following Java code fragment:

Label label = new Label();
label.setText("hi");
label.setBorder(2);
label.setOpaque(false);
label.setName("my-label");
theComponent = label;

Things become more interesting when you combine this with layout
constructors.  Here is one example:

(table ()
  (tr (td (j-label :title "Foo")) (td (j-text-field) :colspan 5))
  ...)


This _hugely_ simplies the construction of Swing interfaces.

Antonio Leitao.
From: Antonio Menezes Leitao
Subject: Re: If C is like marching, Lisp is like dancing
Date: 
Message-ID: <pan.2004.05.23.08.41.46.695929@evaluator.pt>
On Sun, 23 May 2004 08:52:58 +0100, Antonio Menezes Leitao wrote:

> And now, in the appropriate context, you can write:
> 
> (label :text "Hi" :name "my-label" :border 2 :opaque nil)
> 
> and you get:
> 
> (let ((label (new 'label)))
>   (send label set-text "hi")
>   (send label set-border 2)
>   (send label set-opaque nil)
>   (send label set-name "my-label")
>   (setf the-component label))
> 
> Linj then translates this into the following Java code fragment:
> 
> Label label = new Label();
> label.setText("hi");
> label.setBorder(2);
> label.setOpaque(false);
> label.setName("my-label");
> theComponent = label;

Sorry. It should be j-label and JLabel in place of label and
Label.  I must avoid changing examples while posting...

Antonio Leitao.
From: Lars Brinkhoff
Subject: Re: If C is like marching, Lisp is like dancing
Date: 
Message-ID: <857jv3ts94.fsf@junk.nocrew.org>
Antonio Menezes Leitao <··············@evaluator.pt> writes:
> 	    ,@'(,@body)

I think you could write that as

            ,@',body

-- 
Lars Brinkhoff,         Services for Unix, Linux, GCC, HTTP
Brinkhoff Consulting    http://www.brinkhoff.se/
From: Antonio Menezes Leitao
Subject: Re: If C is like marching, Lisp is like dancing
Date: 
Message-ID: <pan.2004.05.23.11.29.55.479581@evaluator.pt>
On Sun, 23 May 2004 11:19:51 +0200, Lars Brinkhoff wrote:

> Antonio Menezes Leitao <··············@evaluator.pt> writes:
>> 	    ,@'(,@body)
> 
> I think you could write that as
> 
>             ,@',body

Indeed.  I really can't remember but I guess there were other
things in that list that no longer exist in the current version of the
macro.  Probably, I forgot to simplify it.  Even more probable, I was
just afraid of breaking something: I'm not an expert in double
backquoting :-)


Thanks,

Antonio Leitao.
From: David Steuber
Subject: Re: If C is like marching, Lisp is like dancing
Date: 
Message-ID: <87vfimg9gj.fsf@david-steuber.com>
Well those are some interesting examples of nested backquoting.  I
must confess they look like they have a similar property to Perl
regular expressions in that you build them up a piece at a time (so
that you know each piece will expand the way you want it to) until you
have your final, but difficult to read (at least for the uninitiated)
result.

This afternoon I spent several hours working on a function that ended
up being eleven lines not counting the docstring I wedged in there.
It makes me wonder how productive I will be in the future as I get
more proficient with Lisp and how long the shorter macros in this
thread took to get into their current form.

I can show it to you.  I will remove the docstring for clarity (or
lack of it):

(defun pc-level-from-xp (xp &key (ecl 0) (level nil))
  (labels ((xp2l (xp lvl)
             (let ((delta (* lvl 1000))
                   (lvl-ecl (- lvl ecl)))
               (if (or (< xp delta) 
                       (when level 
                         (<= level lvl-ecl)) 
                       (> lvl-ecl 19))
                   (values lvl-ecl (- delta xp) xp)
                   (xp2l (- xp delta) (incf lvl))))))
    (xp2l xp 1)))

This is the result of initially wanting an X => Y mapping that I
wasn't sure about the best way to calculate.  Then I added some
features and factored it back down.

During the writing of this function, I had a bug and was rather
annoyed to find out that I couldn't do (trace xp2l) inside the labels
expression.  Then it turned out that step wasn't implemented in the
lisp I was using.  So for a little while, I had a format in there to
manually trace the recursion for me.  The little pun in the let, if
that's what you would call it, is not so much a micro-optimization as
not wanting the same pattern repeated three times.  With any luck, the
compiler can do a tail optimization on xp2l.  Tail optimization isn't
really critical as this is not a function that will be a bottleneck.
But I did have tail recursiveness in mind when I wrote it.

I really hope that function has greater brevity than can be achieved
with Java.

-- 
I wouldn't mind the rat race so much if it wasn't for all the damn cats.
From: Christian Hofer
Subject: Re: If C is like marching, Lisp is like dancing
Date: 
Message-ID: <c8r4i0$i43$1@online.de>
David Steuber wrote:

> (defun pc-level-from-xp (xp &key (ecl 0) (level nil))
>   (labels ((xp2l (xp lvl)
>              (let ((delta (* lvl 1000))
>                    (lvl-ecl (- lvl ecl)))
>                (if (or (< xp delta) 
>                        (when level 
>                          (<= level lvl-ecl)) 
>                        (> lvl-ecl 19))
>                    (values lvl-ecl (- delta xp) xp)
>                    (xp2l (- xp delta) (incf lvl))))))
>     (xp2l xp 1)))

My personal opinion (but be warned: I am a Lisp newbie myself!):
1. The recursive call should probably be: (xp2l (- xp delta) (1+ lvl))
You do not want / need to change the value of lvl.
2. Instead of "when" I would write "and" (or maybe "if"): As Peter 
Norvig plausibly says in PAIP, p. 53: "When the main purpose is to 
return a value rather than take action, cond and if (with explicit nil 
in the else case) are preferred over when and unless, which implicitly 
return nil in the else case."
3. I would simply write a do-loop, looping over delta and lvl-minus-ecl 
(you don't seem to need lvl within your loop at all)

Chris
From: Christian Hofer
Subject: Re: If C is like marching, Lisp is like dancing
Date: 
Message-ID: <c8r5fv$j0r$1@online.de>
Christian Hofer wrote:
> 3. I would simply write a do-loop, looping over delta and lvl-minus-ecl 
> (you don't seem to need lvl within your loop at all)

Looking more precisely, this does not look like a loop at all: Maybe it 
would be clearer to explicitly formulate the three calculations you do 
(for the three conditions you test), and then check, which of those 
applies... But maybe I am missing s.th.

Chris
From: Christian Hofer
Subject: Re: If C is like marching, Lisp is like dancing
Date: 
Message-ID: <c8r78i$kn3$1@online.de>
Christian Hofer wrote:

> Looking more precisely, this does not look like a loop at all: Maybe it 
> would be clearer to explicitly formulate the three calculations you do 
> (for the three conditions you test), and then check, which of those 
> applies... But maybe I am missing s.th.

Sorry for replying in small steps!
I would imagine s.th. like:
(let ((lvl-minus-ecl (min (- (ceiling (/ xp 1000)) ecl)
                           (if level
                               (min level 20)
                               20))))
    (values lvl-minus-ecl (- xp (* 1000 (+ ecl lvl-minus-ecl)))))

Chris
From: ·······@gmx.de
Subject: cancel <············@online.de>
Date: 
Message-ID: <c8r8dg$luq$1@online.de>
This message was cancelled from within Mozilla.
From: Christian Hofer
Subject: Re: If C is like marching, Lisp is like dancing
Date: 
Message-ID: <c8rcg7$qoo$1@online.de>
Christian Hofer wrote:
> Looking more precisely, this does not look like a loop at all: Maybe it 
> would be clearer to explicitly formulate the three calculations you do 
> (for the three conditions you test), and then check, which of those 
> applies... But maybe I am missing s.th.

I hope I am not giving too many wrong replies, but I could not stop me 
taking another look...

I would propose s.th. similar to this - making the calculation explicit.

(defun pc-level-from-xp (xp &key (ecl 0) (level nil))
   (let ((lvl (min (calc-lvl-from-xp xp)
                   (+ ecl (if level
                              (min level 20)
                              20)))))
      (values (- lvl ecl)
              (- (calc-xp-from-lvl (1+ lvl)) xp)
              (- xp (calc-xp-from-lvl lvl))))

(defun calc-lvl-from-xp (xp)
    "This seems to be about the formula you implicitly use"
    (ceiling (+ -0.5 (sqrt (+ 1 (* 8 (/ xp 1000)))))))

(defun calc-xp-from-lvl (lvl)
   (/ (* lvl (1+ lvl)) 2))

Chris
From: Christian Hofer
Subject: Re: If C is like marching, Lisp is like dancing
Date: 
Message-ID: <c8reta$t17$1@online.de>
Ok, I don't know why I thought I could solve this theoretically. Now I 
ran your code and mine in SBCL, and here is my (hopefully) final 
version... Admittedly, it's some lines longer than yours.

Chris

(defun pc-level-from-xp (xp &key (ecl 0) (level nil))
   "Returns the level, the distance to the next level, and the
    distance from the last level in xp"
   (let ((lvl (min (calc-lvl-from-xp xp)
                   (+ ecl (if level
                              (min level 20)
                              20)))))
      (values (- lvl ecl)
              (- (calc-xp-from-lvl (1+ lvl)) xp)
              (- xp (calc-xp-from-lvl lvl)))))

(defun calc-lvl-from-xp (xp)
   ;; This seems to the formula you implicitly use
   (1+ (floor (/ (- (sqrt (+ 1 (* 8 (/ xp 1000))))
		   1)
		2))))

(defun calc-xp-from-lvl (lvl)
   ;; Just a kind of arithmetic medium...
   (* (- lvl 1) lvl 500))
From: David Steuber
Subject: Re: If C is like marching, Lisp is like dancing
Date: 
Message-ID: <877jv2uyot.fsf@david-steuber.com>
Christian Hofer <·······@gmx.de> writes:

> Ok, I don't know why I thought I could solve this theoretically. Now I
> ran your code and mine in SBCL, and here is my (hopefully) final
> version... Admittedly, it's some lines longer than yours.

Well I must give you kudos for your math.  I couldn't figure out a
formula which is why I went with an iterative solution.  I also chose
a recursive form of iteration because that was the first way I figured
out how to do it.

The basic rule is that to reach the next level, you need your current
level times 1000 additional xp.  This I could not turn into a simple?
formula, but I could do it algorithmically.

I haven't bothered with any timings (not really an issue for this
function).  I also haven't checked if your second and third return
values match mine.  I also did not test to see if pasing in level
argumetn would be handled correctly (leveling up is a seperate step,
not automatic).  However, I did run both versions with a test loop
that I had written to verify that the results I was getting matched
the table in the book I was using as a reference:

* (defun pc-level-from-xp (xp &key (ecl 0) (level nil))
  (labels ((xp2l (xp lvl)
             (let ((delta (* lvl 1000))
                   (lvl-ecl (- lvl ecl)))
               (if (or (< xp delta) 
                       (when level 
                         (<= level lvl-ecl)) 
                       (> lvl-ecl 19))
                   (values lvl-ecl (- delta xp) xp)
                   (xp2l (- xp delta) (1+ lvl))))))
    (xp2l xp 1)))

PC-LEVEL-FROM-XP
* (loop with xp = 0
     and level = 1
     when (> xp 253000) return nil
     do
     (format t "XP ~A ECL 0: ~A ECL 1: ~A ECL 2: ~A ECL 3: ~A~%"
             xp
             (pc-level-from-xp xp)
             (pc-level-from-xp xp :ecl 1)
             (pc-level-from-xp xp :ecl 2)
             (pc-level-from-xp xp :ecl 3))
     (setf xp (incf xp (* level 1000)))
     (setf level (incf level)))
XP 0 ECL 0: 1 ECL 1: 0 ECL 2: -1 ECL 3: -2
XP 1000 ECL 0: 2 ECL 1: 1 ECL 2: 0 ECL 3: -1
XP 3000 ECL 0: 3 ECL 1: 2 ECL 2: 1 ECL 3: 0
XP 6000 ECL 0: 4 ECL 1: 3 ECL 2: 2 ECL 3: 1
XP 10000 ECL 0: 5 ECL 1: 4 ECL 2: 3 ECL 3: 2
XP 15000 ECL 0: 6 ECL 1: 5 ECL 2: 4 ECL 3: 3
XP 21000 ECL 0: 7 ECL 1: 6 ECL 2: 5 ECL 3: 4
XP 28000 ECL 0: 8 ECL 1: 7 ECL 2: 6 ECL 3: 5
XP 36000 ECL 0: 9 ECL 1: 8 ECL 2: 7 ECL 3: 6
XP 45000 ECL 0: 10 ECL 1: 9 ECL 2: 8 ECL 3: 7
XP 55000 ECL 0: 11 ECL 1: 10 ECL 2: 9 ECL 3: 8
XP 66000 ECL 0: 12 ECL 1: 11 ECL 2: 10 ECL 3: 9
XP 78000 ECL 0: 13 ECL 1: 12 ECL 2: 11 ECL 3: 10
XP 91000 ECL 0: 14 ECL 1: 13 ECL 2: 12 ECL 3: 11
XP 105000 ECL 0: 15 ECL 1: 14 ECL 2: 13 ECL 3: 12
XP 120000 ECL 0: 16 ECL 1: 15 ECL 2: 14 ECL 3: 13
XP 136000 ECL 0: 17 ECL 1: 16 ECL 2: 15 ECL 3: 14
XP 153000 ECL 0: 18 ECL 1: 17 ECL 2: 16 ECL 3: 15
XP 171000 ECL 0: 19 ECL 1: 18 ECL 2: 17 ECL 3: 16
XP 190000 ECL 0: 20 ECL 1: 19 ECL 2: 18 ECL 3: 17
XP 210000 ECL 0: 20 ECL 1: 20 ECL 2: 19 ECL 3: 18
XP 231000 ECL 0: 20 ECL 1: 20 ECL 2: 20 ECL 3: 19
XP 253000 ECL 0: 20 ECL 1: 20 ECL 2: 20 ECL 3: 20
NIL
* (defun calc-lvl-from-xp (xp)
   ;; This seems to the formula you implicitly use
   (1+ (floor (/ (- (sqrt (+ 1 (* 8 (/ xp 1000))))
                   1)
                2))))

CALC-LVL-FROM-XP
* (defun calc-xp-from-lvl (lvl)
   ;; Just a kind of arithmetic medium...
   (* (- lvl 1) lvl 500))

CALC-XP-FROM-LVL
* (defun pc-level-from-xp (xp &key (ecl 0) (level nil))
   "Returns the level, the distance to the next level, and the
    distance from the last level in xp"
   (let ((lvl (min (calc-lvl-from-xp xp)
                   (+ ecl (if level
                              (min level 20)
                              20)))))
      (values (- lvl ecl)
              (- (calc-xp-from-lvl (1+ lvl)) xp)
              (- xp (calc-xp-from-lvl lvl)))))
STYLE-WARNING: redefining PC-LEVEL-FROM-XP in DEFUN

PC-LEVEL-FROM-XP
* (loop with xp = 0
     and level = 1
     when (> xp 253000) return nil
     do
     (format t "XP ~A ECL 0: ~A ECL 1: ~A ECL 2: ~A ECL 3: ~A~%"
             xp
             (pc-level-from-xp xp)
             (pc-level-from-xp xp :ecl 1)
             (pc-level-from-xp xp :ecl 2)
             (pc-level-from-xp xp :ecl 3))
     (setf xp (incf xp (* level 1000)))
     (setf level (incf level)))
XP 0 ECL 0: 1 ECL 1: 0 ECL 2: -1 ECL 3: -2
XP 1000 ECL 0: 2 ECL 1: 1 ECL 2: 0 ECL 3: -1
XP 3000 ECL 0: 3 ECL 1: 2 ECL 2: 1 ECL 3: 0
XP 6000 ECL 0: 4 ECL 1: 3 ECL 2: 2 ECL 3: 1
XP 10000 ECL 0: 5 ECL 1: 4 ECL 2: 3 ECL 3: 2
XP 15000 ECL 0: 6 ECL 1: 5 ECL 2: 4 ECL 3: 3
XP 21000 ECL 0: 7 ECL 1: 6 ECL 2: 5 ECL 3: 4
XP 28000 ECL 0: 8 ECL 1: 7 ECL 2: 6 ECL 3: 5
XP 36000 ECL 0: 9 ECL 1: 8 ECL 2: 7 ECL 3: 6
XP 45000 ECL 0: 10 ECL 1: 9 ECL 2: 8 ECL 3: 7
XP 55000 ECL 0: 11 ECL 1: 10 ECL 2: 9 ECL 3: 8
XP 66000 ECL 0: 12 ECL 1: 11 ECL 2: 10 ECL 3: 9
XP 78000 ECL 0: 13 ECL 1: 12 ECL 2: 11 ECL 3: 10
XP 91000 ECL 0: 14 ECL 1: 13 ECL 2: 12 ECL 3: 11
XP 105000 ECL 0: 15 ECL 1: 14 ECL 2: 13 ECL 3: 12
XP 120000 ECL 0: 16 ECL 1: 15 ECL 2: 14 ECL 3: 13
XP 136000 ECL 0: 17 ECL 1: 16 ECL 2: 15 ECL 3: 14
XP 153000 ECL 0: 18 ECL 1: 17 ECL 2: 16 ECL 3: 15
XP 171000 ECL 0: 19 ECL 1: 18 ECL 2: 17 ECL 3: 16
XP 190000 ECL 0: 20 ECL 1: 19 ECL 2: 18 ECL 3: 17
XP 210000 ECL 0: 20 ECL 1: 20 ECL 2: 19 ECL 3: 18
XP 231000 ECL 0: 20 ECL 1: 20 ECL 2: 20 ECL 3: 19
XP 253000 ECL 0: 20 ECL 1: 20 ECL 2: 20 ECL 3: 20
NIL
* (quit)

I decided to go ahead and replace incf with 1+.  It doesn't really
matter, but I decided that it was stylistically better.  I prefer to
stick with (when) rather than (and) or (if).  It more explicitly
states my intention to return a boolean when level has been changed
from its default.  This is all in an (or) so the implicit nil is not
an issue.

I really want one function rather than three.  Your auxiliary calc
functions could be wrapped in a (flet) though.

For (pc-level-from-xp) clarity of purpose is more important than
efficiency.  I know that is something in the eye of the beholder (not
to make an obscure pun there).  But the iterative solution is clearer
to me.

Maybe other readers can weigh in on this point.  Changing the
implementation is not at all painful if it delivers the same exact
output for the given input.

Thanks for your critique.

-- 
I wouldn't mind the rat race so much if it wasn't for all the damn cats.
From: Christian Hofer
Subject: Re: If C is like marching, Lisp is like dancing
Date: 
Message-ID: <c8slag$jgp$1@online.de>
David Steuber wrote:

> I haven't bothered with any timings (not really an issue for this
> function).

And efficiency is not what my solution offers (it is overkill to 
precisely evaluate a square-root if you're only interested in its order 
of magnitude...)

> For (pc-level-from-xp) clarity of purpose is more important than
> efficiency.  I know that is something in the eye of the beholder (not
> to make an obscure pun there).  But the iterative solution is clearer
> to me.

But then, maybe you could iterate over
1. The difference of exp points required for the next level, and
2. The total of exp points required for the current level
and make the comparison between xp and the latter, leaving xp untouched. 
By that
- the comparison (the first of your or-conditions) will be clearer
- it is easier to understand what your second and third return values are.

Like:
(defun another (xp)
   (do* ((level 1 (1+ level))
	(delta 1000 (+ delta 1000))
	(accum delta (+ accum delta)))
        ((> accum xp) (values level
                              (- accum xp)
                              (- delta (- accum xp))))))

Just my two cents...

Chris
From: David Steuber
Subject: Re: If C is like marching, Lisp is like dancing
Date: 
Message-ID: <87vfilltv2.fsf@david-steuber.com>
Christian Hofer <·······@gmx.de> writes:

> But then, maybe you could iterate over
> 1. The difference of exp points required for the next level, and
> 2. The total of exp points required for the current level
> and make the comparison between xp and the latter, leaving xp
> untouched. By that
> - the comparison (the first of your or-conditions) will be clearer
> - it is easier to understand what your second and third return values are.
> 
> Like:
> (defun another (xp)
>    (do* ((level 1 (1+ level))
> 	(delta 1000 (+ delta 1000))
> 	(accum delta (+ accum delta)))
>         ((> accum xp) (values level
>                               (- accum xp)
>                               (- delta (- accum xp))))))

This does look nice and clean.

-- 
I wouldn't mind the rat race so much if it wasn't for all the damn cats.