Thanks for the reply. My apologies for so long a delay in replying. Work jumped up and slapped me real hard last week. No time for anything else. :(

On 07/15/2017 05:52 AM, Alexander Burger wrote:
Hi Jimmie,

thanks for your long feedback and sharing your experiences!

I have spent a fair amount of the last 4 days playing with PicoLisp. I
Yess. From your code I see that you master PicoLisp already quite well! I
suspect that you gave up just a little bit too early.

Thank you. Certain difficulties like parens and deeply nested internally function calls are slowly getting easier to read and recognize. I do love the brutal consistency of Lisp. Certain things are a challenge at first. But they get easier, and because of the absolute consistence they can slowly be reasoned about. I haven't necessarily given up yet. But I haven't made a decision for PicoLisp at this time. The beauty of Lisp and the PicoLisp implementation are intriguing. One challenge I have is that it feels much more primitive than what I am used to. I am used to what many and myself consider the best IDE available, Smalltalk (Pharo). It is wonderful and rich. The ability find the class or method which will do exactly what you want is amazing. I struggle with purely documentation or file based source code to find what I am looking for. It may get easier with time. But I don't know.

: (scl 12)
-> 12
: (*Scl)
Segmentation fault (core dumped)
Haha, right. Calling (*Scl) is a prefect way to segfault. Think about what it
means: It calls a *function* starting in memory at address 12!

This is what I mean about Pharo (Smalltalk). The VM/image will not let that crash the environment. I could not have told that it called a position in memory directly expecting to execute a function. I only was wanting to know the value of *Scl. A naive understanding of Lisp is that everything happens inside of parenthesis. So I naively did (*Scl) instead of simply *Scl.

I would love the PicoLisp interpreter to handle stupid programmer errors better. Now I speak as the stupid programmer, not as an interpreter implementer having to do the protection from his stupidity. :)

I am okay if it throws me immediately into that ! commandline. I haven't quite learned what that is all about either, or what I can do.

HOWEVER! The segfaults you observed in your program are NOT your fault! You
indeed found a bug in the bignum functions of PicoLisp, causing a crash upon a
certain combination of numeric arguments.

Strange that this was not detected earlier, I used a lot of test routines with
random numbers.

I cound not locate the exact location of the bug yet. It is a Heisenbug, because
it only occurs if the garbage collector runs in the right moment. I know this
because it all runs well if you call 'gc' *before* the test, e.g.

    (gc 700)
    (doit)

I will dig into it now, to find and fix it, and release a new PicoLisp version.
Thanks for finding it!

Glad I could be of help. That is one of the reasons I wrote the email and not simply disappeared in silence. I thought to myself that if I disappear in silence, that I am not being a very good user of open source software and am not contributing the least thing that I could, which is my experience.

As you spent already so much time on it, I feel that I should try it myself.

I rewrote parts of it to be more PicoLisp-like, as you violate some of the
PicoLisp programming rules and styles. I won't comment on the details, but paste
my own version below.

Thank you for this. I had hoped you would. I knew my code was naive and most likely not idiomatic. It was having learned the least possible to implement a version. I know that if I choose to use PicoLisp that there is much more to learn include coding styles and idioms.

The new version is about 150 times faster. The main reason is that you used
'nth' to index into long lists again and again. Here on my notebook it
finishes the 100 reps in one and a half minutes:

    $ time ./pil x.l +
    *Scl: 12
    createlist
    "loop1"
    87.144 sec
    loop2
    nsum: 11257.349310772499
    navg: 0.390880184402
    4.093 sec
    91.250 sec
    nsum: 11228.585386471924
    navg: 0.389881437030

    real    1m31.628s
    user    1m31.308s
    sys     0m0.400s


A successful 100 rep run should give an
nsum: 11322.307098752284    and
navg: 0.393135663151121    within acceptable floating point differences.
Note that I get slightly different results, because I use '*/' instead of '/' in
the 'average' function. I believe that this is more correct, as '*/' rounds the
result.

I worked on it for a while longer. What I find for me is that it is much harder to reason about and to write error free code for floating point numbers using the fixpoint system.

