26 February 2008

Somewhere a cardiologist got their wings

Last week a group of us had dinner and tasty if a bit snooty beers at Brasserie V.  One appetizer was almond-stuffed dates wrapped in bacon.  These were so good that it was decided the recipe should be reproduced at this week's Geeks' Night In.  So, along with the evening's bottling and brewing of beer, dates were stuffed, wrapped in bacon, skewered, lightly sprinkled with brown sugar and broiled.

Our version was also delicious.  My brilliant innovation came when I spied the loaf of French bread looking lonely on the table.  I ripped a hunk off, and dipped it into the date-flavored bacon fat left in the broiling pan.  Everyone else was appalled — including, I hasten to add, Chuck. Until they tried it.

17 February 2008

I should be studying...

Instead I did this:



I blame Nicholas' recent posts.

I had brief anxieties when the wind and the cold really started.  Branches were falling off neighbors' trees and my working area was right under some suspicious branches.  I was ready to bolt at the first hint of a crack above me.

14 February 2008

It's the little things: Lisp + Unicode

This little snippet of code makes me giddy out of proportion to what it accomplishes:

 ;; A non-final sigma cannot occur at the end of the string.
(when (char= (elt uni (1- (length uni))) #\σ)
(setf (elt uni (1- (length uni))) #\ς))


That this is so easy is a lucky accident of Common Lisp's history. The "common" in Common Lisp is because it was supposed to unite several popular (hey, it was the 80s), but incompatible, Lisp variants. There were lots more kinds of computers in wide use in the 80s, and many different encoding schemes for text. Thus, Common Lisp strings were always arrays of characters which were not necessarily bytes, or even ASCII. EBCDIC, anyone?

Well, the betacode to unicode conversion library works. Next up, indexing Perseus' exotic XML texts.

04 February 2008

Weekend Puzzlements

1) If the system name of the DTD is a 404-ed URL is your XML document valid?

2) What's the point of a DTD, anyway, if you're just going to obliterate it when you think up something cleverer?

3) Anyone who thinks the modern world is "too coldly rational" isn't paying attention.

30 January 2008

Answering him she spoke...

I recently read the BMCR review of A. Kelly's A Referential Commentary and Lexicon to Homer, Iliad VIII. It seemed very interesting so I was delighted to find it in our library. The review will give more details, but basically he presents the text of Iliad VIII on one page and on the facing page gives the title and number indexed to the referential lexicon for every phrase of interest. The entries in that lexicon may be specific phrases, like κέκλυτέ μοι or more general thematic matters — a chariot journey, say.

For each of these entries he has checked the rest of the Iliad for similar words, phrases and scenes, seeking out narrative similarities. The results are really fascinating. For example, the phrase κέκλυτέ μοι is in every case used by someone under a delusion: "[s]peeches so introduced are allotted to figures of particular authority, and contain proposals which are not usually carried out (a narrative disjunction being the result when they are not) and reveal the speaker's delusion" (p. 76). Or οὐδ’ ἀπιθήσεν "denotes acceptance of a command or suggestion (usually from a previous speech), connoting that its substance is then played out in the course of the narrative in the manner forseen by the character giving the command. The command is usually successful." (p. 54). Or again, ἰθὺς μεμαῶτος ("straight eager") "accompanies the onset of a character about to be defeated."

So not only is Homer helping to keep the audience straight with the usual pragmatic tools available to Greek — like those flourishing particles — but the formulaic language itself has a sort of, well, narrative semantics I guess you might call it.

Appendix A is devoted to three speech introduction formulae. He hunts down every scene in the Iliad where two or more of these are used. According to him τὸν δ’ ἠμείβετ’ ἔπειτα indicates emotional perturbation, τὸν δ’ αὖτε προσέειπεν is used when the speaker "will or wants to align himself in a co-operative relationship with the first speaker," and the (similar to the first) τὸν δ’ ἀπαμειβόμενος προσέφη "represents a relatively greater determination on the part of the speaker to impose his or her will upon the narrative."

Considering the size of Iliad VIII, the commentary is substantial — 515 pages total for the book. I've barely begun to stare closely at all the comparative passages Kelly mentions, and I suspect some of his comments hang of very thin threads indeed. But I've have been paying closer attention to the speech introductions in the Odyssey books we're reading in class. So far Appendix A seems rock solid.

Arc, or, Láadan for Programmers

Paul Graham, six years after announcing it, has released arc, his new dialect of lisp.

