Saturday, June 6, 2009

Yay blogging

Ya, yay blogging.

So I'm learning lisp today. Read some Yegge, read the comments. Have a good book about macros, currently lost. Can't remember what it's called. Was self published in what was clearly a custom typography. All page numbers wrong, front cover already fell off. That said, good book. Didn't understand what the hell it was about. So, bunch of trawling later:

Number of lisps in existence:
4,908,801.
Number of lisps which clearly indicate within the first 100 pages of their site that they support Vista (actually my heart's in 7 now but I still have my home laptop on Vista):
Clojure, NewLisp.
Number of lispers who apparently care about Windows:
1 (me, and I'm rounding up).

I like the look of Clojure but I figured I'd start in pure lisp and pick Clojure up later on when I need the libs. Pshah, Java libraries. Someone make me a lisp that works on the Erlang VM and I'll be happy. Yah, I know. Virding already did LFE. Again, I'll pick it up when I start doing any serious work. I don't want to be learning Lisp in an environment that's full of gotchas, or where someone has already ironed out the wrinkles.

So. NewLisp.

Here's my first lisping, just firing in the dark into a REPL based on the vague knowledge that there should be parenthesies (if a REPL is as good as I think it is, I should come out of this with an acceptable fib function):

newLISP v.10.0.2 on Win32 IPv4, execute 'newlisp -h' for more info.
>+ 2 2
nil
> + 2 2
+ <40cb15>
2
2
> + (2 2)
+ <40cb15>
ERR: illegal parameter type : 2
> (+ 2 2)
4
;;AH HA! That's how function application works! Let's move on to an incorrectly calculated fib! And figuring out function definition.

> (define fib n 1)
nil
> (define fib(n 1))
ERR: invalid function in function define : (n 1)
;;It seems to think I've tried to pass 1 to n. I guess that's what it does with lists.
> (define (fib n)(1))
(lambda (n) (1))
;;Well, that's an acceptable definition...
> fib()
(lambda (n) (1))
ERR: invalid function : ()
;;And that's clearly not the way to call it.
> fib
(lambda (n) (1))
;;No, I wanted that evaluated. Maybe I defined it wrong.
>(fib)
ERR: invalid function : (1)
called from user defined function fib
;;Oops, did it again. That's calling 1. What if it weren't in a list?
>(define (fib n)1)
(lambda (n) 1)
>(fib)
1
;;Yippee! Well, that's not quite correct as a fibonacci function though... Let's try to move it up to recursion. Baby steps: Define the exit condition, otherwise recurse. Isn't there some special syntax about letrec or something? Let's see...
;;Firstly, how does the if work?
>((if(1)2)
ERR: invalid function in function if : (1)
;;I guess 1 and 0 aren't true and false. Wouldn't it be a full list or an empty list? Maybe? Shot in the dark:
((if('(1)2)
if <408f74>
ERR: list index out of bounds
;;Well, it made the function okay. Was there some problem with that quote thing? I thought that these two were equal: (quote(1)) and '(1). Aren't they? I wonder if I could find out:
...About five minutes trying (eq) and (equal) and (equals) before I blunder into (= 1 1). Now to try out the quote thing:
>(= '1 (quote 1))
true
;;Yay! And I haven't had to google lisp syntax yet! Now, back to the problem at hand:
>(if true 2 3)
2
;;Right. So my halt condition looks something like:
>(if (= n 1)1)
;;Which is hard to test in the REPL because I need to give n a value. How do I give n a value? I have an idea that it relates to let binding, and might look something like
>(let n 1)
ERR: invalid let parameter list in function let : n
>(let (n 1))
nil
> n
nil
;;Well, maybe that was right but it sure didn't produce the desired result.
;;I'll try a different tack.
> (define (finished n) (= n 1))
(lambda (n) (= n 1))
> (finished 2)
nil
> (finished 1)
true
;;And then we'll use finished internally in fib:
> (define (fib n) (if (finished n) n fib(n-1)))
(lambda (n)
(if (finished n)
n fib
(n-1)))
> (fib 1)
1
> (fib 7)

ERR: invalid function : (n-1)
called from user defined function fib
> (define (fib n) (if (finished n) n (fib n-1)))
(lambda (n)
(if (finished n)
n
(fib n-1)))
> (fib 7(

ERR: missing parenthesis : "...(fib 7(\n X\224\""
> (fib 7)

ERR: call stack overflow in function if : finished
called from user defined function fib
...
called from user defined function fi
> (define (fib n) (if (finished n) n (fib (- n 1)))
;;Oops!
ERR: missing parenthesis : "...ib n) (if (finished n) n (fib (- n 1)\200\232\""
;;Okay, I'm officially getting pissed off with not having my brackets visually synchronized. I'm from VI land, we don't put up with this sort of shit.
> (define (fib n) (if (finished n) n (fib (- n 1))))
(lambda (n)
(if (finished n)
n
(fib (- n 1))))
> (fib 8)
1
> (fib 1)
1
;;There we go! Might not be the soundest proof of recursion ever, but it terminated and it came out at the right end. Now let's go for a proper algorithm:
> (define (fib n) (if (finished n) n (+(fib (- n 1))(fib(- n 2))))
ERR: missing parenthesis : "...shed n) n (+(fib (- n 1))(fib(- n 2))\200\232\""
;;That's annoying.
> (define (fib n) (if (finished n) n (+(fib (- n 1))(fib(- n 2)))))
(lambda (n)
(if (finished n)
n
(+ (fib (- n 1)) (fib (- n 2)))))
;;That works for 1 and for 7 but not for two. Finished needs to be redefined:
> (define (finished n)(<> (fib 8)
21
> (fib 1)
1
> (fib 2)
1
> (fib 3)
2
> (fib 4)
3
;;Yay! Fibbonaci! I wonder how I could go about printing out a nice set of fibonacci numbers, comma separated? Firstly, how to print stuff out? Well, it can just be the return for the moment. Secondly, how to comma separate stuff? It sounds like an accumulator function to me. How would... No, wait. This is lisp. I was about to go and google, like, lisp.lang.collections;

Heh.
So... A comma separating accumulator please:
> (define (commaSeparatedCountdown functionToApply numberToCountDownFrom numberToCountDownTo) doCSCountdown () functionToApply numberToCountDownFrom numberToCountDownTo)
(lambda (functionToApply numberToCountDownFrom numberToCountDownTo) doCSCountdown
() functionToApply numberToCountDownFrom numberToCountDownTo)
;;I figure a top level function which takes the parameters and then a low level function which adds in things like the empty starting list which I don't want to specify every time.
;;I just hit a problem: I know there's such a thing as (cons something aList) but I want to cons lots of things together. Is it really as clunky as (cons something (cons somethingElse aList))? Surely there's something nicer than this? Anyway, here's the ugly (I only have to cons twice every time so I'm not too fussed. There should be an aggregation function defined somewhere I would think).
> (define (doCSCountdown acc f from to ) (cons acc (f from) cons(', (doCSCountdown acc f (- 1 from)))))
(lambda (acc f from to) (cons acc (f from) cons (', (doCSCountdown acc f (- 1 from)))))
> (doCSCountdown () '((n)n) 3 0)
ERR: invalid function : ()
called from user defined function doCSCountdown
;;Oh. Apparently () isn't the empty list. nil? Or a quoted list?
> (doCSCountdown '() '((n)n) 3 0)
ERR: list index out of bounds in function cons
called from user defined function doCSCountdown
;;Apparently not.
;;Maybe I'm going about this the wrong way. Baby steps. Let's try just mapping the various fibs into a list, then we can comma separate them later. This would mean being able to create a range (1..10) and then mapping it to our fib function. I wonder if there's already a range function?
;;Oh, incidentally: Empty lists are like this - you have to quote them.
> '()
()
> ()
ERR: invalid function : ()
;;So, back to range.
;;Oh, just worked out let too: (let (a) 1)
;;So now really back to range:
(define (range start end)
(let (acc) '())
(if (= start end)
acc
(cons start (range (+ 1 start) end))))
;;That was pretty cool. It just worked. I still don't understand why the first parameter to let has to be a separate list though. What's the deal with that? Can you assign the same value to several bindings at the same time?
;;Now, the useful thing is going to be mapping to the range. Let's take a quick sample:
...About forty minutes of banging my head against the wall later.
;;Doesn't seem like my range function is that good - it produces
(1 2 3 4 nil) instead of (1 2 3 4). And that blows everything apart. And the only way I've found to not produce the nil is not to not produce it, but to take it out in what must be the hackiest way ever:
(rest(reverse(range 1 10)))
I am not proud. Moving along...
Now it all looks something like this:
> (map fib (rest(reverse(range 1 10))))
(55 34 21 13 8 5 3 2 1 1)
;;Which, if you take out all the grossness with my range algorithm, is sort of okay... Except for it being in the wrong order :)
> (reverse(map fib (rest(reverse(range 1 10)))))
(1 1 2 3 5 8 13 21 34 55)

And that's enough for today.

Okay, one small revision: Instead of letting my code fall apart on the nil, I'll just make finished consider it as another end condition. Now it looks like this:

>(define (finished n)(or nil (< n 2)))
Which gives us:
> (map fib (range 1 10))
(1 1 2 3 5 8 13 21 34 55 nil)

Which is what I wanted all along, except for the commas. I'll do those later.

No comments:

Post a Comment