5 ms·
Very useful! I suppose I was wrong to say the syntax is confusing, because lisp is so syntactically minimalistic. But when I look through lisp code, I can't det
by m0th87 17y ago
Very useful! I suppose I was wrong to say the syntax is confusing, because lisp is so syntactically minimalistic. But when I look through lisp code, I can't determine anything with respect to scope. Take, for example, the code in pg's A Plan for Spam:
http://www.paulgraham.com/spam.html http://www.paulgraham.com/spam.html
(let ((prod (apply #'* probs)))
(/ prod (+ prod (apply #'* (mapcar #'(lambda (x)
(- 1 x))
probs)))))
It took me several minutes to weed through this code. Part of that is because I didn't know what, say, apply did, and had to look it up. That's not an issue because you have to do that when learning any new language. But I had to carefully inspect the source bottom-up and break it apart because I couldn't tell what the arguments are on each function call. An IDE that does block highlighting would help a lot, but even then it feels overwhelming, because it only helps me inspect the block that my mouse cursor is currently on.
I guess what I'm saying is, I feel as if the syntactical constructs in other languages are necessary for me to be able to quickly parse a language.
- silentbicycle 17y agoLisp code is almost always written on multiple lines, with an editor that automatically indents everything. Code in a more familiar language (C?) would also look incomprehensible if ten lines were all smushed together on one. I let Emacs indent that, and it came out like so: (let ((prod (apply #'* probs))) (/ prod (+ prod (apply #'* (mapcar #'(lambda (x) (- 1 x)) probs))))) You still have to learn the idioms (it's a different language!), but it's not just a jumble of parenthesis. Prefix notation takes some getting used to for arithmetic, but that's neither here nor there. You probably have a bundle of idiosyncratic operator precedence rules memorized (quick, does 3*4+5 equal 27 or 17?), and Lisp doesn't use those, preferring to consistently use parenthetical nesting for everything. See also: http://img264.imageshack.us/img264/1397/lispnd7.png http://img264.imageshack.us/img264/1397/lispnd7.png
- mahmud 17y agoPaul Graham has a peculiar Lisp style which is NOT mainstream. He is succinct, clever, and usually performance-agnostic. He writes expository code like we write one-liners in the repl to quickly test something out. That example you linked to is confusing because: 1) You don't know the syntax to read it out like a formula, and 2) he uses the names prod and probs which look the same and strain the mind of a newbie. The way you read "hard" Lisp code, specially one in Graham's style is to read from the last line upwards. The tell-tale is the deep indentation, btw. Start at the last line, which is the body of MAPCAR, you know mapcar is MAP which applies its first argument function to successive elements of its second-argument, which should be a list. (mapcar #'(lambda (x) (- 1 x)) probs) Make a mental note about the free variable, probs. You can recognize the function as the usual arithmetic step of converting integers to probability values between 0 and 1. The sharp-quote, #', is an archaic style and unnecessary, so let's wrap up these two lines into a function and change the names to be readable. (defun normalize-probabilities (probabilities) (mapcar (lambda (x) (- 1 x)) probabilities)) Ok. Look up further and notice the result of this function call, a list, is being reduced to an integer. APPLY applies its first argument, a function, to its second argument, a list. For example, (apply #'+ (list 1 2 3 4)) means sum of the first four digits. C programmers should find this familiar: (apply #'main argv) Where MAPCAR returns a list, apply returns whatever its function argument is supposed to return. We know the function #'* takes a list of numbers and returns their product. So, the snippet is just multiplying the list of probabilities, or reducing to a scalar. (apply #'* (normalize-probabilities probs)) Then: (+ prod (apply #'* (normalize-probabilities probs))) And here something called prod is being added to our scalar. Hmmm. Look up a bit and see where that's coming from. The nearest binding form for prod is in the LET body on top. Looks very similar to something we have seen before. Another application of multiplication to the list named probs. This is not a Lisp issue, it's a Bayesian statistics or perhaps a PG issues. You continue reading the paper and discover his formula in English prose. Aha! :-) You can move code around, tuck it into functions and do whatever you want with it. See if you recognize the code snippet now: (defun normalize-probabilities (probabilities) (mapcar (lambda (x) (- 1 x)) probabilities)) (defun pg-foo-fomula (product) (/ product (+ product (apply #'* (normalize-probabilities probs))))) (defun main () (loop (let ((probabilities (eval (read)))) (when (listp probabilities) (print (pg-foo-formula (apply #'* probabilities)))) (princ "OK > ")))) Typically you don't do that; you don't make silly repls and you never ever write a MAIN or any other program entry function. You write libraries, that you use in other libraries, and you orchestrate them all via interactive use and deploy in a custom image.
- m0th87 17y agoYou should write a book on practical lisp. I'd buy it!