One of the odder corners of my library — for most people at least — will be the section that has all the books on constructed languages. Of course there's Esperanto, but Klingon is represented along with several works on Tolkien's languages. I also have the second edition of A First Dictionary and Grammar of Láadan by Suzette Haden Elgin (neatly abbreviated SHE). SHE is a linguist by training, but is also a science fiction writer. She created Láadan not only for a series of books, but as an experiment to see if a language designed specifically to represent the views of women could change society, sort of an informal test of the Sapir-Whorf hypothesis. Láadan is thus presented as representing women's views better somehow. I've never really been convinced that it does so — I know more gay men who have learned the language than women — but there is no doubt it does represent the viewpoint of one extremely intelligent woman.

Arc is just Láadanified lisp. It represents the particular views of one particular lisp programmer. He may be aiming at a hundred year language, but all I can see is perfectly conventional lisp with a few common functions spelled differently and a few parentheses eccentrically deleted.

This was my first warning sign:

It's not for everyone. In fact, Arc embodies just about every form of political incorrectness possible in a programming language.


Whatever one's feelings about speech codes, I think it's safe to say that any time someone warns you, or brags, that they're about to be politically incorrect you're almost certainly in for some first class assholism or lunacy. I've not previously seen it used in a programming language context, but it seems to hold here, too.

At long last, Graham's vaporous Microsofting of lisp is over. I need to prepare some Homer (I'm taking a class again this semester), but I think I'll spend some time this evening refining my Common Lisp betacode to unicode conversion library, and maybe play with Hunchentoot some more.

20 January 2008

A new convert to the LOOP facility

Some Common Lisp programmers hate the LOOP facility, some don't.  I used to fall into the first camp, for various reasons, the most important of which is that LOOP is effectively a specialized looping language grafted onto lisp.  Normally I'm a big fan of a single syntactic mode for all corners of a language (like the lisp family, or Smalltalk, quite unlike C or, god help us, perl).

I've been working on some basic forecasting and time series code recently, and I have to say, when you're looping over different time series and smoothing windows, LOOP results in neater code than almost any language I can think of. Here's a simple moving average forecast (in the interest of space, all examples have my anal-retentive sanity checking assertions removed):

(defmethod single-moving-average ((data sequence) (order integer))
(let ((n (length data)))
(/ (reduce #'+ data :start (- n order))
order)))


For such simple sums, the functional style reduce does the job. Once you get to a weighted moving average the math starts to get tricker. As I was thinking about the many, many traversals of sequences I'd be doing, I decided to check out LOOP more seriously by reading a chapter from Seibel's book I had previously skipped, 22. LOOP for Black Belts. I started to develop warm feelings for LOOP immediately. For starters, it does a great job of encapsulating the various sorts of set-up and tear-down you have to do when rolling your own loop so the mechanical bits for doing loops don't infest the rest of your code.

One really lovely touch makes it easy to avoid the off-by-one error — and fussing about — that comes when you use zero-indexed arrays. The FOR clause may indicate exclusive or inclusive bounds, with TO n including n, and BELOW n going up to but not including it. So here's a simple weighted moving average function:

(defmethod weighted-moving-average ((data sequence) (order integer))
(let ((n (length data)))
(/ (loop for i from 0 below n
sum (* (elt data i) (+ i 1)))
(/ (* n (+ n 1)) 2.0))))


Now I didn't really have to use LOOP for this, but the code I think is somewhat cleaner. The SUM clause accumulates by summing successive values of the expression after it, and in this simple LOOP clause that final sum will be the value of the expression.

My biggest example of LOOP-fu this weekend is a weighted moving average smoothing function. It takes a sequence of data and a sequence of weights and spits out a vector of the smoothed data. In this implementation I simply take the original values at the edges of the data where the smoothing sequence is longer than available values. What I need to do at each step is apply the weight vector to a window of data to compute the moving average for that step. This brings out the other really lovely feature of LOOP: parallel loop values. Here's the scary result, somewhat un-lisp-like to my eyes, but clearer I suspect than I'd be able to produce with functional style tools and DO:

(defmethod weighted-average ((data sequence) (weights sequence))
(let ((d-n (length data))
(w-n (length weights)))
(loop with smoothed = (make-array (list d-n))
with start = (- w-n 1)
with end = (- d-n w-n)
with denom = (reduce #'+ weights)
for i from 0 below d-n
if (or (< i start) (> i end))
do (setf (aref smoothed i) (elt data i))
else
do (setf (aref smoothed i)
(/ (loop for j from 0 below w-n
for dj from (- i start) to i
summing (* (elt weights j) (elt data dj)))
denom))
finally (return smoothed))))


A LOOP within a LOOP! The underlined section shows the parallel loop indices, j going over the weights sequence and dj going over the current window on the data. In the outer LOOP I went a bit crazy and used a lot of its abilities — initializing temporary variables, LOOP conditionals, a FINALLY clause — with the results that look like an Algol-Lisp chimera.

If I were a code purist weighted-average would probably make me crazy. Good thing I'm not.