9 Tips for the aspiring Emacs playboy

December 26th, 2007

Introduction

Man fondled by three women who love his Emacs

I consider myself a beginning Lisper. I’ve been developing my software in Emacs for 8 months now. At first, I was clumsy at it. Emacs can be difficult and daunting. The terminology is different from what I’m used to, the key bindings are different, and there are just so many commands, configurations, and modes. But I’ve persevered and I now find myself quite nimble with key bindings and structured editing. And yet there’s still more to learn.

The subject of what to learn is treated in many online tutorials and printed books. But here, now, I thought I’d share some tips I use to keep the key bindings in my head from being garbage collected.

1. Post up a cheat sheet

Create your own cheat sheet with ten key bindings you’d like to learn. No more than 10. Don’t burden yourself with a gigantic list you’ll have to search through. You won’t be able to find them quickly enough, and you’ll stop referring to the sheet.

Make it a point to use bindings from the cheat sheet while you code.

At first, your cheat sheet will have the basic commands you need to master. But it will evolve as you do . . .

2. Don’t use the mouseNo Mouse

No Mouse GraphicThe mouse is a crutch for the beginner. It will keep you from learning keyboard commands. Using the keyboard will let you edit faster than the mouse ever can.

Get rid of the tool bar (tool-bar-mode -1) and the menu bar (menu-bar-mode -1).

Don’t touch the mouse. If you find yourself using the mouse too much, open up emacs in a terminal instead of in X. When you find yourself reaching for the mouse, think of a way to use the keyboard to get to where you want. In no time, you’ll be flying around the text like a crazy rocket-powered hamster on steroids instead of some wimpy mouse.

3. Read other people’s .emacs file

Lots of people post their .emacs file for bragging/demonstration purposes. These often have great tips/insights into a configuration/mode that you didn’t know about. This is like getting a look directly into the brain of an experienced Emacs user. Cut and paste and read the comments.

4. Write down any interesting commands you discover

Woman writing emacs cheat sheetWhile perusing help, reading .emacs files, loitering in the newsgroups, or surfing the web, you’ll invariably find an Emacs command that you’ll want to remember. You won’t remember it unless you write it down. Sure, you may remember some, but not all. Keep a list of key bindings you don’t want to forget. You will not be able to find it again.

5. Update your cheat sheet

Ok, so you’ve got a cheat sheet. Now what? After a week or two of using a single, ten item cheat sheet, go through the sheet and decide which key bindings you’ve graduated from.

Woman admiring a man’s cheat sheetFor each item on you cheat sheet, ask yourself these questions:

Do you use it often enough not to forget it?

Do you want to forget it?

Will you remember it without the cheat sheet?

Do you remember what it does?

When you’ve whittled down your sheet so that it again contains only those key bindings that you want to remember but don’t, add some more from the list of interesting commands you’ve been keeping. But don’t exceed a total list size of ten!

6. Keep your key bindings standard

There are a lot of key bindings that are universally accepted as standard. When you’re still learning, you won’t know which those are, and you’ll destroy any chance you have of fluency in another Emacs window if you change them. There’s nothing worse than sitting down at a terminal and being totally lost with the commands because you’re used to rebinding most of them. You might even do something awful.

It’s like being parachuted into a foreign country where “Hello, I come in peace.” means “Your mother is ugly but she’s good in bed.” But at the same time, you might use some commands so much you want to rebind them to something more useful. It’s a dilemma but I suggest keeping as many of the original key bindings as possible, especially at first.

7. Use help

Online help is a great help (wow, really bad pun). Some help commands that you might find helpful (not again):

  • describe-key (C-h k): Asks you to strike a keystroke and describes the command it is bound to.
  • describe-bindings (C-h b): Lists all of you key bindings.
  • command-apropos (C-h a): Search all of the commands in the system, and gives you a brief description of each matching command(with its key binding).
  • view-order-manuals (C-h RET): View all help commands

Also, watch when you you type a command using M-x. If there is a key binding for it, it will tell it to you.

8. Experiment

Woman with laptop receives praise from manIn order to strengthen the human-Emacs symbiotic bond, you should experiment with different editing commands and editing modes. Most text editors only offer a couple of ways of navigating text. Here are some common ones: Arrow Keys, Home/End keys, PageUp/PageDown, and find. One of the strengths of Emacs is that you can treat the same text differently depending on what command you use. For instance, you treat it as characters with C-f and C-b. But you treat it as words with M-f and M-b. You treat it as s-exprs with C-M-f and C-M-b. Etc.

In order to know when it is best to use which command, experimentation is your best option. Try out different commands and learn their strengths and weaknesses.

9. Create keyboard macros

Keyboard macros are a great thing to learn in their own right, but they can help you learn commands like a pro. When recording your macros, you have to think: “How can I do this so that this command will work perfectly a hundred times?”. The thought process you have to go through to compose those commands will reinforce all of the possible ways to perform an action.

