I Can’t Play Diablo 3 So I’m Going to Make a Game

Well it turns out that the best computer I have cannot run Diablo 3.

As I am not Mr. Warbucks (what with the housing prices in Toronto and a child on the way) I cannot simply go out and blow a thousand bucks on a new rig just to run a new game (sad though it is).

I have also been reading The Making of Prince of Persia at the behest of a co-worker and have been greatly inspired by it. It documents the trials of Jordan Mechner during the years of his life it took to produce this game. In the early years he had been distracted by ambitious dreams of becoming a screenwriter before settling in and deciding that making games was his true calling. While I cannot say that I have any sort of talent for making games I can say that I have developed a certain sense of taste and minor skills in art, music, and I am a fairly decent programmer. If I put all of these skills together why couldn’t I make a game?

Problem is that I never get around to finishing any of them.

So in order to play Diablo 3 I need to buy a new computer. In order to buy a new computer I need more money than what I can get from my paycheck (which is going into saving up for a house and putting away for the little one). The only way I can do this is to make something that I can sell many copies of for relatively cheap. Computer games!

So I’ve dusted off some of my more novel ideas and am in the process of investigating the different platforms, tools, and distribution options. In the coming weeks I’ll post some concept sketches and demos as things come together. And hopefully somewhere in the near future I will have a decent game that I can feel proud of and sell so that I can afford to buy a new computer so that I can play Diablo 3. It’s all about goals and focus. On the important things.

Why Is The Future Not Here Yet?

I had a moment last night at the bar. I’ve been reading Charlie Stross’ The Atrocity Archives on my Kindle Touch. It’s a really cool story and I wanted to share it with a friend. Normally I’d hand him the book and he could read it at his leisure. Instead I wanted to just flick the e-book over to his phone so he could have a copy. I was mildly struck with disappointment realizing that we totally have the technology to make that happen and yet it isn’t here right now.

After telling him how wonderful this book is I said, “I wish I could just hand it to you right now so you could read it.” We both sat in awkward silence for a moment staring at my Kindle sitting placidly on the table. Then he looks at me and grabs his phone and with a flicking gesture suggests, “Don’t you wish it could be like in all the sci-fi movies and TV shows? You could just flick the file over from your Kindle like this and it would just pop up on my phone?” I nod and sigh as I realize that between us the technology to make this happen exists. There’s just a rift of legal and technical policy that restricts anyone from making it happen.

The world is burgeoning with these missed opportunities of late. Floating around us is a dirge of futures that could have been the present. Cool technology that could exist if it wasn’t for out-dated copyright enforcement and capitalist self-interest.

It all leads back to the question of what are we doing locking down all of these general purpose computing devices into “appliances?” (or products if you prefer). The slate gray Kindle on the table between us flashes to a new screen saver. An ad for discounted romance novels in time for Valentines Day. This little device on the table isn’t a Kindle. It’s a general-purpose CPU with all the usual bits inside of a general-purpose computer. It just happens to have a nice form factor and a display that is easy on the human eye. The only thing that makes it a Kindle is Amazon and the “agreements,” I entered into with them when I started using the device (which are obfuscated and ephemeral… how did I agree to anything by using a device? Where’s the signature and the chance to propose my own amendments to the agreement?). If Amazon didn’t exist would the device before me cease to exist as well?

Sadly this is the future we are looking at. Even if they day comes where it becomes possible for me to share a book with a friend again no matter what device they use to read it… such a transaction will be burdened by the interferences of several third parties ensuring that someone somewhere will authorize it and someone else will make money from it. However even that pale future seems like wishful thinking when one considers that presently all of these devices are in competition with one another and attempting to build silos of services that work with and only with that particular associated “product.”

Sad.

[tags]technology, business, kindle, sharing, copyright, future[/tags]

Teaching Kids to Code

