Blog Ini Bertujuan Membantu mendidik masyarakat di bidang matematik (Helping community in studying mathematic)
Selasa, 19 Februari 2008
Pasang Iklan Teks
Sabtu, 16 Februari 2008
Metamorfosis Ujian Nasional
Thinking Blogger Award
I congratulate Leena on behalf of Planet Infinity.
And Now ....She has awarded my class blog with Thinking Blogger Award.
She wrote on her blog "Rash Kath, whose passion in mathematics blogging has made me convinced that I’ll start a real mathematical blog to my students. My current Finnish blog concentrates on the entertaining and beautiful side of math, like this one. Rash Kath also created a group named Passionate Teachers and the amount of knowledge shared there is even too much for me to swallow instantly. Now I know what I will be doing next summer on my vacation."
I would like to thank Leena for this appreciation. I feel proud to put this badge.

About the awardThe participation rules are simple:1. If, and only if, you get tagged, write a post with links to 5 blogs that make you think,2. Link to this post so that people can easily find the exact origin of the meme,3. Optional: Proudly display the 'Thinking Blogger Award' with a link to the post that you wrote (here is an alternative silver version if gold doesn't fit your blog).In addition there is a note: “Please, remember to tag blogs with real merits, i.e. relative content, and above all - blogs that really get you thinking! "
I feel from my side I would like to share with this world about 5 genius people...who are doing excellent job in the field of education.
Now, 5 blogs which make me think ...
1. Lizbdavis ... Wellesley, MA, United StatesShe is a dynamic, passionate Instructional Technology Specialist and TV/Video teacher at the Wellesley High School in Wellesley, MA, a suburb of Boston. She believes in the power of educational technology to help students and teachers succeed in the 21st century. I got to know about her from a post on classroom 20 regarding Twitter. Since then I am learning from her through her sharings on various web platforms.
2.Kwhobbes... Kelly Christopherson, a principal in a K - 12 school in Saskatchewan. He is a great administrator , educator helping and guiding teachers to use techology in classrooms . His posts are inspiring and very close to our daily life routine realities. He is guiding many teachers all across this globe on Passionate teachers ning also. Read his discussions, you will surely experience a new thing.
3.Kolson29 ...Kate Olson , a 21st century Business education teacher ,an e teacher, a self explorer .She is a teacher who has always inspired me through her continual explorations. I am following her on twitter too. She shares her knowledge with the community.
4.Murcha ...Anne Mirtschin is another passionate teacher. Visit her blog ,you would come to know about her interesting sharings about her classroom experiences and explorations. I always appreciate the work and experiments which teachers do for manking teaching learning a joyful experience.
5.Suryaveer...Suryaveer Singh ,India. He is a passionate geographer. He has created a wonderful blog and a wiki in his subject geography.Also, he is continuously exploring e tools and various e strategies to make his subject interesting for students.
I was given this opportunity by Leena, Finland.Now, this award will travel through India to many other countries.
Congratulations to you all for your great contribution in the field of education.
Keep Sharing.
Best Regards
Minggu, 10 Februari 2008
Portrait of a N00b
| The older I grow, the less important the comma becomes. Let the reader catch his own breath. — Elizabeth Clarkson Zwart |
This is how I used to comment my code, twenty years ago (Note: dramatization):
/**Does this style look at all familiar? It should! This is, to put it as impolitely as possible, n00b-style. (Incidentally, if u dont no wat a n00b iz, u r 1.)
* By the time we get to this point in the function,
* our structure is set up properly and we've created
* a buffer large enough to handle the input plus some
* overflow space. I'm not sure if the overflow space
* is strictly necessary, but it can't hurt. Next we
* have to update the counter to account for the fact
* that the caller has read a value without consuming
* it. I considered putting the counter-increment on
* the shoulders of the caller, but since it meant every
* caller had to do it, I figured it made more sense to
* just move it here. We can revisit the decision down
* the road if we find some callers that need the option
* of incrementing it themselves.
*/
counter++; // increment the consumed-value counter
/**
* Now we've got to start traversing the buffer, but we
* need an extra index to do it; otherwise we'll wind up
* at the end of the function without having any idea
* what the initial value was. I considered calling this
* variable 'ref', since in some sense we're going to be
* treating it as a reference, but eventually I decided
* it makes more sense to use 'pos'; I'm definitely open
* to discussion on it, though.
*/
char* pos = buffer; // start our traversal
/**
* NEXT, we...
*/
This is how junior programmers write code. If you've read Malcom Gladwell's remarkable and eye-opening book The Tipping Point, you'll notice a striking similarity to the real-life 2-year-old Emily he describes in Chapter Three, who tells herself stories after her parents leave her room. Here's a short excerpt from one of her stories:
Tomorrow when we wake up from bed, first me and Daddy and Mommy, you, eat breakfast eat breakfast like we usually do, and then we're going to play and then soon as Daddy comes, Carl's going to come over, and then we're going to play a little while. And then Carl and Emily are both going down to the car with somebody, and we're going to ride to nursery school [whispered], and then when we get there, we're all going to get out of the car...Gladwell's account of Emily is fascinating, as she's allegedly a completely normal 2-year-old; they all do this when Mommy and Daddy aren't around.
Gladwell explains:
Sometimes these stories were what linguists call temporal narratives. She would create a story to try to integrate events, actions, and feelings into one structure — a process that is a critical part of a child's mental development.If you look back at the comments in my hypothetical code from 20 years ago, you'll see that I was doing exactly what Emily does: making up a temporal narrative in an attempt to carve out a mental picture of the computation for myself. These stories I told myself were a critical part of my mental development as a programmer. I was a child trying to make sense of a big, scary new world.
Most programmers go through this phase. It's perfectly normal.
In contrast, here's what my code tends to look like today:
Update, Nov 14 2011: I did a terrible job of making my point with this code. I deliberately chose some of the most freakish code I've ever written, because I wanted it to look ugly and scary. I'm trying to show here what "typical" veteran code looks like to a junior programmer. This code serves as a *caricature* for illustration purposes. You're supposed to be put off by it. If I had been trying to show you what modern art looks like to the uninitiated, I would have showed you a graffitied subway station wall that someone had just vomited on. This is the coding equivalent.
If you *insist* on missing my point entirely and arguing about whether this function is "good code" or not, then I assure you: this code is horrific. It's a Lisp port of a Java port of some old C code. *Both* ports intentionally stay as faithful to the original as possible, line-by-line in the most un-idiomatic code imaginable. Why? To make it easy to propagate bug fixes in the original to both ports. So it's ugly for a legitimate reason. But it's still frigging ugly.
(defun js2-parse-variables (in-for decl-type)If I'd seen this code 20 years ago I'd have been appalled. The lines of code are all crammed together! Some of them aren't even commented! If I'd been given the task of maintaining this code, I'd have been screaming "rewrite!"
"Parse a 'var', 'const' or 'let' statement or for-loop initializer.
IN-FOR is true if we are currently in the midst of the init clause of a for.
DECL-TYPE is a token value: either VAR, CONST, or LET depending on context.
Returns the parsed statement node."
(let ((result (make-js2-var-decl-node))
destructuring-init
destructuring
s start tt init name node
(continue t))
;; Examples:
;; var foo = {a: 1, b: 2}, bar = [3, 4];
;; var {b: s2, a: s1} = foo, x = 6, y, [s3, s4] = bar;
(while continue
(setq destructuring nil
s nil
tt (js2-peek-token)
start js2-token-start
init nil)
(if (or (= tt js2-LB) (= tt js2-LC))
;; Destructuring assignment, e.g., var [a, b] = ...
(setq destructuring (js2-parse-primary-expr))
;; Simple variable name
(js2-must-match js2-NAME "msg.bad.var")
(setq name (make-js2-name-node))
(js2-define-symbol decl-type js2-ts-string))
(when (js2-match-token js2-ASSIGN)
(setq init (js2-parse-assign-expr in-for)))
(if destructuring
(progn
(if (null init)
;; for (var [k, v] in foo) is initialized differently
(unless in-for
(js2-report-error "msg.destruct.assign.no.init")))
(setq node (make-js2-destructuring-init-node :start start
:end js2-ts-cursor
:lhs destructuring
:initializer init))
(js2-node-add-children node destructuring init))
;; simple variable, possibly with initializer
(setq node (make-js2-var-init-node :start start
:end js2-ts-cursor
:name name
:initializer init))
(js2-node-add-children node name init))
(js2-block-node-push result node)
(js2-node-add-children result node)
(unless (js2-match-token js2-COMMA)
(setq continue nil)))
result))
I probably write more Java and JavaScript these days, but I picked an Emacs-Lisp function I wrote recently to highlight how alien my code today would have looked to me twenty years ago.
To be fair, this function is actually a port of some Java code from Mozilla Rhino's JavaScript parser, which in turn is a port of some C code from SpiderMonkey's parser, which in turn was probably borrowed and modified from some other compiler. Compiler code tends to have some of the purest lineage around, tracing back to the assembly-language code they wrote for the first compilers 40 or 50 years ago. Which means it's going to be a bit on the ugly side compared to "ordinary" code.
But when I write code in other languages these days, even in Java, it looks a lot more like this Emacs Lisp fragment than like the n00b code I was writing 20 years ago. It's denser: there's less whitespace and far less commenting. Most of the commenting is in the form of doc-comments for automated API-doc extraction. On the whole, my code today is much more compressed.
In the old days, seeing too much code at once quite frankly exceeded my complexity threshold, and when I had to work with it I'd typically try to rewrite it or at least comment it heavily. Today, however, I just slog through it without complaining (much). When I have a specific goal in mind and a complicated piece of code to write, I spend my time making it happen rather than telling myself stories about it.
A decade of experience makes you a teenager
After going through their 2-year-old phase, programmers eventually have to go through a stupid-teenager phase. All this month I've been hearing sad but unsurprising news stories about teenagers getting stuck on big rocks, being killed falling off cliffs, or dying of exposure. I'm actually lucky the same didn't happen to me when I was a teenager. It's just a bad time for us. Even though teenagers are old enough to understand the warnings, they have this feeling of invincibility that gets them into trouble and often mortal peril.
The programming equivalent happens around us all the time too. Junior programmers with five to ten years of experience under their belts (still n00bs in their own way) attempt to build giant systems and eventually find themselves stuck on the cliff waiting for a helicopter bailout, telling themselves "my next system rewrite will be better!" Or they fall off the cliff – i.e., the project gets canceled, people get laid off, maybe the company goes under.
Yes, I've gone through that phase too. And let's face it: even seasoned programmers need a little optimism and a little bravery in order tackle real challenges. Even as an experienced programmer, you should expect to fail at projects occasionally or you're probably not trying hard enough. Once again, this is all perfectly normal.
That being said, as a hiring manager or company owner you should keep in mind that "5 to 10 years of experience" on a resume does not translate to "experienced"; it means "crazy invincible-feeling teenager with a 50/50 shot at writing a pile of crap that he or she and his or her team can't handle, and they'll eventually, possibly repeatedly, try to rewrite it all." It's just how things are: programmers can't escape being teenagers at some point.
Building compression tolerance
Hopefully the scene I've painted so far helps you understand why sometimes you look at code and you just hate it immediately. If you're a n00b, you'll look at experienced code and say it's impenetrable, undisciplined crap written by someone who never learned the essentials of modern software engineering. If you're a veteran, you'll look at n00b code and say it's over-commented, ornamental fluff that an intern could have written in a single night of heavy drinking.
The sticking point is compression-tolerance. As you write code through your career, especially if it's code spanning very different languages and problem domains, your tolerance for code compression increases. It's no different from the progression from reading children's books with giant text to increasingly complex novels with smaller text and bigger words. (This progression eventually leads to Finnegan's Wake, if you're curious.)
The question is, what do you do when the two groups (vets and n00bs) need to share code?
I've heard (and even made) the argument that you should write for the lowest common denominator of programmers. If you write code that newer programmers can't understand, then you're hurting everyone's productivity and chances for success, or so the argument goes.
However, I can now finally also see things from the veteran point of view. A programmer with a high tolerance for compression is actually hindered by a screenful of storytelling. Why? Because in order to understand a code base you need to be able to pack as much of it as possible into your head. If it's a complicated algorithm, a veteran programmer wants to see the whole thing on the screen, which means reducing the number of blank lines and inline comments – especially comments that simply reiterate what the code is doing. This is exactly the opposite of what a n00b programmer wants. n00bs want to focus on one statement or expression at a time, moving all the code around it out of view so they can concentrate, fer cryin' out loud.
So it's a problem.
Should a team write for the least common denominator? And if so, exactly how compressed should they make the code? I think the question may be unanswerable. It's like asking for a single format for all books, from children's books to epic novels. Each team is going to have its own average preference. I suspect it's a good idea to encourage people to move their stories into design documents and leave them out of the code, since a junior programmer forced to work in a compressed code base may well grow up faster.
As for me, at this point in my career I would rather puzzle through a small, dense, complex piece of code than a massive system with thousands of files containing mostly comments and whitespace. To some people this trait undoubtedly flags me as a cranky old dinosaur. Since this is likely the majority of programmers out there, maybe I am a cranky old dinosaur. Rawr.
Metadata Madness
Everyone knows that comments are metadata: information about the data (in this case, the data being your source code.) But people often forget that comments aren't just a kind of metadata. Comments and metadata are the same thing!
Metadata is any kind of description or model of something else. The comments in your code are just a a natural-language description of the computation. What makes metadata meta-data is that it's not strictly necessary. If I have a dog with some pedigree paperwork, and I lose the paperwork, I still have a perfectly valid dog.
You already know the comments you write have no bearing on the runtime operation of your code. The compiler just throws them away. And we've established that one hallmark of a n00b programmer is commenting to excess: in a sense, modeling every single step of the computation in painstaking detail, just like Emily modeled her ideal Friday by walking through every step and reassuring her 2-year-old self that she really did understand how it was going to work.
Well, we also know that static types are just metadata. They're a specialized kind of comment targeted at two kinds of readers: programmers and compilers. Static types tell a story about the computation, presumably to help both reader groups understand the intent of the program. But the static types can be thrown away at runtime, because in the end they're just stylized comments. They're like pedigree paperwork: it might make a certain insecure personality type happier about their dog, but the dog certainly doesn't care.
If static types are comments, then I think we can conclude that people who rely too much on static types, people who really love the static modeling process, are n00bs.
Hee hee.
Seriously, though: I'm not actually bashing on static-typing here; I'm bashing on the over-application of it. Junior programmers overuse static typing in the exact same way, and for the same reasons, as they overuse comments.
I'll elaborate by first drawing a parallel to data modeling, which is another kind of "static typing". If you've been working in a field that uses relational databases heavily, you'll probably have noticed that there's a certain personality type that's drawn to relational data modeling as a career unto itself. They're usually the logical modelers, not the physical modelers. They may have begun their careers as programmers, but they find they really love data modeling; it's like a calling for them.
If you know the kind of person I'm talking about, you'll doubtless also have noticed they're always getting in your way. They band together and form Database Cabals and Schema Councils and other obstructive bureacracies in the name of safety. And they spend a lot of time fighting with the engineers trying to get stuff done, especially at the fringes: teams that are not working directly with the schema associated with the main revenue stream for the company, but are out trying to solve tangential problems and just happen, by misfortune, to be homed in the same databases.
I've been in surprisingly many situations at different companies where I had a fringe team that was being held up by data modelers who were overly-concerned about data integrity when the real business need was flexibility, which is sort of the opposite of strong data modeling. When you need flexible storage, name/value pairs can get you a long, long, LONG way. (I have a whole blog planned on this topic, in fact. It's one of my favorite vapor-blogs at the moment.)
It's obviously important to do some amount of data modeling. What's not so obvious is when to stop. It's like commenting your code: newer programmers just don't know when to quit. When you're a little insecure, adding comments and metadata are a great security-blanket that make you feel busy when you've in fact stopped making forward progress and are just reiterating (or perhaps teaching yourself) what's already been accomplished.
Hardcore logical data modelers often suffer from an affliction called metadata addiction. Metadata modeling is seductive. It lets you take things at a leisurely pace. You don't have to be faced with too much complexity at once, because everything has to go in a new box before you'll look at it. To be sure, having some metadata (be it a data model, or static types, or comments) is important for human communication and to some extent for performance tuning. But a surprising percentage of people in our industry take it too far, and make describing an activity more important than the activity itself.
The metadata-addiction phenomenon applies equally to coders. Code is data, and data is code. The two are inextricably linked. The data in your genes is code. The floor plans for your house are code. The two concepts are actually indistinguishable, linked at a fundamental level by the idea of an Interpreter, which sits at the very heart of Computer Science. Metadata, on the other hand, is more like the kidney of Computer Science. In practice you can lose half of it and hardly notice.
Creeping bureacracy
I think that by far the biggest reason that C++ and Java are the predominant industry languages today, as opposed to dynamic languages like Perl/Python/Ruby or academic languages like Modula-3/SML/Haskell, is that C++ and Java cater to both secure and insecure programmers.
You can write C++ like straight C code if you like, using buffers and pointers and nary a user-defined type to be found. Or you can spend weeks agonizing over template metaprogramming with your peers, trying to force the type system to do something it's just not powerful enough to express. Guess which group gets more actual work done? My bet would be the C coders. C++ helps them iron things out in sticky situations (e.g. data structures) where you need a little more structure around the public API, but for the most part they're just moving data around and running algorithms, rather than trying to coerce their error-handling system to catch programmatic errors. It's fun to try to make a bulletproof model, but their peers are making them look bad by actually deploying systems. In practice, trying to make an error-proof system is way more work than it's worth.
Similarly, you can write Java code more or less like straight C, and a lot of seasoned programmers do. It's a little nicer than C because it has object-orientation built in, but that's fairly orthogonal to the static type system. You don't need static types for OOP: in fact OOP was born and proven in dynamic languages like Smalltalk and Lisp long before it was picked up by the static-type camps. The important elements of OOP are syntax (and even that's optional) and an object model implemented in the runtime.
So you can write Java code that's object-oriented but C-like using arrays, vectors, linked lists, hashtables, and a minimal sprinkling of classes. Or you can spend years creating mountains of class hierarchies and volumes of UML in a heroic effort to tell people stories about all the great code you're going to write someday.
Perl, Python and Ruby fail to attract many Java and C++ programmers because, well, they force you to get stuff done. It's not very easy to drag your heels and dicker with class modeling in dynamic languages, although I suppose some people still manage. By and large these languages (like C) force you to face the computation head-on. That makes them really unpopular with metadata-addicted n00bs. It's funny, but I used to get really pissed off at Larry Wall for calling Java programmers "babies". It turns out the situation is a little more complicated than that... but only a little.
And Haskell, OCaml and their ilk are part of a 45-year-old static-typing movement within academia to try to force people to model everything. Programmers hate that. These languages will never, ever enjoy any substantial commercial success, for the exact same reason the Semantic Web is a failure. You can't force people to provide metadata for everything they do. They'll hate you.
One very real technical problem with the forced-modeling approaches that static type systems are often "wrong". It may be hard to imagine, because by a certain definition they can't be "wrong": the code (or data) is programmatically checked to conform to whatever constraints are imposed by the type system. So the code or data always matches the type model. But the type system is "wrong" whenever it cannot match the intended computational model. Every time want to use multiple inheritance or mixins in Java's type system, Java is "wrong", because it can't do what you want. You have to take the most natural design and corrupt it to fit Java's view of the world.
An important theoretical idea behind type systems is "soundness". Researchers love to go on about whether a type system is "sound" or not, and "unsound" type systems are considered bad. C++ and Java have "unsound" type systems. What researchers fail to realize is that until they can come up with a type system that is never "wrong" in the sense I described earlier, they will continue to frustrate their users, and their languages will be abandoned for more flexible ones. (And, Scala folks, it can't just be possible to express things like property lists – it has to be trivial.)
To date, the more "sound" a type system is, the more often it's wrong when you try to use it. This is half the reason that C++ and Java are so successful: they let you stop using the type system whenever it gets in your way.
The other half of their success stems from the ability to create user-defined static types. Not, mind you, because they're helpful in creating solidly-engineered systems. They are, sure. But the reason C++ and Java (particularly Java) have been so successful is that their type systems form a "let's not get any work done" playground for n00bs to spend time modeling things and telling themselves stories.
Java has been overrun by metadata-addicted n00bs. You can't go to a bookstore or visit a forum or (at some companies) even go to the bathroom without hearing from them. You can't actually model everything; it's formally impossible and pragmatically a dead-end. But they try. And they tell their peers (just like our metadata-addicted logical data modelers) that you have to model everything or you're a Bad Citizen.
This gets them stuck on cliffs again and again, and because they're teenagers they don't understand what they did wrong. Static type models have weight and inertia. They take time to create, time to maintain, time to change, and time to work around when they're wrong. They're just comments, nothing more. All metadata is equivalent in the sense of being tangential documentation. And static type models get directly in the way of flexibility, rapid development, and system-extensibility.
I've deleted several thousand words about the evolution of Apache Struts and WebWork, an example framework I chose to illustrate my point. Rather than waste a bunch of time with it, I'll just give you a quote from one of the Struts developers in "The Evolution of Struts 2":
...the Struts 1 code base didn’t lend itself to drastic improvements, and its feature set was rather limited, particularly lacking in features such as Ajax, rapid development, and extensibility."Struts 2 was thrown away for WebWork, which was in the process of throwing away version 1 (for similar reasons) in favor of version 2 (which has all the same problems).
Some of those several thousand words were devoted to JUnit 4, which has comically (almost tragically) locked on, n00b-style, to the idea that Java 5 annotations, being another form of metadata, are the answer to mankind's centuries of struggle. They've moved all their code out of the method bodies and into the annotations sections. It's truly the most absurd overuse of metadata I've ever seen. But there isn't space to cover it here; I encourage you to go goggle at it.
There are die-hard Java folks out there who are practically gasping to inject the opinion, right here, that "rapid development" is a byproduct of static typing, via IDEs that can traverse the model.
Why, then, was Struts considered by its own developers to be a failure of rapid development? The answer, my dear die-hard Java fans, is that a sufficiently large model can outweigh its own benefits. Even an IDE can't make things go faster when you have ten thousand classes in your system. Development slows because you're being buried in metadata! Sure, the IDE can help you navigate around it, but once you've created an ocean, even the best boats in the world take a long time to move around it.
There are hundreds of open-source and proprietary Java frameworks out there that were designed by code-teenagers and are in perpetual trouble. I've often complained that the problem is Java, and while I think the Java language (which I've come to realize is disturbingly Pascal-like) is partly to blame, I think the bigger problem is cultural: it's hard to restrain metadata addiction once it begins creeping into a project, a team, or an organization.
Java programmers, and logical data modelers, and other metadata-addicted developers, are burying us with their "comments" in the form of models within their static type system. Just like I did when I was a n00b. But they're doing it with the best of intentions, and they're young and eager and energetic, and they stand on street corners and hand you leaflets about how great it is to model everything.
Seasoned programmers ignore them and just get it done.
Solutions and takeaways
Software engineering is hard to get right. One person's pretty data model looks like metadata-addiction to another person.
I think we can learn some lessons from code-commenting: don't try to model everything! You need to step back and let the code speak for itself.
For instance, as just one random illustrative example, you might need to return 2 values from a function in Java (a language with no direct support for multiple return values). Should you model it as a
MyFunctionCallResult class with named ValueOne and ValueTwo fields (presumably with actual names appropriate to the problem at hand)? Or should you just return a 2-element array (possibly of mixed types) and have the caller unpack it?I think the general answer to this is: when in doubt, don't model it. Just get the code written, make forward progress. Don't let yourself get bogged down with the details of modeling a helper class that you're creating for documentation purposes.
If it's a public-facing API, take a lesson from doc-comments (which should be present even in seasoned code), and do model it. Just don't go overboard with it. Your users don't want to see page after page of diagrams just to make a call to your service.
Lastly, if you're revisiting your code down the road and you find a spot that's always confusing you, or isn't performing well, consider adding some extra static types to clarify it (for you and for your compiler). Just keep in mind that it's a trade-off: you're introducing clarifying metadata at the cost of maintenance, upkeep, flexibility, testability and extensibility. Don't go too wild with it.
That way the cliff you build will stay small enough for you to climb down without a helicopter rescue.
Postscript
I'm leaving comments on, at least until "click-my-link" spam starts to surface. I'm curious to know how this entry goes over. This was an especially difficult entry to write. I did a lot of editing on it, and left out a lot as a result. I feel like I may not have made my points as clearly as I'd like. And I'm sure I haven't convinced the metadata-addicted that they have a problem, although at least now they know someone out there thinks they have a problem, which is a start.
Let me know what you think!
Kamis, 07 Februari 2008
Prediksi Sebaran Soal Ujian Nasional Matematika 2008
Minggu, 03 Februari 2008
Soal Try Out Ujian Nasional
Kamis, 31 Januari 2008
Mathematic Syndrome
Minggu, 27 Januari 2008
Ujian Nasional sudah dekat
Blog in News Channel
My class students and myself were interviewed by News Channels CNN IBN and Aaj Tak. The programme on Aaj Tak got telecast on 13.1.08 .
It was all about using blog as a teaching tool . Students shared their experiences of using Planet Infinity ...mathematics activity blog .
Kamis, 24 Januari 2008
Emergency Elisp
There are lots of ways to do things in Lisp, and some are "Lispier" than others. I'm going to focus on how to do things you probably already know how to do from C++ or Java.
I'm mostly focusing on the language itself, since that's arguably the hardest part. There are tons of Emacs-specific APIs that you can learn how to use from the documentation.
Lisp is good at some things (like code that generates code) and not so good at others (like arithmetic expressions). I will generally avoid talking about good vs. bad, and just talk about how to do things. Emacs Lisp is like any other language – you get used to it eventually.
Most Lisp introductions try to give you the "Tao of Lisp", complete with incense-burning, chanting, yoga and all that stuff. What I really wanted in the beginning was a simple cookbook for doing my "normal" stuff in Lisp. So that's what this is. It's an introduction to how to write C, Java or JavaScript code in Emacs Lisp, more or less.
Here goes. Let's see how short I can make it. I'll start with the boring (but hopefully familiar) lexical tokens and operators, then move on to how to implement various favorite statements, declarations and other programming constructs.
Quick Start
Lisp is written as nested parenthesized expressions like
(+ 2 3). These expressions are sometimes called forms (in the sense of "shapes".)There are also "atoms" (leaf nodes, basically) that are not parenthesized: strings, numbers, symbols (which must be quoted with apostrophe for use as symbols, like
'foo), vectors, and other miscellany.There are only single-line comments: semicolon to end of line.
To set a variable named
foo to the value "bar":(setq foo "bar") ; setq means "set quoted"To call a function named
foo-bar with arguments "flim" and "flam":(foo-bar "flim" "flam")
To compute the arithmetic expression (0x15 * (8.2 + (7 << 3))) % 2:
(% (* #x15 (+ 8.2 (lsh 7 3))) 2)
In other words, arithmetic uses prefix notation, just like lisp function calls.
There's no static type system; you use runtime predicates to figure out the type of a data item. In elisp, predicate functions often end with "p". I'll let you figure out what it stands for.
Important: You can (and should) experiment with Lisp in the
*scratch* buffer. You can evaluate an expression and see its result in any of several ways, including:- Put your cursor after the last close-paren and type
C-j(control + j) - Put your cursor inside the expression and type
M-C-x(alt + control + x) - Put your cursor after the last close-paren and type
C-x C-e
The first approach spits the result into the
*scratch* buffer, and the next two echo it into the minibuffer. They all also work for atoms – expressions not in parens such as numbers, strings, characters and symbols.Lexical Stuff
Lisp has only a handful of lexical tokens (i.e. atomic program elements).
Comments:
Single-line only. They start with a semicolon:
(blah blah blah) ; I am a comment
Strings:
Double-quoted only.
"He's said: \"Emacs Rules\" one time too many."
You can embed newlines in strings, like so:
"Oh Argentina!
Your little tin of pink meat
Soars o'er the Pampas"
Characters:
- ?x is the syntax for an ASCII character: ? followed by the character.
- e.g.: ?a is ascii 97 ('a'), ? (that is, question-mark space) is ascii 32 (' ').
- Some need to be escaped, such as ?\(, ?\) and ?\\
- Emacs 22+ has unicode support. Out of scope for this primer.
Numbers:
- Integers are 29 bits of precision (not the usual 32).
-32, 0, 157, etc. - Binary: start with #b, e.g.
#b10010110 - Octal: #o[0-7]+, e.g.
#o377 - Hexadecimal: start with #x, e.g.
#xabcd, #xDEADBEE - Floating-point: the usual.
-10.005, 0.0, 3.14159265(64 bits of precision.) - Scientific: the usual.
6.02e23,5e-10
The variables
most-positive-fixnum and most-negative-fixnum are the largest and smallest integers representable in Emacs Lisp without bignum support. Emacs 22+ comes with a fancy bignum/math library called calc, if you need it. Arithmetic operations overflow and underflow the way you'd expect (in, say, C or Java.)Booleans
The symbol
t (just a letter 't' by itself) is true.The symbol
nil is false (and also means null).In Emacs Lisp,
nil is the only false value; everything else evalutes to true in a boolean context, including empty strings, zero, the symbol 'false, and empty vectors. An empty list, '(), is the same thing as nil.Arrays
Elisp has fixed-sized arrays called "vectors". You can use square-brackets to create a pre-initialized literal vector, for instance:
[-2 0 2 4 6 8 10]
["No" "Sir" "I" "am" "a" "real" "horse"]
["hi" 22 120 89.6 2748 [3 "a"]]
Note that you do not (and cannot) use commas to separate the elements; use whitespace.
Vectors can have mixed-type elements, and can be nested. You usually use the function
make-vector to create them, since literal vectors are singletons, which can be surprising.Lists
Lisp makes heavy use of linked lists, so there's lexical syntax for them. Anything in parentheses is a list, but unless you quote it, it will be evaluated as a function call. There are various ways to quote things in Lisp:
(quote (1 2 3)) ; produces the list (1 2 3) with no list-element evaluation
'(1 2 3) ; apostrophe is shorthand for (quote (...))
; note that it goes _outside_ the left-paren
(list 1 (+ 1 1) 3) ; also produces (1 2 3), since it evaluates the elements first
`(1 ,(+ 1 1) 3) ; another (1 2 3) via a template system called "backquote"There's a lot more that could be said about lists, but other people have already said it.
Pairs
You can set the head and tail (also known as
car and cdr) fields of a lisp link-list node struct (also known as a cons cell) directly, using it as a 2-element untyped struct. The syntax is (head-value . tail-value), and you have to quote it (see above).A common lookup-table data-structure for very small data sets is an associative list (known as an
alist). It's just a list of dotted pairs, like so:'( (apple . "red")Emacs Lisp has built-in hashtables, bit-vectors, and miscellaneous other data structures, but there's no syntax for them; you create them with function calls.
(banana . "yellow")
(orange . "orange") )
Operators
Some operations that are typically operators in other languages are function calls in elisp.
Equality
Numeric equality:
(= 2 (+ 1 1)) Single-equal. Yields t or nil. Works for floats too.Not-numerically-equal:
(/= 2 3) I know, it looks like assign-divide-equal. But it's not.Value equality:
(eq 'foo 2) Like Java ==. Works for ints, symbols, interned strings, and object references. Use eql for floating-point numbers (or just =).Deep (structural) equality: use
equal, as in:(equal '(1 2 (3 4)) (list 1 2 (list 3 (* 2 2)))) ; true
The equal function is like Java's
Object.equals(). Works for lists, vectors, strings, and just about anything else.String
Strings don't have any operators, but there are lots of string functions. Some common ones:
(concat "foo" "bar" "baz") ; yields "foobarbaz"
(string= "foo" "baz") ; yields nil (false). Can also useequal.
(substring "foobar" 0 3) ; yields "foo"
(upcase "foobar") ; yields "FOOBAR"
Do
M-x apropos RET \bstring\b RET to see a list of functions related to strings.Arithmetic
Easiest to show as a table...
| C/Java/JS Operator | Emacs Lisp | Example | Result |
|---|---|---|---|
| + | + | (+ 1 2 3 4 5) | 15 |
| - | - | (- 6 2 3) | 1 |
| * | * | (* 2 -1 4.2) | -8.4 |
| / | / | (/ 10 3) | 3 (use floats for float div) |
| % | % | (% 10 2) | 0 |
| << | lsh | (lsh 1 5) | 32 |
| >> | ash (negative amount) | (ash -32 -4) | -2 |
| >>> | lsh (negative amount) | (lsh 32 -4) | 2 |
| ++ | incf (requires 'cl library) | (incf x 6) | x+6 |
| -- | decf (ditto) | (decf x 5) | x-5 |
| ? : (ternary) | (if test-expr then-expr else-expr) | (if t 3 4) | 3 |
| && | and | (and t t t nil) | nil |
| || | or | (or nil nil nil t) | t |
| ! (logical-not) | not | (not 3) | nil |
| ~ (bit-not) | lognot | (lognot #b1001) | -10 |
| ^ (bit-xor) | logxor | (logxor 5 3) | 6 |
| & (bit-and) | logand | (logand 1 3) | 1 |
| | (bit-or) | logior | (logior 1 3) | 3 |
| < | < | (< 5 3) | nil |
| > | > | (> 5 3) | t |
| <= | <= | (<= 3 3) | t |
| >= | >= | (>= 5 3) | t |
| . (field access) | see setf below | n/a | n/a |
| [] (array access) | aref/aset | (aref [2 4 6] 1) | 4 |
Statements
This section has some recipes for simple Java-like statements. It's not comprehensive – just some recipes to get you going.
if/else
Case 1: no else clause: (if test-expr expr)
Example:
(if (>= 3 2)
(message "hello there"))
Case 2: else clause: (if test-expr then-expr else-expr)
(if (today-is-friday) ; test-expr
(message "yay, friday") ; then-expr
(message "boo, other day")) ; else-expr
If you need multiple expressions (statements) in the then-expr, you wrap them with a call to
progn, which is like curly-braces in C or Java:(if (zerop 0)
(progn
(do-something)
(do-something-else)
(etc-etc-etc)))
You don't need the
progn around the else-expr – everything after the then-expr is considered to be part of the else-expr. Hence:(if (today-is-friday)
(message "yay, friday")
(message "not friday!")
(non-friday-stuff)
(more-non-friday-stuff))
Case 3: else-if clause: Just nest 'em. Or use
cond (see below).(if 'sunday
(message "sunday!") ; then-expr
(if 'saturday ; else-if
(message "saturday!") ; next then-expr
(message ("weekday!")))) ; final else
Case 4: no else-if, multiple body expressions – use
when:If you don't have an else-clause, then you can use the
when macro, which provides an implicit progn:(when (> 5 1)
(blah)
(blah-blah)
(blah blah blah))
You can also use
unless, which is like when but inverts the sense of the test:(unless (weekend-p)
(message "another day at work")
(get-back-to-work))
switch
Elisp has two versions of the classic
switch statement: cond and case.Elisp does not have a table-lookup optimization for switch, so
cond and case are just syntax for nested if-then-else clauses. However, if you have more than one level of nesting, it looks a lot nicer than if expressions. The syntax is:(cond
(test-1
do-stuff-1)
(test-2
do-stuff-2)
...
(t
do-default-stuff))
The
do-stuff parts can be any number of statements, and don't need to be wrapped with a progn block.Unlike classic
switch, cond can handle any test expression (it just checks them in order), not just numbers. The downside is that it doesn't have any special-casing for numbers, so you have to compare them to something. Here's one that does string compares:(cond
((equal value "foo") ; case #1 – notice it's a function call to `equal' so it's in parens
(message "got foo") ; action 1
(+ 2 2)) ; return value for case 1
((equal value "bar") ; case #2 – also a function call (to `+')
nil) ; return value for case 2
(t ; default case – not a function call, just literal true
'hello)) ; return symbol 'hello
The final
t default clause is optional. The first matching clause is executed, and the result of the entire cond expression is the result of the last expression in the matching clause.The 'cl (Common Lisp) package bundled with Emacs provides
case, which works if you're comparing numbers or symbols, so in a sense it works more like standard switch. Example:(case 12
(5 "five")
(1 "one")
(12 "twelve")
(otherwise
"I only know five, one and twelve.")) ; result: "twelve"
With
case you can use either t or otherwise for the default case, but it must come last.It's cleaner to use
case when you can get away with it, but cond is more general.while
Elisp has a relatively normal
while function: (while test body-forms)Example, which you can evaluate in your
*scratch* buffer:(setq x 10
total 0)
(while (plusp x) ; while x is positive
(incf total x) ; add x to total
(decf x)) ; subtract 1 from x
First we set two global variables,
x=10 and total=0, then run the loop. Then we can evaluate the expression total to see that its value is 55 (the sum of the numbers 1 to 10).break/continue
Lisp has a facility for upward control-flow transfers called
catch/throw. It's very similar to Java or C++ exception handling, albeit possibly somewhat lighter-weight.To do a
break from inside a loop in elisp, you put a (catch 'break ...) outside the loop, and a (throw 'break value) wherever you want to break inside the loop, like so:| Emacs Lisp | Java |
|---|---|
(setq x 0 total 0) | var x = total = 0; |
The symbol
'break is arbitrary, but is probably a nice choice for your readers. If you have nested loops, you might consider 'break-outer and 'break-inner in your catch expressions.You can
(throw 'break nil) if you don't care about the "return value" for the while-loop.To
continue a loop, put a catch expression just inside the loop, at the top. For instance, to sum the numbers from 1 to 99 that are not evenly divisible by 5 (artificially lame example demonstrating use of continue):| Emacs Lisp | Java |
|---|---|
(setq x 0 total 0) | var x = total = 0; |
We can combine these examples to show using a break and continue in the same loop:
| Emacs Lisp | JavaScript |
|---|---|
(setq x 0 total 0) | var x = total = 0; |
All the loops above compute the value 4000 in the variable
total. There are better ways to compute this result, but I needed something simple to illustrate break and continue.The
catch/throw mechanism can be used across function boundaries, just like exceptions. It's not intended for true exceptions or error conditions – Emacs has another mechanism for that, discussed in the try/catch section below. You should get comfortable using catch/throw for normal jumps and control transfer in your Elisp code.do/while
Pretty much all iteration in Emacs Lisp is easiest using the
loop macro from the Common Lisp package. Just do this to enable loop:(require 'cl) ; get lots of Common Lisp goodies
The
loop macro is a powerful minilanguage with lots of features, and it's worth reading up on. I'll use it in this primer to show you how to do basic looping constructs from other languages.You can do a
do/while like so:(loop do
(setq x (1+ x))
while
(< x 10))
You can have any number of lisp expressions between the
do and while keywords.for
The C-style
for-loop has four components: variable initialization, the loop body, the test, and the increment. You can do all that and more with the loop macro. For instance, this arbitrary JavaScript:// JavaScript
var result = [];
for (var i = 10, j = 0; j <= 10; i--, j += 2) {
result.push(i+j);
}
Could be done with
loop like so:(loop with result = '() ; one-time initialization
for i downfrom 10 ; count i down from 10
for j from 0 by 2 ; count j up from 0 by 2
while (< j 10) ; stop when j >= 10
do
(push (+ i j) result) ; fast-accumulate i+j
finally
return (nreverse result)) ; reverse and return result
It's a bit more verbose, but
loop has a lot of options, so you want it to be reasonably transparent.Notice that this
loop declares the result array and then "returns" it. It could also operate on a variable declared outside the loop, in which case we wouldn't need the finally return clause.The
loop macro is astoundingly flexible. Its full specification is way out of scope for this primer, but if you want to make Emacs Lisp your, uh, friend, then you should spend some time reading up on loop.for..in
If you're iterating over a collection, Java provides the "smart" for-loop, and JavaScript has
for..in and for each..in. There are various ways to do it in Lisp, but you really might as well just learn how to do it with the loop macro. It's a one-stop shop for iteration.The basic approach is to use
loop for var in sequence, and then do something with the individual results. You can, for instance, collect them (or a function on them) into a result list like so:(loop for i in '(1 2 3 4 5 6)
collect (* i i)) ; yields (1 4 9 16 25 36)
The
loop macro lets you iterate over list elements, list cells, vectors, hash-keys, hash-values, buffers, windows, frames, symbols, and just about anything else you could want to traverse. See the Info pages or your Emacs manual for details.functions
You define a function with
defun.Syntax: (defun function-name arg-list [optional docstring] body)
(defun square (x)
"Return X squared."
(* x x))
For a no-arg function, you use an empty list:
(defun hello ()
"Print the string `hello' to the minibuffer."
(message "hello!"))
The body can be any number of expressions. The return value of the function is the result of the last expression executed. You do not declare the return type, so it's useful to mention it in the documentation string. The doc string is available from
M-x describe-function after you evaluate your function.Emacs Lisp does not have function/method overloading, but it supports optional and "rest" parameters similar to what Python and Ruby offer. You can use the full Common Lisp specification for argument lists, including support for keyword arguments (see the
defstruct section below), if you use the defun* macro instead of defun. The defun* version also lets you (return "foo") without having to set up your own catch/throw.If you want your function to be available as a
M-x command, put (interactive) as the first expression in the body after the doc string.local variables
You declare function local variables with the
let form. The basic syntax is (let var-decl var-decl)
(let ((name1 value1)
(name2 value2)
name3
name4
(name5 value5)
name6
...))
Each
var-decl is either a single name, or (name initial-value). You can mix initialized and uninitialized values in any order. Uninitialized variables get the initial value nil.You can have multiple
let clauses in a function. Code written for performance often collects all declarations into a single let at the top, since it's a bit faster that way. Typically you should write your code for clarity first.reference parameters
C++ has reference parameters, which allow you to modify variables from the caller's stack. Java does not, so you have to work around it occasionally by passing in a 1-element array, or using an instance variable, or whatever.
Emacs Lisp does not have true reference parameters, but it has dynamic scope, which means you can modify values on your caller's stack anyway. Consider the following pair of functions:
(defun foo ()
(let ((x 6)) ; define a local (i.e., stack) variable x initialized to 6
(bar) ; call bar
x)) ; return x
(defun bar ()
(setq x 7)) ; finds and modifies x in the caller's stack frame
If you invoke
(foo) the return value is 7.Dynamic scoping is generally considered a bad design bordering on evil, but it can occasionally come in handy. If nothing else, it's good to know it's what Emacs does.
return
A lisp function by default returns the value of the last expression executed in the function. Sometimes it's possible to structure your function so that every possible return value is in a "tail position" (meaning the last expression out before the door closes, so to speak.) For instance:
| Emacs Lisp | JavaScript |
|---|---|
(require 'calendar) | function dayName() { |
The return value is just the result of the last expression, so whatever our nested
if produces is automatically returned, and there's no need here for an explicit return form.However, sometimes restructuring the function this way is inconvenient, and you'd prefer to do an "early return".
You can do early returns in Emacs Lisp the same way you do
break and continue, using the catch/throw facility. Usually simple functions can be structured so you don't need this – it's most often useful for larger, deeply-nested functions. So for a contrived example, we'll just rewrite the function above to be closer to the JavaScript version:(defun day-name ()
(let ((date (calendar-day-of-week
(calendar-current-date)))) ; 0-6
(catch 'return
(case date
(0
(throw 'return "Sunday"))
(6
(throw 'return "Saturday"))
(t
(throw 'return "weekday"))))))
Obviously using
catch/throw here is slow and clunky compared to the alternatives, but sometimes it's exactly what you need to get out of a deeply nested construct.try/catch
We've already discussed
catch/throw, an exception-like facility for normal control flow transfers.Emacs has a different facility for real error conditions, called the "conditions" system. Going through the full system is out of scope for our primer, but I'll cover how to catch all exceptions and how to ignore (squelch) them.
Here's an example of a universal try/catch using the
condition-case construct, with a Java equivalent:| Emacs Lisp | Java |
|---|---|
(condition-case nil | try { |
If you want an empty catch block (just squelch the error), you can use
ignore-errors:(ignore-errors
(do-something)
(do-something-else))
It's sometimes a good idea to slap an
ignore-errors around bits of elisp code in your startup file that may not always work, so you can still at least start your Emacs up if the code is failing.The
condition-case nil means "Don't assign the error to a named variable." Elisp lets you catch different kinds of errors and examine the error data. You can read the Emacs manual or Info pages to learn more about how to do that.The
progn is necessary if you have multiple expressions (in C/Java, statements) to evaluate in the condition-case body.condition-case will not catch values thrown by throw – the two systems are independent.try/finally
Emacs has a "finally"-like facility called
unwind-protect.| Emacs Lisp | Java |
|---|---|
(unwind-protect | try { |
Like
condition-case, unwind-protect takes a single body-form followed by one or more cleanup forms, so you need to use progn if you have more than one expression in the body.try/catch/finally
If you make the
condition-case (which is basically try/catch) the body-form of an unwind-protect (which is basically try/finally), you get the effect of try/catch/finally:(unwind-protect ; finally
(condition-case nil ; try
(progn ; {
(do-something) ; body-1
(do-something-else)) ; body-2 }
(error ; catch
(message "oh no!") ; { catch 1
(poop-pants))) ; catch 2 }
(first-finally-expr) ; { finally 1
(second-finally-expr)) ; finally 2 }
Classes
Emacs Lisp is not object-oriented in the standard sense: it doesn't have classes, inheritance, polymorphism and so on. The Common Lisp package includes a useful feature calleddefstruct that gives you some simple OOP-like support. I'll walk through a basic example.These two declarations are essentially equivalent:
| Emacs Lisp | Java |
|---|---|
(require 'cl) ; top of file | /* A Person class */ |
Both create a "class" with three named fields, and constructors for initializing any subset of the fields. With
defstruct you get one constructor with keyword parameters, so these are all valid:(make-person) ; new Person()
(make-person :age 39) ; new Person(39)
(make-person :name "Steve" :height 5.83 :age 39) ; new Person("Steve", 39, 5.83)
The
defstruct macro supports single-inheritance (to arbitrary depth):| Emacs Lisp | Java |
|---|---|
(defstruct (employee | /* An Employee class */ |
The
defstruct macro provides a flexible default constructor, but also gives you a fair amount of control over your constructor(s) if you prefer.The
defstruct macro creates an instanceof-like predicate function named after the struct, so you can say:(person-p (make-person))Java may suck at declaring constructors, but Emacs Lisp makes up for it by sucking at setting fields. To set a field in a struct, you have to use the
t
(employee-p (make-person))
nil
(employee-p (make-employee))
t
(person-p (make-employee)) ; yes, it inherits from person
t
setf function, and construct the field name by prepending the structure name. So:| Emacs Lisp | Java |
|---|---|
(setq e (make-employee)) | Employee e = new Employee(); |
The Lisp one doesn't look too bad here, but in practice (because Elisp has no namespace support and no
with-slots macro), you wind up with long structure and field names. So your defstruct-enabled elisp code tends to look more like this:(setf (js2-compiler-data-current-script-or-function compiler-data) current-scriptSo it goes.
(js2-compiler-data-line-number compiler-data) current-line
(js2-compiler-data-allow-member-expr-as-function-name compiler-data) allow
(js2-compiler-data-language-version compiler-data) language-version)
To fetch the value of a field in a struct variable, you concatenate the struct name with the field name and use it as a function call:
(person-name steve) ; yields "Steve"There's more that
defstruct can do – it's a pretty decent facility, all things considered, though it falls well short of a full object system.Buffers as classes
In Elisp programming it can often be useful to think of buffers as instances of your own classes. This is because Emacs supports the notion of buffer-local variables: variables that automatically become buffer-local whenever they are set in any fashion. They become part of the scope chain for any code executing in the buffer, so they act a lot like encapsulated instance variables.
You can use the function
make-variable-buffer-local to declare a variable as buffer-local. Usually it comes right after the defvar or defconst declaration (see below.)Variables
You can declare a variable, optionally giving it some runtime documentation, withdefvar or defconst:(defconst pi 3.14159 "A gross approximation of pi.")The syntax is
(defvar name value [ doc-string ]).Ironically,
defconst is variable and defvar is constant, at least if you re-evaluate them. To change the value of a defvar variable by re-evaluating its declaration you need to use makunbound to unbind it first. You can always change the value of any defvar or defconst variable using setq. The only difference between the two is that defconst makes it clearer to the programmer that the value is not intended to change.You can use
setq to create brand-new variables, but if you use defvar, the byte-compiler will be able to catch more typos.Further reading
Emacs Lisp is a real programming language. It has a compiler, a debugger, a profiler, pretty-printers, runtime documentation, libraries, I/O, networking, process control and much more. There's a lot to learn, but I'm hoping this little primer has got you over the hump, as it were.In spite of its various quirks and annoyances, Elisp is reasonably fun to program in once you get the hang of it. As a language it's not that great, and everyone wishes it were Common Lisp or Scheme or some other reasonable Lisp dialect. Some people even wish it weren't Lisp at all, if you can believe that! (hee)
But it's really, really useful to be able to customize your editor, and also to be able to fix problems with elisp code you borrowed or inherited. So a little Elisp goes a long way.
For those of you learning Emacs Lisp, please let me know if you found this useful. If you try writing some Emacs extensions, let me know what you would like to see documented next; I can always do another installment of the Emergency Elisp series if there's enough interest.
Good luck!