Popularity: 13% [?]

NOLISP Meeting

December 22nd, 2007

Man holding sign about meetingEveryone’s invited to the next meeting of the New Orleans Lisp Group, also known as NOLISP.

When: 7:30 pm Friday, December 28, 2007

Where: Lafitte’s Cafe, 6325 Elysian Fields Ave. near UNO

They have good sandwiches, salads, and chicken wings. Don’t forget your laptops — they have WiFi.  We’ll also hear a presentation about setting up and using Emacs with Slime.

Related Posts

NOLISP Meeting

Popularity: 6% [?]

My two cents

December 19th, 2007

Common Lisp is the most fun programming language I’ve ever programmed in.And I’ve programmed in a few:

  • C
  • Java
  • Python
  • Perl
  • BASIC
  • Haskell
  • Pascal
  • C#

I like Lisp. I think it has a lot to offer. I also think it has a lot of room to grow — so much potential!

People criticize Lisp a lot, especially those who haven’t used it. I also hear a lot of people getting defensive about Lisp, even when someone is trying to give constructive feedback. But I think that we in the Common Lisp community can boost the power and scope of Common Lisp through incremental improvement.

Common Lisp was one of the first high-level languages. Many smart people worked on it throughout its lifetime. It’s no wonder it has been an inspiration for many popular and well-loved programming languages:

  • Smalltalk
  • C
  • Java
  • Perl
  • Python
  • Ruby
  • Haskell
  • and many others . . .

But somehow, a large portion of the community focuses on the fact that other languages are more popular/get more attention. Let’s not focus on such a waste of time. Let’s use the brilliant minds (many of whom were very influential in the history of Lisp) available in the community to actively discover what’s so great about Lisp. And using those discoveries, we can forge new paths down which other languages will eventually follow.

I would like to start with a request for comments.

I’m looking for these things:

  • great features of Lisp — no matter how common, uncommon, big, or small
  • stories that showcase what Lisp has to offer — the stories can be as specific or as general as you like

Popularity: 4% [?]

My XO

December 18th, 2007

I just got my XO on Saturday.

It’s pretty neat.  It’s the lightest computer I’ve ever held.

More about it later . . .

Popularity: 4% [?]

A Clarification

December 18th, 2007

Nikodemus Siivola writes:

> Huh? #’ gets in the way and #L does not?

No, they both get in the way. Thanks for pointing out my omission.

> How is the CURRY klunky?

I wasn’t clear. When compared to Haskell, where you can write

fun 3

then