Part of the problem in your code with regards to the differences, is that your loop indexes are still naive integers and not the fixpoint representation.

example
: (inc '1 1.0)
-> 2
: (+ 1.0 1.0)
-> 2000000000000000000

These values for the calculations below should be identical. Since they are not. The calculations are seemingly being seeded wrong values.

I have yet to get the createlist function to seemingly give me the correct initial values.

(setq nl (createlist 28800))

: (format (nth nl 28800) *Scl)
-> 288000000000

Which is different from both of these and should be the first.

: (format (normalize 28800.0) *Scl)
-> "0.288000000000"
: (format (normalize 28800) *Scl)
-> "0.000288000000"

I made a small modification to your code. Added an argument to doit. And I do not call it automatically. This way I can play in the interpreter to learn.
When I run your code on my laptop I get:

: (doit 100)
*Scl: 12
createlist
"loop1"
59.758 sec
loop2
nsum: 11257.349310772499
navg: 0.390880184402
2.658 sec
62.425 sec
nsum: 11228.585386471924
navg: 0.389881437030
-> "0.389881437030"

However, the app explodes in memory use. From the former 2.5mb (or was it 3.5) of ram to 705mb of ram. So it went from one of the smaller runtimes to the largest.

Now when I attempt to make changes to get it closer to the correct values, the time to run goes up to over 30 minutes.

I don't have any code to show. I got busy at work and in my experimenting I got lost in my code trying various things. It is much harder to reason about than the normal integers and floating point of the rest of the world. It isn't fun.

I take full responsibility that my code may be faulty. Because I struggled with where I needed to change integer values 1 to a 1.0 or not depending on what was happening. It is not easy to know and is error prone. At least for me and my experience. Others may find it different.

Thanks for listening to my experiences. Hopefully you or someone can find value in them.

Once again, I wish you the best on this project. As for me, I will remain a Smalltalker (Pharo).

Jimmie


Now let me start the bug hunt.

♪♫ Alex


PS: Here is my code:

(scl 12)

(de average (L)
    (*/ (sum prog L) (length L)) )

(de normalize (N)
    (let NN (if (=0 N) 0.000123456789 N)
       (while (<= NN 0.0001)
          (setq NN (* 10 NN)) )
       (while (>= NN 1.0)
          (setq NN (*/ NN 10)) )
       NN ) )

(de createlist (Lsize)
    (make
       (let I 0
          (do Lsize
             (link (normalize (inc 'I 1.0))) ) ) ) )

(de loop1calc (I J N)
    (let V (*/ N (+ I N) (- J N) 0.1234567 `(* 1.0 1.0 1.0))
       (normalize (*/ V V V `(* 1.0 1.0)) ) ) )

(de loop2calc (I J N)
    (normalize
       (*/ N
          (+ J 1.0)
          (+ N N N)
          0.1234567
          `(* 1.0 1.0 1.0) ) ) )

(de loopN (Fun Lst Reps)
    (let I 1.0
       (do Reps
          (let J 0
             (map
                '((L)
                   (set L
                      (Fun I (inc 'J 1.0) (car L)) ) )
                Lst ) )
          (inc 'I 1.0) ) )
    (setq
       *Nsum (sum prog Lst)
       *Navg (average Lst) ) )

(de loop1 (Lst Reps)
    (loopN loop1calc Lst Reps) )

(de loop2 (Lst Reps)
    (loopN loop2calc Lst Reps) )

(de doit ()
    (prinl "*Scl: " *Scl)
    (bench
       (let Reps 100
          (prinl "createlist")
          (let L (createlist (* 60 24 5 4))
             (println "loop1")
             (bench (loop1 L Reps))
             (prinl "loop2")
             (prinl "nsum: " (format *Nsum *Scl))
             (prinl "navg: " (format *Navg *Scl))
             (bench (loop2 L Reps)) ) ) )
    (prinl "nsum: " (format *Nsum *Scl))
    (prinl "navg: " (format *Navg *Scl)) )

(gc 700)
(doit)
(bye)



--
UNSUBSCRIBE: mailto:picolisp@software-lab.de?subject=Unsubscribe

Reply via email to