I went down to the Mozilla offices in Toronto tonight for a brainstorming session on teaching kids to code. I had caught on to the event when someone in my twitter feed reposted a tweet from @msurman advertising the event. It struck my fancy because I’ve been really thinking a lot about how I could give back to the world to make it a better place. I have been feeling a sort of “alstruistic itch,” and thought that teaching kids how to program was an incredibly good idea and that it was a great opportunity to give something of myself to the world in the hopes it would make it a better place for persons other than myself and the companies I work for.

I first have to thank Mozilla for hosting the event at their offices. It was a wonderful evening. They provided space and snacks and got everyone together to share their ideas on how we can get kids learning to code. It really goes to show that Mozilla is definitely a company that cares about technology and it’s bigger picture. Kudos and thank you.

The discussions of the evening began with some quick demonstrations of various technologies and pitches from organizations who are working right now to help kids learn to code. I saw a demonstration from Popcorn and Scratch. I heard pitches from Ladies Learning to Code and Hive. I think I was impressed the most by everyone’s passion. They were all so determined that they went out there and put these projects together just to share with kids the opportunities and joys of learning to code despite the challenges and hurdles of bootstrapping altruistic projects.

We then formed discussion groups around various interesting topics to brainstorm ideas. One group discussed how we can bring programming into schools. Another group came up with suggestions on how to host “hack jam” events where kids and mentors can get together for a day and learn something cool. I joined a group to discuss how we could grow the community of people interested in helping kids to code. We discussed why we think Toronto is a great city to start building this community, how to facilitate engaging volunteers and supporting them, to tools we think would be useful for facilitators to run workshops and courses. At the end everyone presented their ideas and people who were interested in helping out could leave their emails under the respective group!

I think it’s a really great idea to teach kids how to code. We live in a world where computers power everything around us and I think teaching kids how to code can empower them to take control of that technology before it controls us. It also provides kids with opportunities to step into many different kinds of careers later in life. And the act of teaching kids to code can bring communities together and rediscover our roots in our communities — that passing on useful skills and knowledge isn’t solely the job of institutions.

I am looking forward to seeing what becomes of this evening. I put in my name to help develop the community. I’d also like to teach kids about the things I know; especially at-risk/disadvantaged youth and girls — I want to live in a world where STEM no longer continues to be a curriculum geared to just boys. Technology is liberating and programming is fun. I want to live in world where anyone can become involved in technology and more people are aware of computers and how they work. I think it will become increasingly important as computers become more ubiquitous and threaten to control more of our daily lives.

Plus, it’s what I would teach my kids so why not share?

[tags]programming, kids[/tags]

Strict Types

When I first heard about Haskell I admit I was quite enamoured with the idea of correctness at the time. I had been working on a string of projects fraught with errors and bugs largely due to types. I had become frustrated with dynamic languages. It just seemed that they were too easily abused to be useful for large projects. As well I had also begun to develop an interest in mathematics and the concept of proof was something I saw as valuable.

I found myself dusting off my dog-eared copy of The C++ Programming Language today. As I leafed through some random passages I came across a bit of wisdom that had reminded me of my initial contact with Haskell. “Good design and the abscence of errors cannot be guaranteed merely by the presence or the absence of specific language features.”

(For some reason this also struck me as profound coming from the inventor of C++…)

As I delve more and more into Common Lisp I find myself wondering what advantage draconian enforcement of a set of language features has. I see Haskell and I marvel at the contortions one must be willing to accept in order to get useful work done. I work with Python day in and out and wonder why the community accepts the rule of a dictator (even if that “dictator” is now a small committee) to tell them what features are good and worth implementing. If I wanted big, nasty lambdas everywhere why shouldn’t I have them and be left to suffer the consequences and benefits myself? What if I really do want recursion? Or eager evaluation? Or lazy evaluation? Or types? Or no types? Or immutability? What if I don’t accept your defaults or conventions?

