Rcjp’s Weblog

January 31, 2008

Playing with Arc

Filed under: lisp — rcjp @ 12:12 pm

Paul Graham has made available a tarball prerelease of his new arc language. I’m currently running Ubuntu gutsy on my laptop so it needs a quick patch

~/lisp> tar -xvf ~/tmp/arc0.tar 
~/lisp> cd arc0
~/lisp/arc0> wget http://blakeley.com/linux-date.patch
~/lisp/arc0> patch arc.arc linux-date.patch 
~/lisp/arc0> rlwrap -C arc mzscheme -m -f as.scm
Use (quit) to quit, (tl) to return here after an interrupt.
arc> 

then you can go through the tutorial, or play with the webserver, which we’ll start in a new thread

arc> (defop hello req (pr "hello world"))
#<procedure:gs1431>
arc> (= webtest (thread asv))
#<thread: asv>
arc> ready to serve port 8080

and then look at http://localhost:8080/hello

The prompt doesn’t quite come back correctly here, but you can carry on typing

(defop hello req (pr "hello world changed"))
#<procedure:gs1438>

and refresh the browser. Kill the thread and quit with

arc> (break-thread webtest) 
arc> (quit)

Though initially disappointed that there was no native compiler (written in arc of course!), I’ve warmed to the syntax – I didn’t find myself in a fighting the language, or frustrated that I had to reinvent stuff I didn’t want to e.g., here’s how to split up a string using spaces or commas

