I've recently started playing around with the LISP distributed along with Slackware Linux (KCL aka GCL), and have a few questions.
Most important: Where can I find an online introduction to LISP for perfectly intelligent people who have never used it before? (No `LISP for Dummies', please.) I've used C for quite a while and BASIC before that, and now I'm trying to expand my horizons. I've looked through the FAQ for pointers to resources, but all of the introductory materials listed are books, and for various reasons I can't buy a book.
Also, what relevant reasons (besides novelty) are there to learn LISP? I guess what I want to know is, what can I do in LISP that is more difficult or not possible in C? Will a program to generate more digits of Pi than any sane human would ever need be any easier in LISP than other languages? What about prime numbers? A game such as Tetris?
If anybody has any simple but nontrivial code samples they're willing to share (with an explanation of what they do and how would also be nice), I would probably also find those useful once I've figured out the basic concepts of the language (beyond figuring out things like the fact that (caar (cons (cons 'a 'b) 'c)) returns A , that is.)
Thanks in advance.
Replies by email would be appreciated but I've been on usenet too long to ask for them. >:] -- If you feel the need to email me personally, feel free to correct the obvious typo in my email address.
< If anybody has any simple but nontrivial code samples they're willing < to share (with an explanation of what they do and how would also be < nice), I would probably also find those useful once I've figured out < the basic concepts of the language (beyond figuring out things like < the fact that < (caar (cons (cons 'a 'b) 'c)) < returns < A < , that is.)
You can do, (caar (acons 'a 'b 'c)) as well. You can use first instead of car; some like this better, I'm surprised that c{ad}r haven't been declared archaic.
Are you interested in fonts? I have some code that will extract the string table of truetype fonts (like the name, copyright, etc), as well as some stuff for working with type1, and pfm files.
You like graphics? I have a terminfo decompiler/parser and some other stuff that you probably wouldn't like to re-write :) (those conditionals are _real_ nasty - and then there's the blasted xterm entry; it should not be that complicated to move a cursor to a given address - it's very sick.)
I have a silly inference program that does the rule/assertion/forward chaining, and a silly prolog thing.
Steve Gonedes <jgone...@worldnet.att.net> writes: > daveva#earthling.net (Dave Vandervies) writes:
> < If anybody has any simple but nontrivial code samples they're willing > < to share (with an explanation of what they do and how would also be > < nice), I would probably also find those useful once I've figured out > < the basic concepts of the language (beyond figuring out things like > < the fact that > < (caar (cons (cons 'a 'b) 'c)) > < returns > < A > < , that is.)
> You can do, (caar (acons 'a 'b 'c)) as well. You can use first instead > of car; some like this better, I'm surprised that c{ad}r haven't been > declared archaic.
Are you sure this is "allowed" by the ANSI standard? The third argument to ACONS should be an alist (association list).
> Are you interested in fonts? I have some code that will extract the > string table of truetype fonts (like the name, copyright, etc), as > well as some stuff for working with type1, and pfm files.
> You like graphics? I have a terminfo decompiler/parser and some other > stuff that you probably wouldn't like to re-write :) (those > conditionals are _real_ nasty - and then there's the blasted xterm > entry; it should not be that complicated to move a cursor to a given > address - it's very sick.)
> I have a silly inference program that does the rule/assertion/forward > chaining, and a silly prolog thing.
Dave Vandervies wrote: > Also, what relevant reasons (besides novelty) are there to learn LISP? > I guess what I want to know is, what can I do in LISP that is more > difficult or not possible in C? Will a program to generate more > digits of Pi than any sane human would ever need be any easier in LISP > than other languages? What about prime numbers? A game such as > Tetris?
Well, if you're interested in digits of pi and prime numbers, you might be amused by something I hacked up recently that would be much harder and less fun in (say) C. (It could be done adequately in other languages with Lisp-like semantics; someone has done it in Python[1], for instance.)
It's a little library of functions for working with infinite-precision real numbers. And I really *do* mean "infinite", not just "variable and possibly very large": a real number is represented as a function that generates approximations to the number. (More precisely, it's a function that takes a positive integer argument n and returns some number within 1/2 of n*x, however big n is. This is not necessarily the best way to do it[2].)
In a few hundred lines you can implement the usual arithmetic operations, conversion between my representation and infinite continued fractions (represented, roughly, as infinite lists, which again use the first-class-ness of functions to good effect), some interesting transcendental numbers, roots of arbitrary polynomials, and so on.
So, if I want to know what pi - root(17)*e^1/2 is, I say something like
(real:print-via-pipe (real:minus real:pie ;; yes, there is a reason for not calling this PI. (real:times (real:nth-root-of-rational 2 17) (real:e-to-the-rational 1/2))))
and it starts churning out digits, and won't stop until either I interrupt it or it runs out of memory.
The whole thing is very primitive and rather slow, and there are plenty of important things I haven't implemented yet and plenty of others that are a real mess. But that's a few evenings' work in Lisp; I couldn't have done anything like as much in a few evenings of C. (And I'm relatively more expert in C than in Lisp.)
Things about Lisp that make it much easier:
- we already have arbitrary-precision rational numbers - we have garbage collection, so it's safe to construct complicated things on the fly and use them without the need to keep a pointer around so we can free them - we have closures as first-class objects, allowing a simple representation of numbers and of "infinite lists" - it's interactive, so debugging is easier and the resulting system gets I/O and parsing for free
[1] real.py, by Jurjen Bos. Actually, I've just looked and it *doesn't* do the same as my code (though it does do some important things my code doesn't yet do).
[2] It would possibly be better to use a function that takes n and returns a rational number within 1/n of x. The main reason why I didn't do that is that I started off implementing this in Scheme, and the Scheme implementation I use doesn't have rationals.
In fact, better still would be a "mixed" representation allowing you to record simple representations of simple numbers (notably, rationals and algebraic numbers). Operations on these could then work better.
-- Gareth McCaughan Dept. of Pure Mathematics & Mathematical Statistics, gj...@dpmms.cam.ac.uk Cambridge University, England.
* Steve Gonedes <jgone...@worldnet.att.net> | You can do, (caar (acons 'a 'b 'c)) as well. You can use first instead | of car; some like this better, I'm surprised that c{ad}r haven't been | declared archaic.
because CONS is not a list-building operator, it's a tree-building operator. lists are just special cases of trees. and vice versa. :)
FIRST, SECOND,... and REST work on lists, C[AD]+R on trees.
#:Erik -- The Microsoft Dating Program -- where do you want to crash tonight?
On 29 Nov 1998 20:15:04 GMT, Steve Gonedes <jgone...@worldnet.att.net> wrote:
>daveva#earthling.net (Dave Vandervies) writes:
>< If anybody has any simple but nontrivial code samples they're willing >< to share (with an explanation of what they do and how would also be >< nice), I would probably also find those useful once I've figured out >< the basic concepts of the language (beyond figuring out things like >< the fact that >< (caar (cons (cons 'a 'b) 'c)) >< returns >< A >< , that is.)
>You can do, (caar (acons 'a 'b 'c)) as well. You can use first instead >of car; some like this better, I'm surprised that c{ad}r haven't been >declared archaic.
I would think (based on my admittedly very limited experience) that c[ad]r would stack better than first and last (which I'm assuming is the equivalent of cdr?) (though I noticed that the version I was using only lets me stack them four deep without needing to do something like (caadar (cdar foo)).) Perhaps the reason why I think this is an advantage is that I'm enough of a C programmer to consider terseness to be nearly equivalent to clarity. >:]
[''Are you interested in...'']
>What are you interested in?
At the moment I'm interested in anything that will help me learn the language, though in general I tend to lean more toward processing-based programs than I/O-based programs.
-- If you feel the need to email me personally, feel free to correct the obvious typo in my email address.
>> Also, what relevant reasons (besides novelty) are there to learn LISP? >> I guess what I want to know is, what can I do in LISP that is more >> difficult or not possible in C? Will a program to generate more >> digits of Pi than any sane human would ever need be any easier in LISP >> than other languages? What about prime numbers? A game such as >> Tetris?
>Well, if you're interested in digits of pi and prime numbers, >you might be amused by something I hacked up recently that would >be much harder and less fun in (say) C. (It could be done >adequately in other languages with Lisp-like semantics; someone >has done it in Python[1], for instance.)
This looks interesting. Would you be willing to send me a copy? (Probably the easiest way for me would be to send it to me by email <dav...@earthling.net> (either as plain text or as a MIME-encoded compressed file (.tar.gz would be best)). If you'd prefer another method please email me first to find out if I can handle it (I probably can, but I'd like to make sure).
The way I did digits of Pi in C was to store the numbers as large (currently working on 500000 digits) arrays of integers, and perform the math (using the arctangent formula to calculate Pi, addition and subtraction of these big numbers and multiplying/dividing them by 32-bit integers is all that is needed) using them as fixed-point numbers. The major disadvantage to this (at least as I see it) is that if I want more digits I have to start over at the beginning, and that I can't get a partial value before it's done the n digits (though with an evening of work I *might* be able to get it to store its progress digit-by-digit as it works it out).
[...]
>In a few hundred lines you can implement the usual arithmetic >operations, conversion between my representation and infinite >continued fractions (represented, roughly, as infinite lists, >which again use the first-class-ness of functions to good effect), >some interesting transcendental numbers, roots of arbitrary >polynomials, and so on.
That `infinite lists' looks interesting... is that a basic construct of the language, or just something that the semantics of the language makes easy?
[...]
>Things about Lisp that make it much easier:
> - we already have arbitrary-precision rational numbers > - we have garbage collection, so it's safe to construct > complicated things on the fly and use them without the > need to keep a pointer around so we can free them
In general, I like to know where my memory is going, but I can see how that would be a pain for something like this. >:]
> - we have closures as first-class objects, allowing a simple > representation of numbers and of "infinite lists"
I may sound completely clueless here, but what does ``closures'' refer to?
> - it's interactive, so debugging is easier and the resulting > system gets I/O and parsing for free
-- If you feel the need to email me personally, feel free to correct the obvious typo in my email address.
Dave Vandervies <daveva#earthling.net> wrote: >On 29 Nov 1998 20:15:04 GMT, Steve Gonedes <jgone...@worldnet.att.net> >wrote: ...
>>of car; some like this better, I'm surprised that c{ad}r haven't been >>declared archaic.
I think the standard says that it is "depreciated".
In the context of dealing with lists FIRST and REST seem much more appropriate. If you are dealing with conses then CAR and CDR. [ Yes, there is a difference if you want to respect the List Abstract Data Type. Most of the List ADT operations take proper lists. Whereas when you are dealing with conses you don't need to maintain a proper list. If lift the "hood" then lists are cons and Lisp sometimes doesn't do a good job of cleanly seperated the two. ]
>I would think (based on my admittedly very limited experience) that >c[ad]r would stack better than first and last (which I'm assuming is >the equivalent of cdr?)
FIRST and REST. LAST is something you use to "grab" a the list cons cell. It is not the "dual" of FIRST.
>only lets me stack them four deep without needing to do something like >(caadar (cdar foo)).) Perhaps the reason why I think this is an
It is highly likely that the cdar of any data structure you might be using to solve a problem has a much more meaningful name than CDAR. However, that is a question of style.
--
Lyman S. Taylor "Computers are too reliable to replace (ly...@cc.gatech.edu) humans effectively." Commander Nathan Spring, "Starcops"
daveva#earthling.net (Dave Vandervies) writes: > On 30 Nov 1998 16:52:48 +0000, Gareth McCaughan > <gj...@dpmms.cam.ac.uk> wrote:
> [...]
> >In a few hundred lines you can implement the usual arithmetic > >operations, conversion between my representation and infinite > >continued fractions (represented, roughly, as infinite lists, > >which again use the first-class-ness of functions to good effect), > >some interesting transcendental numbers, roots of arbitrary > >polynomials, and so on.
> That `infinite lists' looks interesting... is that a basic construct > of the language, or just something that the semantics of the language > makes easy?
The semantics of the language makes it easy. They are not part of the language, but they are trivial to implement.
> [...]
> >Things about Lisp that make it much easier:
> > - we already have arbitrary-precision rational numbers > > - we have garbage collection, so it's safe to construct > > complicated things on the fly and use them without the > > need to keep a pointer around so we can free them
> In general, I like to know where my memory is going, but I can see how > that would be a pain for something like this. >:]
No you don't! :) As it apperas in the C++ ARM, "C programmers think memory management is too important to be left to the computer. Lisp programmers think memory management is too important to be left to the user."
In non-embedded applications (but I would not be so sure that embedded applications would not benifit as well) Lisp programmer are right.
> > - we have closures as first-class objects, allowing a simple > > representation of numbers and of "infinite lists"
> I may sound completely clueless here, but what does ``closures'' refer > to?
They are what in Java Newspeak are called "anonymous classes". :)
> > - it's interactive, so debugging is easier and the resulting > > system gets I/O and parsing for free
Erik Naggum <e...@naggum.no> writes: > * Steve Gonedes <jgone...@worldnet.att.net> > | You can do, (caar (acons 'a 'b 'c)) as well. You can use first instead > | of car; some like this better, I'm surprised that c{ad}r haven't been > | declared archaic.
> because CONS is not a list-building operator, it's a tree-building > operator. lists are just special cases of trees. and vice versa. :)
> FIRST, SECOND,... and REST work on lists, C[AD]+R on trees.
Right. How about a set of operations like
TREE-T tree top TREE-LT tree left top TREE-RT tree right top TREE-LRT tree left rigth top TREE-RLLRT tree right left left right top TREE-[RL]+T ?
daveva#earthling.net (Dave Vandervies) writes: > I may sound completely clueless here, but what does ``closures'' > refer to?
It is essentially a set of variable bindings that aren't accessible in the normal way, but which there are still function objects around that has access to.
As a first example:
(defun make-adder (number) (lambda (x) (+ x number)))
Calling (make-adder 8) returns a function that remembers that NUMBER is 8, even after the function MAKE-ADDER returns.
If you then call (make-adder 6), you will have two function objects, each of which have their own opinion of what NUMBER is:
After the let has finished, the variable WOMBLE is only accessible through the two functions.
Note: The last example is a not considered 'The Right Thing' by some as it is hard to change the accessor functions without losing the state of the closure variable, because running the (let) again creates a _new_ WOMBLE.
The Right Thing, these days, is using CLOS for stuff like this.
Stig Hemmer, perl -e 'print "Not a Perl hacker.\n"' Jack of a Few Trades. "Just when you accept that life's a bitch, it has puppies."
* Dave Vandervies wrote: > That `infinite lists' looks interesting... is that a basic construct > of the language, or just something that the semantics of the language > makes easy?
It's something the semantics makes easy. You can represent a lazy list as a pair where the second (cdr) element of the pair is a promise to create more list on demand. And that in turn you can implement by having operators DELAY which take an expression and produce an object which is a promise to evaluate it later, and FORCE which takes a promise and causes it to be evaluated.
An obvious way of representing a promise is as a function of no arguments. Forcing the promise is just calling the function:
DELAY must be a macro because it can not evaluate its arguments.
This has problems because you probably want to remember whether a promise has been forced or not or you might get odd results. A slightly more sophisticated version is:
(defstruct (promise (:print-function (lambda (p s k) (declare (ignore k)) (print-unreadable-object (p s :type t :identity t))))) thing forced-p)
Dave Vandervies wrote: > This looks interesting. Would you be willing to send me a copy?
Yes, I expect so. It's rather nasty at present, though. You have been warned. :-)
> The way I did digits of Pi in C was to store the numbers as large > (currently working on 500000 digits) arrays of integers, and perform > the math (using the arctangent formula to calculate Pi, addition and > subtraction of these big numbers and multiplying/dividing them by > 32-bit integers is all that is needed) using them as fixed-point > numbers. The major disadvantage to this (at least as I see it) is > that if I want more digits I have to start over at the beginning, and > that I can't get a partial value before it's done the n digits (though > with an evening of work I *might* be able to get it to store its > progress digit-by-digit as it works it out).
I also have to start at the beginning every time I compute pi (although I believe there's an algorithm that avoids this). I'm using the AGM algorithm, which converges rather fast. Because Lisp has bignums, of course, I don't have all the pain of working with arrays of 32-bit integers for everything. I think my definition of "pi" is under 10 lines.
> That `infinite lists' looks interesting... is that a basic construct > of the language, or just something that the semantics of the language > makes easy?
The latter. I see that other people have already posted explanations, so I shan't bother.
>> - we have garbage collection, so it's safe to construct >> complicated things on the fly and use them without the >> need to keep a pointer around so we can free them
> In general, I like to know where my memory is going, but I can see how > that would be a pain for something like this. >:]
Yes. The point is that if you say something like x=f(g(y),h(z)) you need to have some way of freeing the intermediate stuff produced by g,h. In Lisp you write
On 1 Dec 1998 02:28:19 -0500, ly...@cc.gatech.edu (Lyman S. Taylor) wrote:
>In article <36630492.628...@news.tct.net>, >Dave Vandervies <daveva#earthling.net> wrote: >>only lets me stack them four deep without needing to do something like >>(caadar (cdar foo)).) Perhaps the reason why I think this is an
> It is highly likely that the cdar of any data structure you might > be using to solve a problem has a much more meaningful name than > CDAR. However, that is a question of style.
My opinions on style put (caadadar foo) ahead of a symbolic name for that particular node of a tree, especially for (probably a bad example, since I can't come up with a real-world situation where it would be relevant) code where I have to access a significant proportion of the 2^6 nodes 6 layers deep in a tree structure. Can you imagine typing (first (first (rest (first (rest (first foo)))))) 50 times? Of course, code that does something like that would have to be documented well enough for the average programmer to easily understand what it's doing and why, since at the moment I'm more interested in maintainability than job security. >:]
-- If you feel the need to email me personally, feel free to correct the obvious typo in my email address.
* daveva#earthling.net (Dave Vandervies) | Can you imagine typing (first (first (rest (first (rest (first foo)))))) | 50 times?
how about (first (second (second (first foo))))?
in case you're referring to a tree, how about (tree-node foo 0 1 1 0)? note that the order of the arguments is reversed from the above code. the location in a tree is specified with the number of CDRs before a CAR. other location specifications may be more useful in a given case.
| Of course, code that does something like that would have to be documented | well enough for the average programmer to easily understand what it's | doing and why, since at the moment I'm more interested in maintainability | than job security. >:]
funny how that actually improves job security... :)
#:Erik -- The Microsoft Dating Program -- where do you want to crash tonight?
Dave Vandervies <daveva#earthling.net> wrote: >On 1 Dec 1998 02:28:19 -0500, ly...@cc.gatech.edu (Lyman S. Taylor) >wrote:
>>In article <36630492.628...@news.tct.net>, >>Dave Vandervies <daveva#earthling.net> wrote: >>>only lets me stack them four deep without needing to do something like >>>(caadar (cdar foo)).) Perhaps the reason why I think this is an
>> It is highly likely that the cdar of any data structure you might >> be using to solve a problem has a much more meaningful name than >> CDAR. However, that is a question of style.
>My opinions on style put (caadadar foo) ahead of a symbolic name for >that particular node of a tree, especially for (probably a bad >example, since I can't come up with a real-world situation where it >would be relevant) code where I have to access a significant >proportion of the 2^6 nodes 6 layers deep in a tree structure. Can >you imagine typing (first (first (rest (first (rest (first foo)))))) >50 times? Of course, code that does something like that would have to >be documented well enough for the average programmer to easily >understand what it's doing and why, since at the moment I'm more >interested in maintainability than job security. >:]
You're talking about 64 nodes of a tree, which obviously has a certain defined structure. How stable is this structure going to be? If you decide that the structure has to change, how easy is it going to be to change it? You can of course search for all examples of "caadadar" in your code. Are all of them applicable to that data structure? Given a "caadar", does that make an abbreviated reference to something that's a "cdar" of a base tree?
My opinion is that either you're doing a quick-and-dirty or you aren't. If you're doing a quick-and-dirty with six-layer trees, you're in trouble already, particularly if you're accessing a sample node 50 times. If you're not, then you should keep it maintainable and changeable.
Of course, as you say, you could document it. My preferred method of documentation would be something like
Marco Antoniotti <marc...@copernico.parades.rm.cnr.it> writes:
< Are you sure this is "allowed" by the ANSI standard? The third < argument to ACONS should be an alist (association list).
It works in emacs lisp... my mistake.
< Everything. Could you post them?
Here's the binary type1 to ascii type1 converter. It takes a binary font (pfb) and dumps an ascii font (pfa) suitable for sending to a postscript printer. I'm still working on the other stuff though.
Would love to hear some comments of improvemnts and such.
#| Some commentary...
Binary type1 fonts have three sections, an ascii section for the font dictionary (fontname, encoding, etc.), a binary section for the encoded charstrings (font program), and a final ascii section that is usually just a bunch of zeros for faster decoding on printers.
A section header is six 8-bit bytes: 1 #x80 magic header 2 section type, 1 for ascii or 2 for binary 3-6 used to find the section size in 8-bit bytes (read-section-size).
|#
;;;; November 30, 1998 Monday 5:30 PM ;;;; Convert a binary type1 to an ascii type1 font
(defmacro section-assertion-bind ((size-var) section-type input-stream) "Bind the variable SIZE-VAR to the length of the following section. Check that the following section has the proper marker and is the type indicated by SECTION-TYPE. Destructively modify the binary-input input-stream by five 8-bit bytes."
(let ((local-size (gensym "SIZE")) (local-section (gensym "SECTION")) (section (gensym "SECTION")) (stream (gensym "STREAM")) let-bindings) (assert (symbolp size-var) (size-var) "SIZE-VAR must be a setf-able symbol, not ~s" size-var)
;; If the variables are constants then we can evaluate them more ;; than one time - so check for it. Is this bad idea? (cond ((integerp section-type) (setq section section-type)) (t (push `(,section ,section-type) let-bindings)))