I actually have a funny spot for languages that encourage freedom of expression over correctness. I think Common Lisp has that in spades; you can program the language to do what you need it to to solve your problem. To a degree you can also do that with C++. And Perl. And maybe even Javascript too. Who knows? Either way I still prefer clear thought-out design, but I also like to break the rules when I see fit to. And I like languages that let me do that.

[tags]programming, languages, philosophy[/tags]

Parenscript Is Awesome

Warning: I am still very new to parenscript. This is more of a rant than anything.

I’m building an HTML5-ified graphical chat client. It draws the conversation as a comic book. There have been clients like it before but I want to try using the wonderful new web technologies we have today to try and take the idea further. It’s called mug.io.

I decided to build it in Common Lisp. I’ve been learning a lot about the language and decided that if I’m going to be building this thing in my free time I might as well enjoy doing it and use the language I want to use. Common Lisp has so much going for it that I wish I could use it in my day job… but I can’t so there you go.

However at some point I knew I’d have to write some Javascript. Initially I thought I would just do all the positioning calculations server side and push updates to a dead-simple client that would just issue the canvas calls to draw what I wanted. However, the more I played with the idea the less I liked it. And I didn’t relish the thought of writing a lot of Javscript either. What’s a Lisp hacker-wannabe to do?

Parenscript, duh.

This amazing little library can compile a subset of Common Lisp to Javascript. And it’s really good at it too. The Javascript that parenscript generates looks eerily hand-coded. The best thing about it is that I get all the power of Common Lisp that Javascript is lacking — macros, backquote… the whole nine yards.

My next question was… will I be able to use third-party Javascript libraries like JQuery? Yes you can. Awesome.

So I whipped up a bit of parenscript code to play around with it and get a feel for the library and what’s possible. Please pardon any glaring wierdness… I’m still new to the library and haven’t figured it all out yet (eg: how to turn off the implicit return statement).


(setf (ps:chain window onload) init)

(defvar *WIDTH* 400)
(defvar *HEIGHT* 400)

(defvar *canvas* nil)
(defvar *context* nil)
(defvar x 15)
(defvar y 0)
(defvar dx 10)
(defvar dy 5)

(defun init ()
(init-canvas)
(set-interval animate-box 20))

(defun init-canvas ()
(progn
(setf *canvas* (ps:chain document (get-element-by-id "canvas")))
(setf *context* (ps:chain *canvas* (get-context "2d")))
(setf (ps:@ *canvas* width) *WIDTH*)
(setf (ps:@ *canvas* height) *HEIGHT*)
1))

(defun draw-rect (x y w h)
(progn
(ps:chain *context* (begin-path))
(setf (ps:@ *context* stroke-style) "#000")
(ps:chain *context* (fill-rect x y w h))
1))

(defun draw-grid ()
(ps:chain *context* (begin-path))
(loop for x from 0.5 below *WIDTH* by 10
do (progn (ps:chain *context* (move-to x 0))
(ps:chain *context* (line-to x *HEIGHT*))))
(loop for y from 0.5 below *HEIGHT* by 10
do (progn (ps:chain *context* (move-to 0 y))
(ps:chain *context* (line-to *WIDTH* y))))
(progn (setf (ps:@ *context* stroke-style) "#eee")
(ps:chain *context* (stroke)))
1)

(defun clear ()
(ps:chain *context* (clear-rect 0 0 *WIDTH* *HEIGHT*)))

(defun animate-box ()
(progn (clear)
(draw-grid)
(draw-rect x y 20 20)
(if (or (> (+ x dx) (- *WIDTH* 20)) (< (+ x dx) 0))
(setf dx (- dx)))
(if (or (> (+ y dy) (- *HEIGHT* 20)) (< (+ y dy) 0))
(setf dy (- dy)))
(setf x (+ x dx))
(setf y (+ y dy))
1))

I put this in a file I called wafflemaker.paren and I compile it with parenscript using the following form:


(with-open-file (f "wafflemaker.js" :if-exists :supersede :direction :o utput)
(write-string (ps:ps-compile-file "wafflemaker.paren") f))