(curry #'fun 3)

is more klunky. Especially when currying and composing multiple times, as in this function which computes the sum of squares of a list of numbers:

(compose (curry #'reduce #'+) (curry #'mapcar #'square))

I would just much rather see something shorter. In Haskell (using the same function names):

(reduce +) . (mapcar square)

This was a failed attempt at clarity and brevity:

#M(#R(reduce #'+) #R(mapcar #'square))

I wish the syntax were better, still.

>Finally, I really don’t understand why to use a reader-macro for this when regular macros work for similar purposes more then fine.

Now that you mention it, reader macros do seem a bit overkill.

>Use macros when fuctions won’t do, and use reader-macros when macro’s won’t do is a good rule of the thumb in my books.

Yes, very good, it seems.

>(defmacro <- (&body body) `(lambda (_) ,@body))

> …with the added benefit that the user can macroexpand the form for instant comprehansion.

Yes, that’s good. I hadn’t thought of that. Thanks for the idea.

> I just so do not get this.

Sorry for the confusion.

Popularity: 3% [?]

Toolchest: Shortcuts for higher-order functions

December 17th, 2007

Introduction

There’s a balance that a programmer has to find. A good programmer finds the method of expression that best fits his/her need. When there’s a need for the fastest possible code, the programmer finds it. When there’s a need for readable code, he/she will make forgo clever solutions and opt for clarity. When power or expressiveness is required, he/she will use the tools at his/her disposal in harmonious combination.

To know this balance, one must inventory the different methods available. One must know each method’s strengths and weaknesses. And one must be able to see the problem in many ways.

I’m quite interested in the study of the art of programming. I know I try to better myself continually. I often try out different ways of expressing the same functions.

I would like to present some higher-order functions in different representations and analyse the pros and cons of using each type of form. There are some macros used below. I’ve stored the code for them. This post was inspired by (code) vs ‘(data).

(mapcar #'(lambda (x)
		  (* 2 x))
        '(1 2 3))

Plus

  • This is the standard, canonical form for representing one-use functions. Most everyone will understand it.
  • It can represent any kind of function definable, except recursive functions.

Minus

  • It is long-winded for what it does.
  • With the same amount of writing, you might as well do a defun
(loop for x in '(1 2 3)
      collect (* 2 x))

Plus

  • Also a standard. Everyone will understand it.
  • More succinct and direct than the map-lambda.

Minus

  • It neglects the higher-order possibilities.
  • It could be more simple and direct.

Interesting

  • Since this is a simple example, it doesn’t demonstrate the power of the loop macro.
(mapcar #L(* 2 _) '(1 2 3))

Plus

  • Shorter and more succinct.
  • Can work on any form, even a macro. #L(if (evenp _) _ (* 2 _))
  • Most lambdas are single-argument (think find-if, remove-if, mapcar of one list, etc)
  • Recommended by Peter Norvig

Minus

  • It’s not widely used, so it might be hard for someone to understand at first glance.
  • Can only be used for functions of one argument.

Interesting

  • Could someone write one that could figure out _1 _2 . . . _N variables?
(defun double (x)
  (* 2 x))

(mapcar #'double '(1 2 3))

Plus

  • The DEFUN of course will be away from the MAPCAR form — the MAPCAR form is nice and clean.
  • DOUBLE can be used elsewhere.
  • Very readable

Minus

  • Very long for one-off functions.
  • Need a #’ or at minimum ‘

Interesting

  • This is the kind of thing that results from refactoring code.
(mapcar (curry #'* 2) '(1 2 3))

Plus

  • CURRY expresses this function perfectly.

Minus

  • Not very common, so it’s not readable by all.
  • #’ syntax gets in the way.
  • I wouldn’t use it since the form is a bit clunky.

Interesting

  • Curry is a mathematical idea.
;; Where #R expands to a CURRY form.
(mapcar #R(* 2) '(1 2 3))

Plus

  • No #’.
  • (* 2) looks like a function call
  • Very short
  • Composable with COMPOSE macro below
  • I would use this

Minus

  • Not widely used — it makes it harder for others to understand it at first glance.

I would also like to analyze

(mapcar #'(lambda (x y)
            (abs (/ x y)))
        '(1 -2 3 -4)
        '(2 -9 -8 2))

Compared to

(mapcar (compose #'abs #'/)
        '(1 -2 3 -4)
        '(2 -9 -8 2))

Plus

  • COMPOSE expresses this function perfectly. Function composition is precisely what you want.

Minus

  • Not very common, so it’s not readable by all at first glance.
  • #’ syntax gets in the way.
  • I wouldn’t use this, either

Interesting

  • Function composition is also a mathematical idea.
;; #M expands to compose form
(mapcar #M(abs /)
        '(1 -2 3 -4)
        '(2 -9 -8 2))

Plus

  • COMPOSE expresses this function perfectly.
  • Smaller and simpler than the full COMPOSE form
  • No #’
  • I would use this.
  • Composabe with CURRY macro above

Minus

  • Not very common, so it’s not readable by all.

Related Posts

My two cents

My XO

Popularity: 4% [?]

Human-Order Sorting

December 13th, 2007

Have you ever wanted to more intelligently sort strings that contain digits?

My motivation

Coding Horror had an article about what it calls “Natural” sort order. The article talks about sorting strings that contain digits. He calls it “natural” sort order, but I think that’s misleading (as I explain later). I think “human” ordering is the best way to describe it. In ASCII ordering, digits are just ordinary characters like “A” and “%”. They don’t mean anything special. But they are special to humans. When we see digits, we think numbers. And numbers have their own ordering.

When sorting character strings, you start at the beginning and keep comparing until the end.

You can stop comparing at the caret.

Redundant
Rebound
  ^ b < d means Rebound < Redundant

You don’t care about the rest of the string, so length doesn’t always matter. If you did the same thing for integers, it would look like this.

989898832
9899
   ^ 8 < 9 means 989898832 < 9899

That’s wrong. You can compare integers like strings, but they need special treatment. Here’s one way:

989898832
000009899
^ 0 < 9 means 9899 < 989898832

But an easier way is to parse them as integers and compare them directly.

Background

The Python code you can find in the Coding Horror article is simple and succinct. Being one to favor Lisp over Python, I wanted to see what I could come up with in Common Lisp and what I would find on the way there. I will use my PCOND library. It will come in handy for stripping apart strings that contain digits and non-digits.

Introduction

The code you’ll read here is written to be read sequentially. I find that it makes it easier to follow the thread of development. Normally, however, you might want you tests separate from your code. Imagine, then, that this is all being typed at the REPL as you read it.

Code demonstration

Let’s start with some example test data:

CL-USER> (defvar list-of-strings (list
           “abc1″ “abc10″ “abc11″ “abc2″
           “43bf”
           “hf34fd” “hf340fd”
           “ABC9″))

LIST-OF-STRINGS
  • We have four strings that will need to be sorted according to numbers mixed with letters.
  • We have a string that starts with a number.
  • We have two strings with a number between letters.
  • We have an uppercase string.

Normally, to sort strings, you’d do this (note: SORT is destructive, so we copy the list):

CL-USER> (sort (copy-list list-of-strings) #’string<)
(”43bf” “ABC9″ “abc1″ “abc10″ “abc11″ “abc2″ “hf340fd” “hf34fd”)

This is obviously not the result we want. Let’s analyse it a bit.

The SORT function takes two required arguments: the sequence to sort, and an ordering predicate.

STRING< sorts strings by comparing characters in the string using CHAR<. This sort is case sensitive.

We can make it case insensitive by using STRING-LESSP:

CL-USER> (sort (copy-list list-of-strings) #’string-lessp)
(”43bf” “abc1″ “abc10″ “abc11″ “abc2″ “ABC9″ “hf340fd” “hf34fd”)

This might be closer to what we want, depending on if we want our filenames sorted in a case-sensitive manner. But we still have not solved the number sorting issue.

Let’s take a closer look at SORT:

SORT takes a keyword parameter called :KEY that is applied to each element of the list to be sorted. The comparison predicate then uses the values returned by KEY to sort on instead of the values in the list.

So we need to find (or write) a combination of PRED and KEY that gives us the results we want.

Here is a LISP-UNIT test that defines exactly what we want:

CL-USER> (lisp-unit:define-test done-test
           (lisp-unit:assert-equal
            '(“43bf” “ABC9″ “abc1″ “abc2″ “abc10″
                  “abc11″ “hf34fd” “hf340fd”)
            (sort (copy-list list-of-strings) #’alpha-num-list<
                  :key #’split-alpha-num)))
DONE-TEST

When DONE-TEST passes, we are done!

Our sorter needs to treat letter sequences differently from digit sequences. So let’s define a function that breaks a string up into a list of strings (non-digit sequences) and integers (parsed from digit sequences).

CL-USER> (lisp-unit:define-test split-alpha-num-test
           (lisp-unit:assert-equal '("a" 1 “b” 2)
                         (split-alpha-num “a1b2″))
           (lisp-unit:assert-equal ‘(”ab” 12 “de” 23)
                         (split-alpha-num “ab12de23″))
           (lisp-unit:assert-equal ‘(1 “asa” 333 “ere.txt”)
                         (split-alpha-num “1asa333ere.txt”)))
SPLIT-ALPHA-NUM-TEST

Let’s write a recursive function, with three cases:

  1. string is empty => nil
  2. string starts with a digit sequence (1 or more) => return the integer represented by the sequence and the application of the function to the rest of the string
  3. string starts with a non-digit sequence (1 or more) => return the sequence and the application of the function to the rest of the string
CL-USER> (defun split-alpha-num (string)
           (pcond:pcond
            ((equal “” string)
             nil)
            ((:re “^(\\d+)(.*)” string ((#’parse-integer num) rest))
             (cons num (split-alpha-num rest)))
            ((:re “^(\\D+)(.*)” string (alpha rest))
             (cons alpha (split-alpha-num rest)))))
SPLIT-ALPHA-NUM

Now our test:

CL-USER> (lisp-unit:run-tests split-alpha-num-test)
SPLIT-ALPHA-NUM-TEST: 3 assertions passed, 0 failed.
; No value

Great! It works.

We still need our function ALPHA-NUM-LIST<

Since it has two parameters, and each can be either nil or non-nil, we have four possibilities. The last possibility has sub-possibilities.

Output of

(alpha-num-list< a b)

alpha-num-list< Spec

This table represents the function we’d like to define.

Let’s write a test and develop each one separately.

CL-USER> (lisp-unit:define-test alpha-num-list<-test00
	   (lisp-unit:assert-false
	    (alpha-num-list< nil nil)))
ALPHA-NUM-LIST<-TEST00
CL-USER> (defun alpha-num-list< (a b)
	   nil)
ALPHA-NUM-LIST<
CL-USER> (lisp-unit:run-tests alpha-num-list<-test00)
ALPHA-NUM-LIST<-TEST00: 1 assertions passed, 0 failed.
; No value

One test down. Now another:

CL-USER> (lisp-unit:define-test alpha-num-list<-test01
	   (lisp-unit:assert-true
	    (alpha-num-list< nil  '(1)))))
ALPHA-NUM-LIST<-TEST01

We’ll see if the test happens to pass.

CL-USER> (lisp-unit:run-tests
    alpha-num-list<-test00
    alpha-num-list<-test01)
ALPHA-NUM-LIST<-TEST00: 1 assertions passed, 0 failed.
ALPHA-NUM-LIST<-TEST01: (ALPHA-NUM-LIST< NIL (LIST ‘(1))) failed:
Expected T but saw NIL
ALPHA-NUM-LIST<-TEST01: 0 assertions passed, 1 failed.
TOTAL: 1 assertions passed, 1 failed, 0 execution errors.
; No value

It doesn’t. So we need to redefine our function.

CL-USER> (defun alpha-num-list< (a b)
	   (if (null b)
	       nil
	       t))
STYLE-WARNING: redefining ALPHA-NUM-LIST< in DEFUN
ALPHA-NUM-LIST<

And now to test it:

CL-USER> (lisp-unit:run-tests
     alpha-num-list<-test00
     alpha-num-list<-test01)
ALPHA-NUM-LIST<-TEST00: 1 assertions passed, 0 failed.
ALPHA-NUM-LIST<-TEST01: 1 assertions passed, 0 failed.
TOTAL: 2 assertions passed, 0 failed, 0 execution errors.
; No value

Two tests down . . .

CL-USER> (lisp-unit:define-test alpha-num-list<-test10
	   (lisp-unit:assert-false
	    (alpha-num-list< '(1) nil)))
ALPHA-NUM-LIST<-TEST10
CL-USER> (lisp-unit:run-tests
      alpha-num-list<-test00
      alpha-num-list<-test01
      alpha-num-list<-test10)
ALPHA-NUM-LIST<-TEST00: 1 assertions passed, 0 failed.
ALPHA-NUM-LIST<-TEST01: 1 assertions passed, 0 failed.
ALPHA-NUM-LIST<-TEST10: 1 assertions passed, 0 failed.
TOTAL: 3 assertions passed, 0 failed, 0 execution errors.
; No value

The tests pass, so no need to change anything.

The last one is more complicated.

We’ve defined the output in the lower right corner of the table in terms of another function. That function is the string/number comparison predicate. I’ve called it ALPHA-NUM<

Here’s the specification of that function

(alpha-num< a b)

alpha-num< Spec

Basically, it’s all about the types. Numbers come before strings. We’re ordering case-sensitively.

I’ll use methods that specialize on the given types. They will be one line functions, so I won’t bother to write tests.

CL-USER> (defmethod alpha-num< ((a string) (b string))
	   (string< a b))
#<STANDARD-METHOD ALPHA-NUM< (STRING STRING) {B1218D9}>
CL-USER> (defmethod alpha-num< ((a number) (b string))
	   t)
#<STANDARD-METHOD ALPHA-NUM< (NUMBER STRING) {B1740F1}>
CL-USER> (defmethod alpha-num< ((a string) (b number))
	   nil)
#<STANDARD-METHOD ALPHA-NUM< (STRING NUMBER) {B1B4489}>
CL-USER> (defmethod alpha-num< ((a number) (b number))
	   (< a b))
#<STANDARD-METHOD ALPHA-NUM< (NUMBER NUMBER) {B202741}>

Notice how we rely on CLOS methods to perform the logic of our function. Very nice.
Let’s get back to our other function.

We can test the two constant squares on the bottom right very easily. Let’s hit those first.

CL-USER> (lisp-unit:define-test alpha-num-list<-testa<b
	   (lisp-unit:assert-true
	    (alpha-num-list< '(1) '("a"))))
ALPHA-NUM-LIST<-TESTA<B
CL-USER> (lisp-unit:run-tests
      alpha-num-list<-test00
      alpha-num-list<-test01
      alpha-num-list<-test10
      alpha-num-list<-testa<b)
ALPHA-NUM-LIST<-TEST00: 1 assertions passed, 0 failed.
ALPHA-NUM-LIST<-TEST01: 1 assertions passed, 0 failed.
ALPHA-NUM-LIST<-TEST10: 1 assertions passed, 0 failed.
ALPHA-NUM-LIST<-TESTA<B: 1 assertions passed, 0 failed.
TOTAL: 4 assertions passed, 0 failed, 0 execution errors.
; No value
CL-USER> (lisp-unit:define-test alpha-num-list<-testb<a
	   (lisp-unit:assert-false
	    (alpha-num-list< '("a") '(1))))
ALPHA-NUM-LIST<-TESTB<A
CL-USER> (lisp-unit:run-tests
    alpha-num-list<-test00
    alpha-num-list<-test01
    alpha-num-list<-test10
    alpha-num-list<-testa<b
    alpha-num-list<-testb<a)
ALPHA-NUM-LIST<-TEST00: 1 assertions passed, 0 failed.
ALPHA-NUM-LIST<-TEST01: 1 assertions passed, 0 failed.
ALPHA-NUM-LIST<-TEST10: 1 assertions passed, 0 failed.
ALPHA-NUM-LIST<-TESTA<B: 1 assertions passed, 0 failed.
ALPHA-NUM-LIST<-TESTB<A: (ALPHA-NUM-LIST< '("a") '(1)) failed:
Expected NIL but saw T
ALPHA-NUM-LIST<-TESTB<A: 0 assertions passed, 1 failed.
TOTAL: 4 assertions passed, 1 failed, 0 execution errors.
; No value

Ah! A failure. Now we can write some code!

CL-USER> (defun alpha-num-list< (a b)
	   (if (null b)
	       nil
	       (if (null a)
		   t
		   (if (alpha-num< (first a) (first b))
		       t
		       (if (alpha-num< (first b) (first a))
			   nil)))))

STYLE-WARNING: redefining ALPHA-NUM-LIST< in DEFUN
ALPHA-NUM-LIST<
CL-USER> (lisp-unit:run-tests
    alpha-num-list<-test00
    alpha-num-list<-test01
    alpha-num-list<-test10
    alpha-num-list<-testa<b
    alpha-num-list<-testb<a)
ALPHA-NUM-LIST<-TEST00: 1 assertions passed, 0 failed.
ALPHA-NUM-LIST<-TEST01: 1 assertions passed, 0 failed.
ALPHA-NUM-LIST<-TEST10: 1 assertions passed, 0 failed.
ALPHA-NUM-LIST<-TESTA<B: 1 assertions passed, 0 failed.
ALPHA-NUM-LIST<-TESTB<A: 1 assertions passed, 0 failed.
TOTAL: 5 assertions passed, 0 failed, 0 execution errors.
; No value

No failures! But are we done? Let’s ask the test.

CL-USER> (lisp-unit:run-tests done-test)
DONE-TEST: (SORT (COPY-LIST LIST-OF-STRINGS) #’ALPHA-NUM-LIST< :KEY

                 #’SPLIT-ALPHA-NUM) failed:
Expected (“43bf” “ABC9″ “abc1″ “abc2″ “abc10″
    “abc11″ “hf34fd” “hf340fd”)
but saw (“43bf” “ABC9″ “abc1″ “abc10″ “abc11″
    “abc2″ “hf34fd” “hf340fd”)
DONE-TEST: 0 assertions passed, 1 failed.
; No value

It failed. Why? We never recurse.

CL-USER> (defun alpha-num-list< (a b)
	   (if (null b)
	       nil
	       (if (null a)
		   t
		   (if (alpha-num< (first a) (first b))
		       t
		       (if (alpha-num< (first b) (first a))
			   nil
			   (alpha-num-list< (rest a) (rest b)))))))

STYLE-WARNING: redefining ALPHA-NUM-LIST< in DEFUN
ALPHA-NUM-LIST<
CL-USER> (lisp-unit:run-tests done-test)
DONE-TEST: 1 assertions passed, 0 failed.
; No value

We’ve got complete functionality. But the code for ALPHA-NUM-LIST< is ugly. Let’s change the nested IF’s to a COND.

CL-USER> (defun alpha-num-list< (a b)
	   (cond
	     ((null b) nil)
	     ((null a) t)
	     ((alpha-num< (first a) (first b)) t)
	     ((alpha-num< (first b) (first a)) nil)
	     (t
	      (alpha-num-list< (rest a) (rest b)))))

STYLE-WARNING: redefining ALPHA-NUM-LIST< in DEFUN
ALPHA-NUM-LIST<

That’s a lot better.

CL-USER> (lisp-unit:run-tests
    alpha-num-list<-test00
    alpha-num-list<-test01
    alpha-num-list<-test10
    alpha-num-list<-testa<b
    alpha-num-list<-testb<a
    done-test)
ALPHA-NUM-LIST<-TEST00: 1 assertions passed, 0 failed.
ALPHA-NUM-LIST<-TEST01: 1 assertions passed, 0 failed.
ALPHA-NUM-LIST<-TEST10: 1 assertions passed, 0 failed.
ALPHA-NUM-LIST<-TESTA<B: 1 assertions passed, 0 failed.
ALPHA-NUM-LIST<-TESTB<A: 1 assertions passed, 0 failed.
DONE-TEST: 1 assertions passed, 0 failed.
TOTAL: 6 assertions passed, 0 failed, 0 execution errors.
; No value

So, now that we’ve got it working, let me propose this change:

CL-USER> (lisp-unit:define-test done-test
           (lisp-unit:assert-equal
            '(“43bf” “ABC9″ “abc1″ “abc2″ “abc10″
    “abc11″ “hf34fd” “hf340fd”)
            (sort (copy-list list-of-strings)
                  (make-list-comp #’alpha-num<)
                  :key #’split-alpha-num)))
DONE-TEST

We’ll modularize it so that we can use any comparison function.

CL-USER> (defun make-list-comp (fun)
	   (labels ((f (a b)
		      (cond
			((null b) nil)
			((null a) t)
			((funcall fun (first a) (first b)) t)
			((funcall fun (first b) (first a)) nil)
			(t
			 (f (rest a) (rest b))))))
	     #’f))
CL-USER> (lisp-unit:run-tests done-test)
DONE-TEST: 1 assertions passed, 0 failed.
; No value

Let’s do the same for our function ALPHA-NUM<. We want to generate a new function, passing in the appropriate string comparison and number comparison function. We never wrote a test for it, so let’s do that now.

CL-USER> (lisp-unit:define-test alpha-num<-test
	   (lisp-unit:assert-true
	    (alpha-num< 1 “a”))
	   (lisp-unit:assert-false
	    (alpha-num< “a” 1))
	   (lisp-unit:assert-true
	    (alpha-num< 1 2))
	   (lisp-unit:assert-false
	    (alpha-num< 2 1))
	   (lisp-unit:assert-true
	    (alpha-num< “a” “b”))
	   (lisp-unit:assert-false
	    (alpha-num< “b” “a”)))
ALPHA-NUM<-TEST
CL-USER> (lisp-unit:run-tests alpha-num<-test)
ALPHA-NUM<-TEST: 6 assertions passed, 0 failed.
; No value

Now we need to combine it back into a function instead of four methods.

CL-USER> (defun alpha-num< (a b)
	   (cond
	     ((and (stringp a)
		   (stringp b))
	      (string< a b))
	     ((and (numberp a)
		   (stringp b))
	      t)
	     ((and (stringp a)
		   (numberp b))
	      nil)
	     ((and (numberp a)
		   (numberp b))
	      (< a b))))
STYLE-WARNING: redefining ALPHA-NUM< in DEFUN
ALPHA-NUM<
CL-USER> (lisp-unit:run-tests alpha-num<-test)
ALPHA-NUM<-TEST: 6 assertions passed, 0 failed.
; No value

Now we can change it.

CL-USER> (lisp-unit:define-test alpha-num<-test
	   (let ((fun (make-alpha-num-comp #’string< #’< t)))
	     (lisp-unit:assert-true
	      (funcall fun 1 “a”))
	     (lisp-unit:assert-false
	      (funcall fun “a” 1))
	     (lisp-unit:assert-true
	      (funcall fun 1 2))
	     (lisp-unit:assert-false
	      (funcall fun 2 1))
	     (lisp-unit:assert-true
	      (funcall fun “a” “b”))
	     (lisp-unit:assert-false
	      (funcall fun “b” “a”))))
ALPHA-NUM<-TEST

Our test will fail now until we define the function.

CL-USER> (defun make-alpha-num-comp (string-comp
           num-comp
           nums-before-strings-p)
	   #’(lambda (a b)
	       (cond
		 ((and (stringp a)
		       (stringp b))
		  (funcall string-comp a b))
		 ((and (numberp a)
		       (stringp b))
		  nums-before-strings-p)
		 ((and (stringp a)
		       (numberp b))
		  (not nums-before-strings-p))
		 ((and (numberp a)
		       (numberp b))
		  (funcall num-comp a b)))))
MAKE-ALPHA-NUM-COMP
CL-USER> (lisp-unit:run-tests alpha-num<-test)
ALPHA-NUM<-TEST: 6 assertions passed, 0 failed.
; No value

Ok, so what do we have? Not only do we have our predicates for doing the human sort, we also have a new language for combining string and number comparisons to create any combination we want.

Case-sensitive, numbers first, all ascending order.

CL-USER> (sort (copy-list list-of-strings)
	       (make-list-comp
		(make-alpha-num-comp
		 #’string<
		 #’<
		 t))
	       :key #’split-alpha-num)
(”43bf” “ABC9″ “abc1″ “abc2″ “abc10″ “abc11″ “hf34fd” “hf340fd”)

Case-insensitive, numbers last, strings ascending, numbers descending

CL-USER> (sort (copy-list list-of-strings)
	       (make-list-comp
		(make-alpha-num-comp
		 #’string-lessp
		 #’>
		 nil))
	       :key #’split-alpha-num)
(”abc11″ “abc10″ “ABC9″ “abc2″ “abc1″ “hf340fd” “hf34fd” “43bf”)
CL-USER> (lisp-unit:define-test done-test
           (lisp-unit:assert-equal
            '(“43bf” “ABC9″ “abc1″ “abc2″ “abc10″
            “abc11″ “hf34fd” “hf340fd”)
            (sort (copy-list list-of-strings)
		  (make-list-comp
		   (make-alpha-num-comp #’string< #’< t))
                  :key #’split-alpha-num)))
DONE-TEST
CL-USER> (lisp-unit:run-tests done-test)
DONE-TEST: 1 assertions passed, 0 failed.
; No value
CL-USER> (defun human-sort (list)
	   (sort list (make-list-comp
		       (make-alpha-num-comp #’string-lessp #’< t))
		 :key #’split-alpha-num))
HUMAN-SORT

Done.

Conclusion

Although the code here is not as short as the Python version (Python: 4 non-comment lines, CL: 37), it is clear and readable. This is a case where the Python standard libraries and semantics result in a smaller function definition — Python has a default ordering function for lists. I searched around the docs for one in Common Lisp. I didn’t find one, so I developed my own routines. And with just a little more effort, I have a great little library for making an infinite number of orderings for lists.

That’s what’s great about Lisp — the language might not have exactly what you need built in, but you can build it easily and while you’re doing it, you get all sorts of cool functionality.

One thing that I find newer languages (eg. Perl, Python) having is “magic” comparisons.

For instance, in Perl, from what I remember of the language, you can do this:

1 == "1"

That returns a true value. This is great for scraping text documents because you don’t have to convert strings to integers (and I believe the operator returns false when you can’t parse it as an integer). How’s that for a default? It seems to make sense in a lot of contexts. It’s kind of a poor man’s polymorphism. Perl’s defaults are often “human” in the same way the sort order we just developed is “human”.

However, defaults aren’t the answer.  You can’t call an ordering “natural” because that implies that it is THE correct ordering, and that is false. Kent Pitman has a nice paper about expecting magic from your predicates. Its conclusion: that The Right Thing is arbitrary and application specific. Just because Python has a default ordering for lists doesn’t mean you’ll always want that ordering. It could even seem like the default ordering is wrong in a given context.   Therefore, what’s important is the ability to quickly and powerfully create new predicates that fit your context. And Lisp is exceptional at that.

Well, that’s the end! As you mentally digest this post, ask yourself this question:

What does your experience with Common Lisp and other languages tell you about “magic” comparisons?

Then come back and share your thoughts!

Popularity: 3% [?]

Today is Make a Package ASDF-Installable Day!

December 11th, 2007

I hereby proclaim December 11, 2007 to be

Make a Package ASDF-Installable Day!

ASDF-Install is a package management system that fetches packages from cliki.net and installs them on your local machine.

It has two essential features:

  • A single command will download and install a package from the web.
  • It will download dependencies automatically, if they too are ASDF-Installable.

In addition, it will verify PGP signatures of the files downloaded before installing. This provides some safety over what you are downloading.

But not all Common Lisp software uses this system. How can you tell if a package is ASDF-Installable? There are two requirements:

  1. It has a cliki.net page named after the package name.
  2. The cliki.net page has an ASDF-Install compatible link.

The graphic below shows these two things. Click on it to see a larger version.
ASDF-Install link small

Everyone knows of one or two packages that are not ASDF-Installable. And that can get annoying, especially when you want to release your package as ASDF-Installable.

What I propose is that on Tuesday, everyone finds one package that is not ASDF-Installable and makes it so.

Here are the steps:

  1. If it’s your package, go on to step 2. If it’s not your package, get permission from the authors.
    • Email the author of the package and ask if you can help him/her make their package installable.
    • Motivate them by explaining the benefits of ASDF-Install.
    • This is important when working with other people’s creations. People have worked hard on their software, and they have the right to determine how it is distributed.
  2. Read and follow the instructions to create the necessary files.
    • Create an ASDF package definition.
    • Package everything up in a tar.gz archive.
    • Generate a signature.
  3. Upload them to a web server.
  4. Create or edit the cliki page.
    • And put in it an ASDF package directive.

It’s that easy!

Remember, keep the creator in the loop.

When you’ve finished, post a comment about it! Good luck!

Popularity: 3% [?]

Announcement: PCOND

December 10th, 2007

Have you ever wanted to perform pattern matching, bind variables, and do conditional branching all with one handy macro?

Well now you can!

I’ve just released a library called PCOND. I’ve posted about it before. You can read about it on cliki.

Basically, it’s a pattern matching conditional macro.  Imagine COND with unification expressions and regular expression matching.

And a surprise for tomorrow . . .

While I was creating the ASDF package and setting up the ASDF-Install, I thought of something. Two packages that PCOND depends on, CL-UNIFICATION and LISP-UNIT, are not ASDF-Installable.  This basically means that my package is not as simple as just ASDF-Installing my package for the users that don’t already have the dependencies.

So I had an idea . . . which you’ll read about tomorrow!

Popularity: 2% [?]

LispCast Episode 5

December 10th, 2007

A Thanksgiving trip to California, a massive hard drive failure, and the end of the semester rush later . . .

Episode 5 is ready!

Sorry for the delay, but I have a great episode for you now. This time, I add an ability to maintain users. In the last episode, I made the software persist to a database. But there was still no way to identify users. In this episode, you can watch me add a system for maintaining users. Users log in, own their ratings, and can only rate a link once. There’s more to the system, but you’ll have to watch the video to find out.

Watch LispCast Episode 5

Plus, I revised the acceptance tests behind the scenes to use the new database backend. The tests should work for the code from last episode.

Next episode: Revising acceptance tests to fit with the new login semantics.

Source code for the server and for the acceptance tests.

Popularity: 3% [?]