arc> (tokens "abc def,ghi" [in _ #\ #\,])
("abc" "def" "ghi")

There’s plenty to play with by reading through the source. So far, it feels fun.

October 1, 2007

Simple diff of 2 files

Filed under: c, lisp, python — rcjp @ 7:59 pm

A few days ago I wanted to compare to files each of which had one word per line (they were completion files for the rlwrap utility incidentally) thats easy enough with the unix shell commands, infact you can do it in one line

diff -iyw --suppress-common-lines <(sort -f file1) <(sort -f file2)

but I thought as a quick programming exercise I’d do it in C++/C, python and Common Lisp…

Calculating the difference is very easy using C++’s set_difference etc. but it is a surprise, I think for most programmers anyway, that you have to write your own case insensitive string comparison function. C++ sure is a peculiar mix of high and low level programming.

#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <iterator>
#include <functional>

// 
// not sure there is any advantage to inherit from binary_function here?
// (infact we could just define a function rather than a struct and operator)
//
struct lessthan_nocase :
  public std::binary_function<const std::string&, const std::string&, bool>
{
    bool operator()(const std::string& s1, const std::string& s2) const
    {
        std::string::const_iterator p1 = s1.begin();
        std::string::const_iterator p2 = s2.begin();

        while(p1 != s1.end() && p2 != s2.end()) {
            if (toupper(*p1) != toupper(*p2)) return toupper(*p1) < toupper(*p2);
            ++p1;
            ++p2;
        }
        return s1.size() < s2.size();
    }
};

//
// set_difference only work on sorted containers
//
void sorted_readfile(const char* file, std::vector<std::string>& fvec)
{
    std::ifstream f(file);
    std::istream_iterator<std::string> finput(f), fend;

    copy(finput, fend, back_inserter(fvec));
    sort(fvec.begin(), fvec.end(), lessthan_nocase());
}

//
// dump results (show words if < MAXSHOW)
//
const unsigned int MAXSHOW = 10;

void display_diff(std::string title, std::vector<std::string> v)
{
    std::cout << title;
    if (v.size() < MAXSHOW) {
        std::cout << std::endl << std::string(title.size(), '-') << std::endl;
        copy(v.begin(), v.end(),
             std::ostream_iterator<std::string>(std::cout, "\n"));
    } else {
        std::cout << " =  " << v.size() << " words" << std::endl;
    }
    std::cout << std::endl;
}

int main(int argc, char* argv[])
{
    if (argc != 3) {
        std::cerr << "Usage: tdiff filename1 filename2" << std::endl;
        exit(1);
    }

    std::vector<std::string> f1;
    sorted_readfile(argv[1], f1);

    std::vector<std::string> f2;
    sorted_readfile(argv[2], f2);

    std::vector<std::string> notinf1;
    set_difference(f1.begin(), f1.end(), f2.begin(), f2.end(),
            back_inserter(notinf1), lessthan_nocase());
    display_diff("words in 1st but not in 2nd", notinf1);

    std::vector<std::string> notinf2;
    set_difference(f2.begin(), f2.end(), f1.begin(), f1.end(),
            back_inserter(notinf2), lessthan_nocase());
    display_diff("words in 2nd but not in 1st", notinf2);

    std::vector<std::string> symdiff;
    set_symmetric_difference(f2.begin(), f2.end(), f1.begin(), f1.end(),
            back_inserter(symdiff), lessthan_nocase());
    display_diff("symmetric difference", symdiff);

    std::vector<std::string> inter;
    set_intersection(f2.begin(), f2.end(), f1.begin(), f1.end(),
            back_inserter(inter), lessthan_nocase());
    display_diff("intersection", inter);

    return 0;
}

In C, the only thing to trip you up is getting the casting of the void * pointers in strcmp_nocase correct before trying to dereference them. Also I’ve cheated and used the non-ansi strcasecmp (sometimes called stricmp).

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#define MAXLINE  256
#define MAXWORDS 10000
#define MAXSHOW  10

typedef enum {false, true} bool;

bool strempty(char *str)
{
    char *p = str;
    while (*p) if (!isspace(*p++)) return false;
    return true;
}

int strcmp_nocase(const void *s1, const void *s2)
{
    /* The actual arguments to this function are "pointers to
       pointers to char", but strcmp() arguments are "pointers
       to char", hence the following cast plus dereference */
    return strcasecmp( *(char * const *)s1, * (char * const *) s2);
}

int sorted_readfile(char *filename, char **words)
{
    FILE *f = fopen(filename, "r");

    if (!f) {
        fprintf(stderr, "can't open %s\n", filename);
        exit(1);
    }

    int count = 0;
    char line[MAXLINE], *q;
    while (fgets(line, MAXLINE, f) && (count < MAXWORDS)) {
        if ((q=strpbrk(line,"\r\n"))) *q=0;     /* words dont include newlines */
        if (strempty(line)) continue;           /* skip blank lines */
        words[count] = (char *) malloc(strlen(line)+1);
        /* or words[count++] = strdup(line)*/
        strcpy(words[count], line);
        count++;
    }

    fclose(f);

    if (count == MAXWORDS) {
        fprintf(stderr, "increase MAXWORDS buffer size\n");
        exit(1);
    }
    qsort(words, count, sizeof(char *), strcmp_nocase);
    return count;
}

void display_diff(char *title, char **v, int vsize)
{
    int i;
    printf("%s", title);

    if (vsize < MAXSHOW) {
        printf("\n");
        for (i=0; i<strlen(title); i++) putchar('-');
        printf("\n");
        for (i=0; i<vsize; i++)
            printf("%s\n", v[i]);
    } else {
        printf(" = %d words\n", vsize);
    }
    printf("\n");
}

int main(int argc, char *argv[])
{
    if (argc != 3) {
        fprintf(stderr, "Usage: tdiff filename1 filename2\n");
        exit(1);
    }
    int i;
    char **f1words;
    f1words = malloc(MAXWORDS*sizeof(char*));
    int nf1words = sorted_readfile(argv[1], f1words);

    char **f2words;
    f2words = malloc(MAXWORDS*sizeof(char*));
    int nf2words = sorted_readfile(argv[2], f2words);

    char **diff;
    int ndiff = 0;
    diff = malloc(MAXWORDS*sizeof(char*));
    for (i=0; i < nf1words; i++) {
        if (!bsearch(&f1words[i], f2words, nf2words,
                     sizeof(char*), strcmp_nocase)) {
            /* could just point into f1words instead */
            diff[ndiff++] = strdup(f1words[i]);
        }
    }
    display_diff("words in 1st but not in 2nd", diff, ndiff);

    return 0;
}

In python, most of it is easy, though its perhaps not very pythonesque to cram some things on one line like I’ve done here. Making set case independent requires the __hash__ and __eq__ functions (I think that is all we need in this case) get overridden to use a saved lowercase version of the supplied string (see the python reference) .

import string

class NoCaseStr(str):
    def __init__(self, s):
        str.__init__(self, s)
        # keep a copy of the lower case string
        self.loweredstr = s.lower()

    def __eq__(self, s):
        return self.loweredstr == s.lower()

    def __hash__(self):
        return hash(self.loweredstr)

def display_diff(title, dset, Maxshow=10):
    if len(dset) < Maxshow:
        print title
        print len(title) * '-'
        print '\n'.join(dset)
    else:
        print title, '=', len(dset)
    print

def read_words(f):
    w = set(NoCaseStr(string.strip(word)) for word in open(f).readlines())
    w.discard('')# slightly clumsy... '  \n' gets stripped to '' so discard it
    return w

def tdiff(f1, f2):
    w1 = read_words(f1)
    w2 = read_words(f2)
    display_diff('in 1st but not in 2nd', w1.difference(w2))
    display_diff('in 2nd but not in 1st', w2.difference(w1))
    display_diff('symmetric difference',  w1.symmetric_difference(w2))
    display_diff('intersection',          w1.intersection(w2))

if __name__ == '__main__':
    import sys
    if len(sys.argv) < 3:
        print 'Usage: %s file1 file2' % sys.argv[0]
        sys.exit(1)

    tdiff(sys.argv[1], sys.argv[2])

My favourite language – Common Lisp has everything you need to do with without overriding anything. string-equal is case insensitive (as opposed to string=)

(defun read-words (file)
  (with-open-file (str file)
    (loop for line = (read-line str nil nil)
       for word = (string-trim " " line)
       while line
       unless (string= word "")
       collect word)))

(defun display-diff (title diff &optional (max-show 10))
  (format t "~A: ~V{~A~^ ~}~%" title max-show diff))

(defun tdiff (f1 f2)
  (let ((w1 (read-words f1))
        (w2 (read-words f2)))
    (display-diff "in 1st but not in 2nd" (set-difference w1 w2 :test #'string-equal))
    (display-diff "in 2nd but not in 1st" (set-difference w2 w1 :test #'string-equal))
    (display-diff "intersection" (intersection w1 w2 :test #'string-equal))))

(tdiff "/home/r/tmp/f1" "/home/r/tmp/f2")

January 23, 2007

Symbol-function

Filed under: lisp — rcjp @ 9:36 am

You can make an adder function that changes its closure variable via an optional argument

    (defun make-variable-adder (n)
      (lambda (x &optional change)
        (if change
            (setq n x)
            (+ x n))))

    (setq add2 (make-variable-adder 2))
    (mapcar add2 '(1 2 3)) => (3 4 5)

But then whilst trying to change the closure variable I forgot that (add23 t) won’t work, you need (funcall add2 3 t) since (symbol-function 'add2) hasn’t been defined, only the symbol-value has. The only reason mapcar works is that as the hyperspec says… “If function is a symbol, it is coerced to a function as if by symbol-function.”

January 15, 2007

Swapping Spaces for Punctuation

Filed under: c, lisp, python — rcjp @ 10:17 am

Just a quicky. In Common Lisp…

    (defun spacify-punc (string)
      (substitute-if #\Space #'punc-p string))

    (defun punc-p (char) (find char "*,.:;-()"))

then use it…

    CL-USER> (spacify-punc "this, string. and: more")
    "this  string  and  more"

in python…

    def spacify_punc(char):
        if char not in "*,.:;-()":
            return char
        else:
            return ' '

    print ''.join(spacify_punc(c) for c in "this, string. and: more")

in C possibly something like…

    #include <stdio.h>
    #include <string.h>

    int main()
    {
      char words[] = "this, string. and: more";
      char *answer = strdup(words);
      unsigned int i;

      /* using sizeof (instead of strlen) so we include the NULL */
      for (i=0; i < sizeof(words); i++)
        answer[i] = strchr("*,.:;-()", words[i]) ? ' ': words[i];

      printf("'%s' becomes '%s'\n", words, answer);
      return 0;
    }

All three languages have functions to detect punctuation, but we are only looking for a subset in this case.

I think I like the CL solution best but the order of args to substitute-if always throws me, thank heavens for SLIME’s argument prompting.

December 26, 2006

lotto numbers

Filed under: lisp, python — rcjp @ 9:26 am

I can’t remember how I came to be subscribed to a perl feed but I ended up reading http://www.oreillynet.com/onlamp/blog/2006/12/99_problems_in_perl_6.html
which was based on something similar in Lisp I think. The article showed some Scheme like Common Lisp solution to picking out the lottery numbers all cons’s etc.

My first somewhat crumby solution was

    (defun lotto (n m)
      "Draw N different random numbers from the set 1..M"
      (loop for num = (1+ (random m))
         for bag = nil then (adjoin num bag)
         while (< (length bag) n)
         finally (return bag)))

as I first thought of the adjoin function when I thought about adding numbers, but you shouldn’t be adjoining and then setting the result back to itself – pushnew would do that. Maybe its clearer just to use member

    (defun lotto1 (n m)
      (loop while (< (length bag) n)
         for num = (1+ (random m))
         unless (member num bag) collect num into bag
         finally (return bag)))

I vaguely remembered a thread on comp.lang.lisp on this subject and found a post from Alan Crowe did it prettier with ‘do’ – slapped wrists for me since I always dismiss ‘do’ as being harder to read than loop

    (defun lotto2 (n m)
      (do ((draw '()))
          ((= (length draw) n) draw)
        (pushnew (+ (random m) 1) draw)))

Of course these don’t work well if you want 49 out of 50 as there are too many non-unique numbers generated, Brian Downing posted a version using a nice idea of generating a list of numbers with an associated random number and sorting on that random number to shuffle the list

    (defun lotto3 (n m)
      (subseq (mapcar #'car (sort (loop for i from 1 upto m
                                     collect (cons i (random 1.0)))
                                  #'< :key #'cdr))
              0 n))

In python, as I was looking up the random function, the docs showed how you can do this sort of thing in one line (remember xrange, like range, generates numbers upto but not including the second arg)

    In [16]: random.sample(xrange(1,50),6)
    Out[16]: [29, 28, 32, 45, 42, 2]

December 22, 2006

String Searching/Replacing

Filed under: c, lisp, python — rcjp @ 9:04 am

just some quick notes on how various languages handle replacing/splicing a string…

In C++ chopping, searching and replacing in a string is fairly easy

    #include <iostream>
    #include <string>

    int main()
    {
        std::string s = "some one with more than one that ones.";

        s.erase(0,3);
        s.replace(s.find("one"), 3, "three");

        std::cout << s << std::endl;
    }

in straight C doing the same thing is a bit more fiddly

    #include <stdio.h>
    #include <string.h>

    int main()
    {
        char* s = "some one with more than one that ones.";

        char buf[255];
        strcpy(buf, s+3);  /* chop off the first 3 chars */

        char *p = strstr(buf, "one");
        if (p)
        {
            char tmp[255];
            *p = (char) 0;
            strcpy(tmp, buf);
            strcat(tmp, "three");
            strcat(tmp, p+3);  /* skip over length of "one" */
            strcpy(buf, tmp);
        }

        printf("%s\n", buf);
        return 0;
    }

you have to think about the size of the temporary buffer unless you malloc something based on the size of s – but then you have to know how much bigger your operations will make the string.

Perhaps suprisingly there isn’t any standard function to replace strings in Common Lisp – I guess its one of those things your are supposed to deal with yourself since if you know you your replacement word is the same size you can destructively alter the string (using setf on the subseq), otherwise you have to build a new string (since you may be replacing a word with a bigger word). So CL leaves things to you to figure out the best approach.

You can get the position of a string within another with

    * (search "one" "there one is one more than ones")
    6

and then build up the string a la C…

    (let* ((str "some one with more than one that ones.")
           (word "one")
           (p (search word str)))
      (if p
          (concatenate 'string (subseq str 3 p)
                               "three"
                               (subseq str (+ p (length word))))
          p))

In python

    In [3]: "there one is one more than ones".replace("one", "three", 1)
    Out[3]: 'there three is one more than ones'

where we are using a count=1 otherwise it would replace all instances. We can even chop off the first three chars and then replace all in one go

    In [5]: "there one is one more than ones"[3:].replace("one", "three")
    Out[5]: 're three is three more than threes'

Replacing all occurances in C++ is a bit more work

    std::string::size_type n;  // or   size_t n; 
    std::string word = "one";
    while((n=s.find(word)) != std::string::npos)
        s.replace(n, word.size(), "three");

Replacing all strings in C or Common Lisp is quite alot more work and probably better to write a utility function and keep it somewhere or use a library.
http://en.wikibooks.org/wiki/Programming:Common_Lisp/Strings has

    (defun replace-all (string part replacement &key (test #'char=))
      (with-output-to-string (out)
        (loop with part-length = (length part)
           for old-pos = 0 then (+ pos part-length)
           for pos = (search part string :start2 old-pos :test test)
           do
             (write-string string out :start old-pos
                                      :end (or pos (length string)))
           when pos do
             (write-string replacement out)
           while pos)))

or maybe use cl-ppcre

December 19, 2006

Reading in Files

Filed under: c, lisp — rcjp @ 5:52 pm

I was catching up on Dr.Dobb’s columns and came across Walter Browns clean way of reading lines from files in Andrew Koenig’s c++ blog.

    #include <iostream>    // for cout 
    #include <fstream>     // for ifstream 
    #include <string>      // for string 

    int main()
    {
        std::ifstream file("t2.cpp");

        for(std::string s; getline(file, s);)
        {
            std::cout << s << '\n';
        }
    }


which is a nice alternative to the usual while loop as the string is scoped in the for loop that uses it.

Of course you can do something similar in C, provided you use -std=c99 compiler flag to gcc to allow declarations in for loops…

    #include <stdio.h>
    #define LINE_MAX 256

    int main()
    {
        FILE *fp=fopen("t1.c","r");

        for (char line[LINE_MAX]; fgets(line, LINE_MAX, fp);)
        {
            printf("line = %s\n", line);
        }
    }


Production code should always check the results of fopen, and fgets may return a NULL because of a problem reading the file – not because it reached the end, so you should check with feof. But in ‘quick and dirty’ mode I skip these and C will segmentation fault trying to read from a missing file. C++ will just quietly carry on, neither of which is ideal.

Common Lisp will throw you into the debugger when

    (defun test ()
      (with-open-file (stream "badfilename" :direction :input)
        (loop for line = (read-line stream nil nil)
           while line do
             (format t "line = ~A~%" line))))


fails to find the file. (I’ve never really liked those nil’s that follow read-line though, the first says the end of file isn’t an error and the second is what you want to signify the end of input. I think I’d prefer it if they were keyword arguments – more descriptive.)

If you are using a lisp compiler that supports restarts (unfortunately CMUCL/SBCL don’t as far as I can tell, but clisp and Lispworks do) you can quickly patch the running code if you don’t feel like going back to the source just yet…

    r@laptop:~/lisp/tmp$ clisp -q
    [1]> (load "test.lisp")
    ;; Loading file test.lisp ...
    ;; Loaded file test.lisp
    T
    [2]> (test)

    *** - OPEN: file #P"/home/r/tmp/badfilename" does not exist
    The following restarts are available:
    ABORT          :R1      ABORT
    Break 1 [3]>


first find out where you are with :w

    Break 1 [3]> :w
    <1> #<SYSTEM-FUNCTION OPEN>
    EVAL frame for form (OPEN "badfilename" :DIRECTION :INPUT)
    Break 1 [3]>


now you can see the actual open command the with-open-file macro got expanded into, and use :rt to return the correct filename by typing in the correct value of OPEN at the “Values:” prompt and carry on execution

    Break 1 [3]> :rt
    Values: (OPEN "correctfilename" :DIRECTION :INPUT)


There is an interesting document showing some more advanced restarting of code here using acl but is worth reading even if you don’t have that implementation.

October 11, 2006

Find classes in java jar files

Filed under: lisp, utils — rcjp @ 1:57 pm

;;;
;;; Find classes in jar files
;;; Usage (findjar classname directory)
;;; e.g (findjar "session" "c:/Novell/ndk/njclv2/lib")
;;;
(defun findjar (classname dir)
  ;;  (if (not (directory dir))
  ;;(format t "Invalid directory syntax - remember a trailing slash")
  (dolist (file (directory (concatenate 'string dir "/**/*")))
    (when (string-equal (pathname-type file) "JAR")
      (format t "processing ~S~%" file)
      (with-open-stream (str
        #+cmu
        (run-program (concatenate 'string "jar tf " (namestring file)) :output :stream)
        #+ clisp
        (ext:run-program "jar" :arguments (list "tf" (namestring file)) :output :stream)
        #+ lispworks
        (sys:open-pipe (concatenate 'string "jar tf " (namestring file)))
                          )
        (loop for line = (read-line str nil nil)
              while line
              do
              (when (search classname line :test 'string-equal)
                (format t "   found->~S in file (~S)~%" line (namestring file))
                (return)))))))

Nonograms in Common Lisp

Filed under: lisp — rcjp @ 1:47 pm

;;;
;;; Nonograms are puzzles from Sunday Telegraph: the length and order
;;; of blocks in a grid are given along both rows and columns the
;;; challenge is to work out how many spaces occur between the blocks
;;; and find the hidden picture (see example below)
;;;

;;; the strategy we use is to generate all the possible spacings
;;; of blocks and see if any parts of the grid, for all possible
;;; solutions, are always filled '1' or always empty '0'

(defun common-bits (bitvecs)
  "Returns a bit vector with '1' in positions where all supplied
bitvectors all have 1 or 0"
  (let ((firstvec (first bitvecs)))
    (reduce #'bit-and (loop for bv in bitvecs collect (bit-eqv firstvec bv)))))

(defun common-list-positions (lists)
  "Returns a list of (value . position) elements common to all lists"
  (let ((bitvecs (mapcar #'(lambda (list) (coerce list 'bit-vector)) lists)))
    (loop with common = (common-bits bitvecs)
       with vec = (first bitvecs)
       for start = 0 then (1+ pos)
       for pos = (position 1 common :start start)
       while pos collect (cons (sbit vec pos) pos))))

(defun make-section (lhs lspace blocksize rhs &optional not-end-section)
  "Make a section of a row (also used for columns), the end piece has
the rhs full of spaces else just pad a space ready for the next section"
  (append lhs
          (make-list lspace :initial-element 0)
          (make-list blocksize :initial-element 1)
          (if not-end-section
              '(0)
              (make-list rhs :initial-element 0))))

(defun blocks-spacings (blocklist nspaces &optional prevblocks)
  "Calculate all the arrangements of the list of blocks over nspaces"
  (loop
     with blocklen = (first blocklist)
     with moreblocks = (rest blocklist)
     with maxleft = (- nspaces (+ (apply #'+ moreblocks) (length moreblocks)))
     ;;
     ;; working left-to-right, work out all possible positions for the
     ;; current block recursively calling for blocks to the rhs
     ;;
     for blockpos upto (- maxleft blocklen)
     for section = (make-section prevblocks blockpos blocklen
                                 (- maxleft blocklen blockpos) moreblocks)
     ;;
     ;; build the row (column) in sections, appending sections to the
     ;; collect'ed right hand edge sections
     ;;
     when moreblocks append
       (blocks-spacings moreblocks (- nspaces blockpos blocklen 1) section)
     else collect section))

(defun find-spacings (blocklists nspaces)
  "Generate a vector from the blocklists where each element holds
all possible arrangements of each blocklist in nspaces"
  (let ((board (make-array (length blocklists) :fill-pointer 0)))
    (dolist (blocklist blocklists board)
      (vector-push (blocks-spacings blocklist nspaces) board))))

(defun multiple-solutions-p (boardx boardy)
  "If there is more than one possible arrangement anywhere - its not solved"
  (flet ((length1-p (seq) (= (length seq) 1)))
    (or (notevery #'length1-p boardx)
        (notevery #'length1-p boardy))))

(defun print-board (board)
  (loop for row across board
     do (format t "~{~&~{~[ ~;o~]~}~}" row)))

(defun delete-arrangement (array element test)
  "Delete from the list of arrangements in array element those
that fail the test"
  (setf (aref array element) (delete-if-not test (aref array element))))

(defun eltcheck (index val)
  "Returns a function which can be applied to a sequence to
check if the index value is val"
  #'(lambda (n) (= (elt n index) val)))

(defun nonogram (row-blocks col-blocks &optional (maxiterations 100))
  "Solve the nongram described by the list of row-blocks and
col-blocks, give up after maxiterations"
  (let ((possible-rows (find-spacings row-blocks (length col-blocks)))
        (possible-cols (find-spacings col-blocks (length row-blocks))))
    (flet ((find-apply-rules (board-find board-apply)
             (dotimes (line (length board-find))
               (dolist (rule (common-list-positions (aref board-find line)))
                 (delete-arrangement board-apply (cdr rule) (eltcheck line (car rule)))))))
      (loop while (multiple-solutions-p possible-rows possible-cols)
         repeat maxiterations
         do
           (find-apply-rules possible-rows possible-cols)
           (find-apply-rules possible-cols possible-rows))
      (print-board possible-rows))))

#|
e.g.
(nonogram '((5) (4) (3 3 3) (8 1) (7 3 3) (2 2 5 1) (1 6 3 1) (3 3 2) (3 2 1) 
            (2 2) (1 2) (1 2) (2) (2) (2) (2) (8) (11) (13) (4 4 5 3) (3 2 3 3) 
            (2 3) (20) (14 1 2) (4 1 12))
          '((6) (6) (2 3) (3 1 3) (3 1 2) (3 5 3 2) (3 4 4 3) (7 5 2) (4 4 3) 
            (4 2 3 3) (4 14 3) (20 3) (2 2 5 3) (1 3 5 3) (1 3 5 1 1) (1 3 3 1 1) 
            (1 1 3) (3 4 1) (1 6) (3 6)))
=>
          ooooo     
         oooo       
     ooo ooo   ooo  
    oooooooo     o  
   ooooooo ooo   ooo
   oo  oo ooooo    o
   o  oooooo ooo   o
     ooo ooo  oo    
     ooo  oo   o    
     oo   oo        
     o    oo        
     o    oo        
          oo        
          oo        
          oo        
          oo        
       oooooooo     
     ooooooooooo    
    ooooooooooooo   
oooo oooo  ooooo ooo
ooo   oo    ooo  ooo
oo               ooo
oooooooooooooooooooo
oooooooooooooo  o oo
oooo  o oooooooooooo

|#

October 3, 2006

Word Frequency Comparison

Filed under: c, lisp, python — rcjp @ 2:49 pm

Quite a common programming exercise is to write a word frequency counter. For a bit of fun I thought I’d code it in a few languages for comparison. There are essentially two parts to the problem: splitting up text into real words and keeping a tally of the occurances. Before we look at the python here is an interesting piece of shell script from the Classic Shell Scripting Oreilly book:

#! /bin/sh
# Read a text stream on standard input, and output a list of
# the n (default: 25) most frequently occurring words and
# their frequency counts, in order of descending counts, on
# standard output.
#
# Usage:
#       wf [n]
tr -cs A-Za-z\' '\n' |        # Replace nonletters with newlines
  tr A-Z a-z |                # Map uppercase to lowercase
    sort |                    # Sort the words in ascending order
      uniq -c |               # Eliminate duplicates, showing their counts
        sort -k1,1nr -k2 |    # Sort by descending count, and then by ascending word
          sed ${1:-25}q       # Print only the first n (default: 25) lines;


You can then use e.g. head -n10 to get the top ten words, or wf 999999 < texfile | wc -l to find the number of unique words etc. It is impressive that it can be done, but I think I’d forget what those args to sort did pretty quick.

Python

There was an entry in the python shootout (which I can’t seem to find now online?)

import sys

def main():
    i_r = map(chr, range(256))

    trans = [' '] * 256
    o_a, o_z = ord('a'), (ord('z')+1)
    trans[ord('A'):ord('Z')+1] = i_r[o_a:o_z]
    trans[o_a:o_z] = i_r[o_a:o_z]
    trans = ''.join(trans)

    count = {}
    for line in sys.stdin:
        for word in line.translate(trans).split():
            try:
                count[word] += 1
            except KeyError:
                count[word] = 1

    l = sorted(zip(count.itervalues(), count.iterkeys()), reverse=True)

    print '\n'.join(["%7s %s" % (count, word) for count, word in l])

main()


and a sample run would be…

    r@laptop:~$ python py/work/wordfreq.py < testwords
   5621 the
   2739 a
   2585 of
   2242 and
   2155 it
   2086 to
   1938 he
   1750 said
   1495 you
   1387 was
   1221 in
   1132 that


But I don’t think I would have come up with that translate solution. My, probably slower, effort was:

import re

def wordfreq_fromfile(filename):
    wordfreq(file(filename).read())

def wordfreq(str):
    freq={}
    words = re.compile(r"[\w'-]+")
    for word in words.finditer(str):
        w = word.group(0).lower()
        freq[w] = freq.get(w,0) + 1
    # make a list of most common words
    common = sorted(freq, key=freq.get, reverse=True)
    print '\n'.join("%7s %s" % (freq[word], word) for word in common)


and I think it is more obvious what that code is doing.

C++

The standard library has a map container handy to keep the word/occurance, but you can’t sort that so we switch to a vector at the end

#include <iostream>
#include <fstream>
#include <map>
#include <string>
#include <vector>
#include <algorithm>
#include <iterator>
#include <iomanip>

typedef std::map<std::string,int>  Dict;
typedef std::pair<std::string,int> DictItem;
typedef std::vector<DictItem>      VDict;

bool wordfreq_greater(const DictItem& a, const DictItem& b)
{
    return a.second > b.second;
}

// cout will only look for << operators in std namespace
// (could've just defined format in print_words rather than <<)

namespace std{
    std::ostream& operator << (std::ostream& out, const DictItem& d)
    {
        return out << std::setw(7) << d.second << " : " << d.first;
    }
}

template <typename T>
void print_words(T iter, std::string title, int maxcount)
{
    std::cout << title << std::endl
              << std::string(title.size(), '-') << std::endl;
    for(int i = 0; i < maxcount; ++iter, ++i)
        std::cout << *iter << std::endl;
    std::cout << std::endl;
}

int main(int argc, char *argv[])
{
    std::ifstream essay(argv[1]);
    if (!essay) {
        std::cerr << "please supply a filename for input" << std::endl;
        exit(1);
    }

    Dict dict;
    std::string word;
    while (essay >> word)    // count up frequency of words
        ++dict[word];

    // print the first 10 words, map sorts alphabetically
    // by default.. map< std::string,int,std::less<std::string> > dict;

    print_words<Dict::const_iterator> (dict.begin(), "Alphabetically", 10);

    // copy the dict to a vector so we can sort it

    VDict vdict(dict.begin(), dict.end());
    sort(vdict.begin(), vdict.end(), wordfreq_greater);

    print_words<VDict::const_iterator>(vdict.begin(), "Frequency", 10);
}


when run with a sample text file prints something like:

Alphabetically
--------------
      3 : !
    230 : "
      1 : ""allo,
      1 : "'E's
      1 : "'Ha'
      1 : "'Yet
      2 : "...
      2 : "/
      2 : "7
     38 : "A

Frequency
---------
   4836 : the
   2529 : a
   2509 : of
   1997 : to
   1843 : and
   1342 : was
   1304 : said
   1156 : he
   1093 : in
    917 : it


so it could probably do with some work on getting punctuation right.

Common Lisp

Common Lisp doesn’t have regular expressions included, but its usually not too much trouble to just write a quick bit of parsing code

    (defvar *freq* (make-hash-table))

    (defun word-letter-p (c)
      (and (characterp c) (alpha-char-p c)))

    (defun gobble-punctuation (stream)
      (loop for c = (peek-char t stream nil)
         until (word-letter-p c)
         while c do (read-char stream)))

    (defun getword (stream)
      (gobble-punctuation stream)
      (loop for c = (read-char-no-hang stream nil nil)
         while (word-letter-p c)
         collect c into letters
         finally (return (format nil "~{~C~}" letters))))

    (defun wordfreq (stream)
      (clrhash *freq*)
      (loop for word simple-string = (getword stream)
           while (string-not-equal word "")
           do (incf (the fixnum (gethash (intern (string-downcase word)) *freq* 0))))
      (let ((freqlist nil))
        (maphash #'(lambda (k v) (push (cons v k) freqlist)) *freq*)
        (loop for (k . v) in (sort freqlist  #'> :key #'car)
           repeat 15 ;; just dump out the top 15 words
           do (format t "~4A, ~A~%" k v))))

    (defun wordfreq-string (str)
      (with-input-from-string (s str)
        (wordfreq s)))

    (defun wordfreq-file (filename)
      (with-open-file (s filename :direction :input)
        (wordfreq s)))

    (wordfreq-file "/home/r/py/finished/testwords")


of course there are regexp libraries for Common Lisp like Edi Weitz’s cl-ppcre:

    (eval-when (:compile-toplevel :load-toplevel :execute)
      (require :cl-ppcre))

    (defvar *freq* (make-hash-table))

    (defun wordfreq (stream)
      (clrhash *freq*)
      (loop for line simple-string = (read-line stream nil nil)
         while line
         do (cl-ppcre:do-matches-as-strings (word "[\\w'-]+" line)
              (incf (the fixnum (gethash (intern (string-downcase word)) *freq* 0)))))
      (let ((freqlist nil))
        (maphash #'(lambda (k v) (push (cons v k) freqlist)) *freq*)
        (loop for (k . v) (fixnum . symbol) in (sort freqlist  #'> :key #'car)
           repeat 15 ;; just dump out the top 15 words
           do (format t "~4A, ~A~%" k v))))

    (defun wordfreq-string (str)
      (with-input-from-string (s str)
        (wordfreq s)))

    (defun wordfreq-file (filename)
      (with-open-file (s filename :direction :input)
        (wordfreq s)))

    (wordfreq-file "/home/r/py/finished/testwords")


None of these programs are really finished… it’d be very fiddly to get them to deal correctly will all kinds of punctuations marks etc, also I haven’t bothered to profile any of them – computers are so quick these days that speed is almost irrelevent for this kind of stuff.

September 2, 2006

Characteristic Language Structures – Dictionaries

Filed under: lisp, python — rcjp @ 2:45 pm

Its funny how some languages get associated with a single storage container; even when they support many others. Its often the first thing that pops into your head when you hear the language mentioned and influences the resulting code you write. For lisp thats undoubtedly a list, for C++ its a class, python I reckon its a dictionary and for C, well I suppose pointers are the first thing most people think of, that and having to write almost all storage structures from scratch!

Almost all python programs seem to end up using a dictionary for something, like this one I used in some code to markup html tags

    tags =  {
        'date' : '<div class="date">%s</div>',
        'list' : '<ul>%s</ul>'
        }

accessing, iteration and checking for values is easy:

    In [8]: tags['date']
    Out[8]: '<div class="date">%s</div>'

    In [10]: tags.has_key('date')
    Out[10]: True

    In [24]: tags.keys()
    Out[24]: ['date', 'list']

as is adding to the dictionary, and removing:

    In [17]: tags['yyy']='blah'
    In [19]: tags.pop('yyy')
    Out[19]: 'blah'

normally a KeyError exception occurs if the key doesn’t exist but we can fail quietly if there isn’t a value, or return a default value:

    In [15]: tags.get('xxx')

    In [16]: tags.get('xxx',0)
    Out[16]: 0


But it python 2.5 it is apparantly faster to do

    from collections import defaultdict
    tags=defaultdict(int)
    tags['xxx'] += 1


this will call int() to supply a value if it is missing and that defaults to 0. If we wanted a different default then

    tags = defaultdict(lamda: 1)

gives 1 by supplying a ‘factory’ function. I’ll finish this quick dictionary overview with an example for swapping out those pesky characters like spaces when generating url’s

    In [48]: [{'@':'%40', ' ':'%50'}.get(c,c) for c in "name=ab@c"]
    Out[48]: ['n', 'a', 'm', 'e', '=', 'a', 'b', '%40', 'c']

    In [49]: ''.join({'@':'%40', ' ':'%50'}.get(c,c) for c in "name=ab@c")
    Out[49]: 'name=ab%40c'

the get(c,c) trick means that characters default to themselves if they are not in the dictionary we are using for swapping. Incidentally, as usual, python already has a built-in mapping of all those codes…

    In [42]: import urllib
    In [43]: urllib.urlencode({'name':'ab@c'})
    Out[43]: 'name=ab%40c'

Common Lisp

There are several choices for pairing data in Common Lisp. There are property lists – using get, getf, get-properties; but they are meant for symbol properties I think, not for general data. Then there are association lists (a-lists):

    CL-USER> (setq *tags* '((date . "<datey>") ("yyy" . "blah")))
    ((DATE . "<datey>") ("yyy" . "blah"))
    CL-USER> (assoc 'date *tags*)
    (DATE . "<datey>")
    CL-USER> (assoc "yyy" *tags*)
    NIL

but note that the ‘whole’ thing is returned and since the default test is eq, it won’t work for string comparison, you have to do

    CL-USER> (assoc "yyy" *tags* :test #'equalp)
    ("yyy" . "blah")

you can alter what the test function will operate on with key. Here we use rassoc to search on the values rather than the keys to find an entry with ‘a’ in position 2 (zero base indexed strings)

    CL-USER> (rassoc #\a *tags* :key #'(lambda(x) (char x 2)))
    (DATE . "<datey>")

you can replace existing values (but they must exist, else you’ll be trying to set a nil)

    CL-USER> (rplacd (assoc 'date *tags*) "yyy")
    (DATE . "yyy")

you can add things on, but not destructively with

    CL-USER> (acons "aaa" 5 *tags*)
    (("aaa" . 5) (DATE . "yyy") ("yyy" . "blah"))
    CL-USER> *tags*
    ((DATE . "yyy") ("yyy" . "blah"))

or destructively with

    CL-USER> (push '("aaa" . 5) *tags*)
    (("aaa" . 5) (DATE . "yyy") ("yyy" . "blah"))
    CL-USER> *tags*
    (("aaa" . 5) (DATE . "yyy") ("yyy" . "blah"))
    CL-USER> (pop *tags*)
    ("aaa" . 5)
    CL-USER> *tags*
    ((DATE . "yyy") ("yyy" . "blah"))

since its just a cons pair you’d probably loop on them by doing

    CL-USER> (loop for item in *tags* do (print (cdr item)))
    5
    "yyy"
    "blah"

You also have the choice of using hash tables: gethash can easily specify default values

    CL-USER> (defvar *tags-hash* (make-hash-table))
    *TAGS-HASH*
    CL-USER> *tags-hash*
    #<HASH-TABLE :TEST EQL :COUNT 0 {AC96F29}>
    CL-USER> (gethash "aaa" *tags-hash*)
    NIL
    NIL
    CL-USER> (gethash "aaa" *tags-hash* 0)
    0
    NIL

and to add a value to the hashtable we setf a query to the value we want

    CL-USER> (setq *tags-hash* (make-hash-table :test #'equalp))
    #<HASH-TABLE :TEST EQUALP :COUNT 0 {AC0DE51}>
    CL-USER> (setf (gethash "aaa" *tags-hash*) 111
                   (gethash "bbb" *tags-hash*) 222
                   (gethash "ccc" *tags-hash*) 333)

    333
    CL-USER> (gethash "aaa" *tags-hash*)
    111
    T
    CL-USER> (remhash "bbb" *tags-hash*)
    T
    CL-USER> *tags-hash*
    #<HASH-TABLE :TEST EQUALP :COUNT 2 {AC0DE51}>

and you can dump out the values of hashtables with

    CL-USER> (maphash #'(lambda (k v) (format t "~A ~A~%" k v)) *tags-hash*)
    aaa 111
    ccc 333
    NIL
    CL-USER> (loop for i being the hash-keys of *tags-hash* do (print i))

    "aaa"
    "ccc"
    NIL
    CL-USER> (loop for i being each hash-value of *tags-hash* do (print i))

    111
    333
    NIL
    CL-USER> (loop for i being the hash-keys of *tags-hash*
                using (hash-value myvalue)
                do (format t "~A->~A~%" i myvalue))
    aaa->111
    ccc->333
    NIL

Note: we have to set the test type when we create the hashtable rather than in the query like with a-lists. CL is really set up for using symbols wherever possible, so consider using intern to create a symbol from the string and store that instead. For small stuff use alists and bigger stuff use hashtables. Finally, the url character substitution from above could be something like

CL-USER> (loop for c across "name=ab@c" collect (or (cdr (assoc c '((#\@ . 40)))) c))
(#\n #\a #\m #\e #\= #\a #\b 40 #\c)
CL-USER> (format nil "~{~A~}" *)
"name=ab40c"

July 28, 2006

Queens Chess problem

Filed under: lisp — rcjp @ 1:49 pm

;;;
;;; How to place 8 queens on a chess board so that none attack the others
;;;
;;; find-queens is a recursive implementation
;;; return-queens collects the solutions and uses loop instead of dotimes
;;;
;;; (length (return-queens)) => 92

(proclaim '(optimize (speed 0) (space 0) (debug 3)))

(defstruct (square
             (:print-function (lambda (sq stream depth)
                                (declare (ignore depth))
                                (format stream "(~A, ~A)" (square-col sq) (square-row sq)))))
  (col 1 :type integer)
  (row 1 :type integer))

(defun print-square (sq stream depth)
  (declare (ignore depth))
  (format stream " (~A, ~A) " (square-col sq) (square-row sq)))

(defun find-queens (&optional (queens '()))
  (let ((col (list-length queens)))
    (if (= col 8)
        (format t "~A~%" queens)
        (dotimes (row 8)
          (let ((q (make-square :col (+ col 1) :row (+ row 1))))
            (if (safe-queen-p q queens)
                (find-queens (cons q queens))))))))

(defun return-queens ()
  (let ((solutions nil))
    (labels ((find-queens1 (queens)
               (let ((col (1+ (list-length queens))))
                 (if (> col 8)
                     (push queens solutions)
                     (loop for row from 1 upto 8
                           as q = (make-square :col col :row row)
                           when (safe-queen-p q queens)
                           do (find-queens1 (cons q queens)))))))
      (find-queens1 '())
      solutions)))

(defun safe-queen-p (q1 queens)
  (notany #'(lambda (q2) (attack-p q1 q2)) queens))

(defun attack-p (q1 q2)
  "determine if two queens in positions q1, q2 are attacking each other"
  (declare (square q1 q2))
  (or (= (square-col q1) (square-col q2))
      (= (square-row q1) (square-row q2))
      (= (abs (- (square-col q2) (square-col q1)))
         (abs (- (square-row q2) (square-row q1))))))

July 26, 2006

Twenty Questions

Filed under: lisp — rcjp @ 1:55 pm

;;;
;;; Classic 'fish-flower-fruit' game
;;; Questions are put to guess the object, and can only be
;;; answered 'y' or 'n'
;;;
;;; run (ask-questions) to start

(defun make-info-node (question-text afirmative negative)
  (list question-text afirmative negative))

(defun get-question (info-node) (first info-node))
(defun get-afirmative (info-node) (second info-node))
(defun get-negative (info-node) (third info-node))

(defun guess (info-node item)
  (format t "Is it a ~A ~%" item)
  (let ((reply (read)))
    (if (equal reply 'y)
        (continue-game?)
      (progn
        (format t "What is it ?~%")
        (let ((object (read)))
          (format t "What question distinguishes a ~A from a ~A ?" object item)
          (let ((question (read-line)))
            (break)
            (setf (cdr (cdr info-node)) (list (make-info-node question object item)))
            (continue-game?)))))))

(defun continue-game? ()
  (format t "Continue game ?~%")
  (let ((reply (read)))
    (if (equal reply 'y)
        (ask-questions)
      nil)))

(defun ask-question (info-node)
  (format t "~A  " (get-question info-node))
  (let ((reply (read)))
    (if (equal reply 'y)
        (check-answer info-node (get-afirmative info-node))
      (check-answer info-node (get-negative info-node)))))

(defun check-answer (info-node info-node-part)
  (if (listp info-node-part)
      (ask-question info-node-part)
    (guess info-node info-node-part)))

(defparameter *info* (list 'animal?
                     (list 'slimy? 'fish 'bear)
                     'box))

(defun ask-questions ()
  (ask-question *info*))

July 23, 2006

Creating Functions

Filed under: c, lisp — rcjp @ 2:34 pm

In the introduction of Paul Graham’s ANSI Common Lisp he writes that you can’t do the following in C


(defun addn (n)
  #'(lambda (x) (+ x n)))

(setf add-ten (addn 10))
(setf add-twenty (addn 20))

(format t "value ~d~%" (funcall add-ten 4))
(format t "value ~d~%" (funcall add-twenty 4))

(This will print 14 and 24). Although you can have function pointers in C, there isn’t really a convenient place to store the captured n variable. You can use a global variable, but that will get overridden by another function


#include <stdio.h>

int nglobal = 0;
int addn(int n) { return (n+nglobal); }

int main()
{
    int (* myfunc)(int);

    nglobal = 10;
    myfunc = addn;

    printf("value %d\n", (* myfunc)(4));
    return 0;
}

you can get further with C++ though by using a class instance variable


#include <iostream>

class addn
{
  int nInstance;
public:
  addn(int n) : nInstance(n) {}
  int operator()(int i) {return nInstance + i;}
};

int main()
{
  addn add_ten(10);
  addn add_twenty(20);

  std::cout << "value " << add_ten(4) << std::endl;
  std::cout << "value " << add_twenty(4) << std::endl;
  return 0;
}

but it is clumsy compared to the Common Lisp. The C++ function objects in the standard library aren’t particularly pretty either, compare:

#include <iostream>
#include <vector>

int main()
{
    std::vector<int> v;
    v.push_back(5);
    v.push_back(4);
    v.push_back(11);
    v.push_back(14);

    // count numbers less than 10

    std::cout << count_if(v.begin(),  v.end(),
                 bind2nd(std::less<int>(),  10)) << std::endl;
}


in python

In [9]: sum(itertools.imap(lambda x: x<10, [5,4,11,14]))
Out[9]: 2


and in Common Lisp this is just

(count-if (lambda (x) (< x 10)) '(5 4 11 14))

June 2, 2006

Using a shared lib from cmucl

Filed under: c, lisp, unix — rcjp @ 1:24 pm

library code mylib2.c

#include <stdio.h>

int mytest()
{
    printf("inside c lib !!!\n");
    return 2;
}

test.lisp

(defun fact (n)
  (if (= n 1)
      1
      (* n (fact (- n 1)))))

(ext:load-foreign "/usr/local/lib/libmylib2.so.1")
(alien:alien-funcall (alien:extern-alien "mytest" (function c-call:void) ))

test2.c

#include <stdio.h>

int main()
{
    printf("inside main program\n");
    mytest();
    printf("back inside main program\n");
}

build using

#!/bin/sh
cc -fPIC -c mylib2.c
gcc -shared -Wl,-soname,libmylib2.so.1 -o libmylib2.so.1   mylib2.o
# 
# now use the lib
#
# need to copy libmylib2.so.1 to /usr/local/lib or somewhere
# then as root do 'ldconfig' 
cc test2.c -L. -lmylib2.so.1 -o test2

April 27, 2005

Amsterdam European Common Lisp Users Meeting 23rd-26th

Filed under: lisp — rcjp @ 3:32 pm

Just one or two quick notes about the meeting and city…

I stayed at the Wiechmann Hotel which was ok, I was glad I was on the Prinsengracht canal, definitely the best one (I didn’t bring my camera – wanted to travel light as possible)

It was interesting to see Amsterdam’s café culture but so many bikes and vespa’s made walking really quite tiring – having to scan around at the many little canal/road junctions before crossing. The road system also seemed very complex, glad I wasn’t driving. Most surprising though was the number of mums I saw cycling with toddlers peering out from a wooden box mounted on the front; looks like tremendous fun for the kiddie but there is no way you’d see that in Manchester with all its Range Rovers etc. I’d like to try riding one of those bikes with no brake levers – you just pedal backwards apparantly, I think my dad said he had one as a boy; I wonder why they disappeared in the UK, maybe they are illegal here?

Amsterdam as a city attracts some very dodgy characters and quite alot of graffiti; they say 300 bikes a day are pulled from the canals and I suppose, as with any major city, the large number of tourists give rise to the tacky shops etc. and spoil it somewhat. Still pretty though, I love that there are shops below apartments, property developers in the UK never seem to think/care about that.

The talks were at the Felix Meritis – the old communist headquarters many years ago…

Dave Fox of Lispworks presented some debugger stuff, but I was not over impressed that when someone asked what would happen for a recursive function in the profiler tree he presented he said he did not know: perhaps he was just fed up that people had interupting his talk with questions. Jans Aasman also had a fairly rough time presenting AllegroCache from the audience; I sat next to him on the Sat dinner – I didn’t realise he was a directory of Franz.

Espen Vestre’s Primetrader stuff was impressive, very polished and encouraging that a commercial company is making gains from Common Lisp’s expressive capabilities and he generously shared some of the techniques he was using.

Luke Gorrie was so chilled out presenting Slime – maybe he’d been smoking something? ;) He sure was lightening fast manoeuvring around emacs and is another lisper that seems to be using CMUCL.

Robert Strandh presented Climacs, but it is built on McClim which seems horribly inefficient to me – like it sends all the changes to the buffer and relies on McClim to sort out what needs to be actually updated on the screen, but I haven’t looked at it deeply: it, and clim, seem too easy to crash to spend any time on it at the moment, maybe it will improve.

Linj seems amazing – roundtripping Lisp to Java development and Antonio Menezes Leitao seemed very honest and open in discussing it, even though he did sigh wearily and repeatedly thoughout his talk – almost as though he despairs at a world full of Java monkeys.

Bruno Haible was interesting to meet, he seems an incredibly focussed person and he showed me his gorgeous Apple Powerbook, though in my opinion, it still does not have a good enough resolution (compared to my dell-8100@1600×1200) for reading pdf’s etc. It is amazing how many people now have mac’s though.

Peter Seibel was funny talking about Lisp, I hope he repeats it at lots of conferences around the world – it should pull in quite a few interested programmers.

Definitely worth attending the conference – if only to put faces to so many familiar names. Many thanks are due to Edi Weitz and Arthur Lemmens who did a grand job organising things.

Blog at WordPress.com.