And this writes out a nice little file with some javascript in for me. I created a simple index.html file that loads it and open that in my HTML5 compliant browser (Firefox 5). I see a little bouncing box on a grid background. Pretty nifty.


window.onload = init;
var WIDTH = 400;
var HEIGHT = 400;
var CANVAS = null;
var CONTEXT = null;
var x = 15;
var y = 0;
var dx = 10;
var dy = 5;
function init() {
initCanvas();
return setInterval(animateBox, 20);
};
function initCanvas() {
CANVAS = document.getElementById('canvas');
CONTEXT = CANVAS.getContext('2d');
CANVAS.width = WIDTH;
CANVAS.height = HEIGHT;
return 1;
};
function drawRect(x, y, w, h) {
CONTEXT.beginPath();
CONTEXT.strokeStyle = '#000';
CONTEXT.fillRect(x, y, w, h);
return 1;
};
function drawGrid() {
CONTEXT.beginPath();
for (var x = 0.5; x < WIDTH; x += 10) {
CONTEXT.moveTo(x, 0);
CONTEXT.lineTo(x, HEIGHT);
};
for (var y = 0.5; y < HEIGHT; y += 10) {
CONTEXT.moveTo(0, y);
CONTEXT.lineTo(WIDTH, y);
};
CONTEXT.strokeStyle = '#eee';
CONTEXT.stroke();
return 1;
};
function clear() {
return CONTEXT.clearRect(0, 0, WIDTH, HEIGHT);
};
function animateBox() {
clear();
drawGrid();
drawRect(x, y, 20, 20);
if (x + dx > WIDTH - 20 || x + dx < 0) {
dx = -dx;
};
if (y + dy > HEIGHT - 20 || y + dy < 0) {
dy = -dy;
};
x += dx;
y += dy;
return 1;
};

Now this is just a toy script and does not demonstrate completely what's entirely possible with parenscript. You still get full access to Common Lisp macros. You still have the SLIME development environment in emacs. There's even a nice little emacs hack I'm looking into called slime-proxy that will stream your compiled parenscript defuns to the browser directly — updating your running Javascript code right from the REPL as its running... just like you can when writing a pure-Lisp program.

This is some seriously good kit.

If anyone can help me out with the explicit return thing.. that would be appreciated....

[tags]lisp, parenscript[/tags]

Stopping To Observe the Code

After reading the Little Schemer a while ago I became convinced that it was the best introduction to programming I have ever read. That it teaches the concepts of programming using Scheme is incicdental; they don’t spend much time teaching the language at all. Instead the book focuses on concepts and builds up from a small set of primitives all the way to the Y-Combinator (and a little bit beyond as well). The whole time I never felt I had to implicitly trust what the author was saying because the book would walk through the proof of each concept step by step. I am not aware of any other introductory title that takes this fundamental approach.

So suitably impressed was I that I have begun reading The Seasoned Schemer. It continues right where the previous book left off; picking up the chapter numbers as if it were literally a continuation of the first book (which it is in many regards). Within the first chapter I have been re-introduced to some familiar problems to get started that I wanted to share because I think there is some beauty here we programmers should all be aware of.

In order to ensure that I understood the problem and its solution, I decided to challenge myself to express the solution in other languages that I am familiar with. I also wanted to use the natural idioms of the language as much as I understood them. I immediately chose Python and Common Lisp. Feel free to contribute and discuss others. The goal isn’t to benchmark the run time performance of the solution but to discover a correlation between the expression of the solution, its implementation, and interpretation by humans.

First the problem: write a function that takes a tuple (a list of numbers) and returns a list summing the numbers that preceed each element to that element in the original tuple. For example:


sum_of_prefixes( (1, 1, 1) ) => (1, 2, 3)

sum_of_prefixes( (1, 2, 3, 4, 5) ) => (1, 3, 6, 10, 15)

The solution given in The Seasoned Schemer is:


(define sum-of-prefixes
(lambda (tup)
(sum-of-prefixes-b 0 tup)))

(define sum-of-prefixes-b
(lambda (sonssf tup)
(cond ((null? tup) '())
(else (cons (+ sonssf (car tup))
(sum-of-prefixes-b
(+ sonssf (car tup))
(cdr tup)))))))

This is by far my favorite solution. It states the problem and its solution explicitly. The structure of the function’s value is structure of the function itself. There is no implicit knowledge anywhere in this function. If you know Scheme, you can read this and understand what it is doing right away. No guessing or interpreting required.

Contrast that with Python:


def sum_of_prefixes(tup):
return [x for x in _sum_of_prefixes_b(0, tup)]

def _sum_of_prefixes_b(sonssf, tup):
for n in tup:
sonssf = sonssf + n
yield sonssf

The apparent shape of the problem is much different. Python doesn’t have a cons primitive and recursion, while supported, isn’t a paradigm that the language supports idiomatically or technically very well. Instead it prefers iteration which really is just a special case of recursion anyway. Naturally I reached for a generator when I needed a stateful iterator and the resulting solution was pretty succinct when compared to the Scheme version.

However, try to observe the shape of this problem. In sum_of_prefixes we see that the return value will be a list constructed from a list comprehension that iterates over our generator. Underneath this code we know that at some point Python has to allocate an array in memory to hold these values since it doesn’t have a linked list from which it can generatively construct the result. We can test and verify that this function is indeed correct and be happy with it. However, if for some large tup we find that it begins to slow down, we will soon realize that we have to figure out some of Python’s internals.

Unfortunately I feel that this is where the Python solution falls short — the true nature of this solution is hidden in a black-box. If we want to be able to reason about the time and space complexity of this solution we have to know how Python constructs and allocates the array that the list comprehension creates. Does it allocate an empty dynamic array and append elements to it as it iterates over the generator? Does it implement the “array” as a heap of some sort? Does it use some other trick? How can we reason about that which we cannot see?

Of course this is an extremely trivial problem. We could write a function like this and be be perfectly happy with it. Even if it wasn’t the most performant. Stretching the example to large tup would be an artificial excercise and not prove anything. The real cost here is that a Python programmer can write something so succinct and not worry about its implementation until it matters. That’s when things can get a little hairy.


(defun sum-of-prefixes (tup)
(let ((sonssf 0))
(loop for n in tup
do (setf sonssf (+ sonssf n))
collect sonssf)))

Finally I went for a Common Lisp solution. If brevity was our goal, this example would win by leaps and bounds. However, even more so than Python this solution is hiding a score of information under its skirts. That’s because the loop function there is actually a Lisp macro. At compile time it will generate a bunch more code to do what we actually want. So while Python hides some information from the programmer simply by nature of its implementation, we can willfully hide details from the programmer in Common Lisp.

How does the shape of this solution work out? Well we have to know what the code that loop will generate looks like. Fortunately Common Lisp makes this quite easy and if we’re interested, but I’m not going to go into expanding macros and all that jazz. You should take a moment to look it up sometime.

So there you go. I still prefer the Scheme version the most for being so correct and explicit. However, I also appreciate the brevity of the Common Lisp version. It’s quite practical to brush the details under the carpet once the parameters are well understood. I think this is what ultimately makes Common Lisp quite powerful. It has also affected the way I write Python quite a bit lately and I see it as a good thing. I think ESR was quite right in saying that learning Lisp might make you a better programmer.

[tags]lisp python programming[/tags]

Follow Up With Ben: Part 1

So after some discussion on #BikeTO a consensus was made. We want to evidence to answer the question of whether law-abiding cyclists are in the minority. Since I am comfortable with my “inner grandpa,” I decided I could totally take up the torch. Maybe I would find my observations on bike safety to be suffering from the selection bias Ben had noted afterall. As a patron of truth and science I could not let a challenge go unanswered!

For the rest of this week I will be documenting with as much detail as I can, the quantity of cyclicsts who obey the law at intersections vs. the ones who do not. I am counting each unique incident in a table for each day for my one single route. Where I can I will also record anecdotal observations of various intersections of note. However I have not found a satisfactory system whereby I can record the quantities of each incidence at every intersection along the route without sacrificing too much time or safety. Perhaps another run at this can be made if I can devise one.

For now I am recording my observations with the voice-recording feature of my phone and transcribing them after each run of the route. I am only noting unique incidences. This means for each cyclist I only record one event of each type — if they successfully stop several times along the route for a red light it counts as one successful stop for the whole route. Same goes for all the other events that can happen.

The events I’m recording are:

  1. Stops
  2. Signals
  3. Turns

vs.

  1. Runs
  2. Jumps
  3. Crosses

The route I am recording runs from Broadview and Danforth to Bathurst and Bloor. I’ll be riding it twice a day starting today until Friday. I’ll post my results on Friday and leave it up to the community to decide what it means. I might have a few thoughts myself that I’ll follow up with on the following Tuesday or so.

To the streets!

[tags]bikes[/tags]

An Open Letter to Ben Mueller-Heaslip

I’ve been cycling in Toronto year-round for about 10 years. I was one of those cyclists who ran the reds, rode across the pedestrian cross-walk, and had a flagrant disregard for anyone else but myself while riding. I was one of those cyclists until I got hit. Blind-sided by a car pulling into a busy inter-section to make a last-minute left on a yellow light. I was thrown from my saddle and my bike was a contorted mess. I was lucky that I suffered no serious injuries; but it was a few days before I could move again. A friend of mine was not so lucky. I decided that day that my behaviour was incredibly stupid and needed to change.

I don’t think @emmamwoolley is suffering from selection bias. I count the number of cyclists who stop with me at a red light and those who will run it at the opportune moment when all the lights are red (or most of them). More than half will run the light. I’ll start recording my results if anyone doesn’t believe me. Almost everyone will make a left by crossing on the pedestrian cross-walk at an intersection. The majority of cyclists believe that an all-crossing cross-walk is legal to use for cyclists. The people who do understand and obey the rules of the road while riding a bike are in the minority.

The cross-walk issue is a peeve of mine. Cross-walks are meant for pedestrians and I think many cyclists who do not understand the rules of the road do not realize that they are operating a vehicle (as far as the law is concerned). Glibly they whip under the string of yellow Xs or dart around the right side of a car at an intersection into on-coming foot-traffic. I wince every time not because they are breaking a law but because I see the people who have to dodge out of their way. It’s not just a safety issue for cyclists and drivers.

Signalling. Watch each cyclist you come across and note whether they use the proper signals. This week alone I’ve seen just two people use a right-turn signal when turning right at an intersection. I’ve seen the majority of people just turn right, dart across a lane, or simply stop and get off their bike without a signal. It’s not in common usage.

One thing I think we can agree on though is that enforcement is key. I was happy to hear that the police were out in my neighbourhood last month handing out informational flyers to cyclists they caught breaking the law. Presumably the next time they “do a blitz,” they will be handing out tickets. However, I don’t really see isolated events like this stemming the tide of misinformation amongst the cycling community. It’s something at least I guess.

(Worse though, I saw a patrol of officers on bicycles cross at the cross-walk on their bikes. Riding three abreast. Ugh.)

Sadly I think that the only way to improving the situation is self-policing. Toronto isn’t known to have a very out-spoken populace. But the next time you see someone breaking the law, let them know. I know @emmamwoolley has tried this and received only derision; but what else is there but our principles? I would be encouraged to speak up if I saw more people doing the same… it’s the silence that kills us.

Maybe it will all disappear again in a few months when the trend buckles under the changing seasons… but I think action needs to be taken. I iike the fact that more people are choosing to ride their bikes. Yet I find myself as upset by ignorant cyclists endangering themselves and others by their choice to break the laws of the road as the drivers in their cars. And the pedestrians doing their best not to get in the way. It’s been getting worse every year and if something isn’t done soon I might just put up my bicycle for the summer (at least in winter it’s only us die-hards).

[tags]bikes, cycling, toronto, politics[/tags]

You Might Be a Data Scientist…

As I understand it, 80% of a “data scientists” job is massaging a data set into something useful. Many large data sets come in plain text and are almost never very uniform. It requires parsing and fixing and tuning to format it just right for a computer program to utilize the data efficiently. And accurately!

For the slightly neurotic programmers amongst us, this can be a very pleasing experience. I don’t know what part of my brain enjoys this intellectual masochism, but I absolutely get a buzz from finding patterns in data and writing programs that can see, verify, and take actions from them. There have been numerous occasions on the job where I had to noodle around with a large data set to fix some application or save some corrupted data from certain doom. While tedious and lacking in the comforts of determinism; I find it to be a rather… invigorating art (I tend to get quite animated during these sessions much to the chagrin of those who witness them).

So I think I might make a good data scientist. There’s still some math I’m working out (I’m working through my copy of Concrete Mathematics and follow a few courses on MIT OpenCourseWare). Yet if that’s only a small fraction of the job, I don’t think there’s much to worry about. The grungy, tortuous, hard bits… I’m really good at that stuff. And worse, I actually kind of like it in an insane sort of way.

[tags]data scientist, programming[/tags]

Run Length Encoding in Lisp

Run length encoding in Lisp is kind of a trivial problem. Not one that would typically come up in most applications. I came across a treatment while reading the third chapter of ANSI Common Lisp by Paul Graham. I’ve read The Little Schemer and a friend just handed me a copy of ANSI Common Lisp. My style has been greatly influenced by The Little Schemer so when I encountered Paul Graham’s example, my nose crinkled a little and I wondered if I could write a more concise solution. I thought it would be a simple kata.

The “problem” goes like this. You have a list like:

(1 1 1 0 1 0 0 0)

And you want a more concise representation. Paul Graham suggests:

((3 1) 0 1 (3 0))

For example. Here’s Paul Graham’s solution. I hope I understand fair-use correctly:

(defun ansi-compress (lst)
(if (consp lst)
(compr (car lst) 1 (cdr lst))
lst))

(defun compr (elt n lst)
(if (null lst)
(list (n-elts elt n))
(let ((next (car lst)))
(if (eql next elt)
(compr elt (1+ n) (cdr lst))
(cons (n-elts elt n)
(compr next 1 (cdr lst)))))))

(defun n-elts (elt n)
(if (> n 1)
(list n elt)
elt))

And here is my attempt at a more “Little Schemer” style solution:

(defun compress-b (p c lat)
(cond ((null lat) (cons (cons c p) '()))
((equal (first lat) p)
(compress-b (first lat) (1+ c) (rest lat)))
(t (cons (cons c p) (compress-b (first lat) 1 (rest lat))))))

(defun compress (lat)
(cond ((null lat) '())
(t (compress-b (car lat) 1 (cdr lat)))))

As you can see, it’s a few lines shorter. It’s also in many ways, functionally identical. However, my return data structure is a little different. Even if there is a single element, I return a cons. With a little extra code I could probably return an identical data structure. But it will require some way of emulating “n-elts.” However I think I like the uniformity, even if it trades off a tiny, tiny but of space. My function will always return a list of cons cells unless the input list is empty (in which case it will return an empty list).

Of course I’m still learning. I did a trace of the “compress-b” and “compr” functions to test my assumptions. When I first expanded the calls to “compress-b” I was slightly alarmed by the number of returns on the tail end of the function. However, I see that Paul Graham’s solution is the same. The question I have left is whether this is normal and desirable behavior. It seems to me that a single return of the final “chunk” would be better. If there are any Lisp experts in the crowd, would you care to enlighten me?

[tags]lisp, programming[/tags]