Wednesday, February 20, 2008

Yay Reading Week!

Well, I'm in Ottawa for my Reading Week now. Managed to get some reading done, though not as much as I'd have liked, due to my foolish decision to reinstall Planescape: Torment on my computer. Gods, but that is a great game...if ever a video game deserved to win a literary award, Torment is it. How can you not love an RPG where your most important stats are Intelligence and Wisdom, followed by Charisma, when virtually all other (especially Infinity Engine) games are all about STR, CON and DEX? Absolutely everything about that game is great: If you have never played it, you owe it to yourself to do so.


Anyway, I unfortunately have not done much (read: any) work on my parser. I was reading the Dragon Book and Beautiful Code, and I decided it would be more fun to implement the "Top-Down Operator Precedence" parser presented in Beautiful Code in Common Lisp. That's the plan...but I've already so many books to read, and coming to my grandparent's house always seems to end with me having several more. So, it will probably be a while before I get down to some serious programming. Maybe on the train ride back?


My Personal NBL

So, for the—third? Fourth?—time, I decided to try to get back into Factor. It is a really neat language, Slava is a cool and very smart guy...I like the idea of concatenative languages, and learning a new programming paradigm is always a fun thing to do. If I can just get Emacs integration working, and then figure out how to actually do things (I guess I'll go and read all the archives of Slava's and Daniel Ehrenberg's blogs). Wish me luck!


Politics Annoys Me

So, I've had some trouble coming up with something to write in the past few days. I blame this on Noam Chomsky: I started reading Failed States the other day, and it fills me with rage—I can't read it for more than forty minutes at a stretch, or I just get too filled with rage to read. The only things I was considering was how best to overthrow the corrupt regime that currently rules over the United States. However, I really didn't want to start writing about politics on this blog, at least not anti-Bush/US articles, for reasons that I will now elucidate: Other people are stupid. You see, I both take great pleasure from being an elitist snob it all things and believe that the general public is deeply stupid. Therefore, when I have the same opinion of something as the aforementioned "General Public", I feel that I must either conceal that opinion, or assume that this opinion is wrong, and begin seeking some new, untapped, and contrary viewpoint that I can get behind.


Well, that was weird. Just wanted to write something, to maintain momentum. Hopefully the next entry will be more interesting.

Wednesday, February 13, 2008

On Taste

Note: Before reading this entry, you should probably read Paul Graham's essay on the same topic. In fact, you should read all, they're quite good.

So, yesterday I was talking to a friend about how a mutual acquaintance of ours, while being a nice guy, had remarkable poor taste. Most of his favorite authors, television shows, video games, etcetera, were almost uniformly what one would charitably refer to as "trashy", or less charitably refer to as "bad".

Well, since you read Paul Graham's article I pointed you to at the beginning (you're so studious!), you understand the premise that no, he isn't entitled to his opinion in this case: Some things just aren't as good as others, and liking those things equates to bad taste. So, my question is, can you learn better taste? I'm sure that you could learn to like things that are better—if we took away some hypothetical person's bad music, movies and books, and had them just listen to Tchikovsky, watch Citizen Kane, and read Shakespeare (or whatever we'd agree upon is good, for some value of "good"), they'd probably get used to it eventually—but does that necessarily mean one will have better taste? Would one still seek out better materials, or would they remain unable to tell the difference in quality between Dan Brown and William Faulkner?

This is not a trivial problem.

I'm really not too sure what the answer is. In fact, as writing this, I realized how poorly defined "taste" is: When giving my examples of "good" things to force feed someone, I knew full well that people would have issue with some of the items. How can we objectively assess how good something like art/entertainment is? I do agree with Paul's thesis that there is such a thing as good taste or bad taste, but I think that one can only really assess it by what they create, since judging what they like is too hard, and will probably be reflected in their creations anyway.

Of course, all this does is move the problem from defining judging if what they like is good to judging if what they've made is good. However, I would intuitively say that this is a more tractable problem: If they've made something elegant, that works well, and is appealing to some statistically significant (for some value of "significant") audience. Essentially, I think that the popularity of the creation is positively related to one's taste, if not directly proportional. Then again, if that's a valid metric, why can't we use it to assess what people like (I think I'm starting to tread into a Zen and the Art of Motorcycle Maintenance-esque "what is Quality" question, which is an established Hard Problem...)? Is there some fundamental difference in judging someone by what they make, rather than by what they like?

Again, not a trivial problem

That being said, I think there is a difference. When you create something, you have to actively think about how this system is going to be designed: You have to consider all aspects of it, while when one is the passive recipient of something, you typically only notice the most superficial level. Therefore, when choosing what you like, it will probably be based on popularity first, since that is most likely the criteria you encounter. On the other hand, if you create something, then you see every aspect of it: At that point, it's being good or bad is more dependent on your taste, as it controls every aspect of what is being designed.

Well, not sure if that made sense to anyone else, but whatever. No-one reads this anyway (except you, of course. Thanks!).

In any case, my original question is still unanswered: Is it possible for someone to learn good taste? With my new pseudo-definition of taste, I would now say yes, it is. You can improve your taste (at least as it pertains to a particular domain) by good designing things. Determining how to design good things is left as an exercise to the reader.

Next time!

So, with my Algorithm X implementation done, I probably should design the sudoku solver that it was originally for...but, know what I think will be more fun? Writing a lexer/parser for ECMAscript 4. Hopefully, this will turn into the front-end for the compiler I keep planning on writing...

Anyway, laughs aplenty sure to follow!

Tuesday, February 12, 2008

The Algorithm Saga Continues

Last time, I talked about some problems that I encountered while trying to write my implementation of Algorithm X, promising at the end to give it's current state next time. That, of course, was a shameless delaying tactic, while I tried to get the bugs out. However, about an hour ago, I finally ironed out the problems I'd been having, and it seems to be working now. I'll write unit tests soon, I promise. Anyway, this time I want to talk a bit more about the problems I had, and then hopefully get to what worked.

A Brief Segue Into Things That Annoy Me

As I mentioned in the previous entry, I had to put in what I'm referring to as a “tag” for each row; an alphabetical character which doesn't change as the row index does, to allow me to reference in a meaningful way the rows that are chosen while recursing on sub-matrices of the original matrix. However, I really don't like the way I did it. It's an ugly kludge, and I really want to find a better way to do it. As it is, having the cons pairs as the values of the hash makes the sparse-matrix class far less general, and as I ranted about last time, if there's one thing that drives me crazy, it's specific solutions. What if I want to use that implementation of sparse matrices in something else? I'm carrying around these characters that are completely useless to me, just taking up space. It's also forced me to change the implementation of various methods in non-obvious ways. If anyone actually read this, I'd solicit suggestions for a better method...

We Now Return to Your Regularly Scheduled Ravings

So, one thing that I learned in the course of implementing this was how to make asdf work. All my previous common lisp programs had been fairly small, but I figured that I should probably learn how make require-able packages. Unfortunately, most of the tutorials I found were on how to make one's package asdf-installable. Luckily, the fine people on #lisp were able to give me a hand, and everything went smoothly.

So, now the moment you've all been waiting for: My algorithm X implementation in all it's glory

(defun algorithm-x-solve (matrix)
  "Given a sparse-matrix, determine the exact spanning set, using Knuth's Algorithm X.  Returns a
list of rows which compose an exact spanning set"
  (labels (        
           ;; Helper function that gives a list of the cols of the matrix, sorted by least filled to most
           (get-min-cols (mat)
             (sort (range 1 (cols mat)) (lambda (a b) (< (length (rows-filled a mat)) (length (rows-filled b mat))))))
           
           ;; Function to perform the main recursive step of the solver
           (rec (r mat acc)
             
             ;; Find the first column with the least rows filled (Step 2)
             (let* ((col (car (get-min-cols mat)))
                    (cur-row (get-tag r mat))
                    (poss-rows (mapcar #'(lambda (r) (get-tag r mat)) (rows-filled col mat))))
               
               ;; Perform step 5
               (let* ((rows-to-drop nil)
                      (cols-to-drop
                       (loop for j from 1 to (cols mat)
                          when (= (get-entry r j mat) 1) collect j
                          when (= (get-entry r j mat) 1)
                          do (loop for i from 1 to (rows mat)
                                when (= (get-entry i j mat) 1)
                                do (unless (member (get-tag i mat) rows-to-drop)
                                     (push (get-tag i mat) rows-to-drop))))))
                 
                 (dolist (rw (sort rows-to-drop #'char>))
                   (setf mat (drop-by-tag rw mat)))
                 
                 (dolist (cl (reverse cols-to-drop))
                   (setf mat (drop-col cl mat))))
               
               (cond
                 ((matrix-empty-p mat)
                  (cons cur-row acc)) ;; Done, (Step 1) return the rows of the solution
                 ((null (rows-filled (car (get-min-cols mat)) mat))
                  nil) ;; A column is empty, failed
                 (t ;; Else, find a row (Step 3) 
                  (loop for row in (rows-filled (car (get-min-cols mat)) mat)
                     for ret = (rec row (copy-matrix mat) (cons cur-row acc)) then
                       (rec row (copy-matrix mat) (cons cur-row acc))
                     until ret finally (return ret)))
                 )))
           )
    
    (let ((poss-rows (rows-filled (car (get-min-cols matrix)) matrix)))
      (loop for row in poss-rows
         for ret = (rec row (copy-matrix matrix) nil) then (rec row (copy-matrix matrix) nil)
         until ret finally (return (or ret 'failure))))))
 
Yeah...it is pretty ugly. The bit that took the longest to get right was the "Perform Step 5" segment. Initially, I was dropping the rows and columns while looping across them which (obviously, in retrospect) caused things to go wrong, as the index variable of the loop was now off. Frankly, it was embarrassing how long it took to get that right.

Well, there it is. Next, I guess I'll try make a sudoku solver with that. And wither then? I'm thinking:

  1. Displaying the puzzles with cl-pdf or Vecto
  2. Generating sudoku puzzles
  3. Writing a compiler of some sort
But we'll see...

Monday, February 11, 2008

On Algorithm X, Sudoku, Common Lisp, and Pants

So, as I mentioned in my previous post, my current proramming project is trying to implement Knuth's Algorithm X in Common Lisp. I was initially going to write a sudoku solver, but realized that I was basically copying Peter Norvig's solver, so I decided to try and generalize. You see, I am very anal about having general solutions to problems. An example: I recently acquired a pair of pants with a nifty little pocket on the side of the leg, between the hip pocket and the side pocket. Now, I always need more pockets, as I tend to carry tons of random things with me. However, I never use that pocket, although it would be idea for my keys and phone. Why? Because it isn't a general solution to the problem of carrying my stuff: I'd need to have that pocket on all my pants before I started using it. Right, I'm crazy.

Anyway, enough about pants. While looking at the wikipedia article on the mathematics of sudoku, I came across the intriguing fact that sudoku (along with n-queens, among others) can be reduced to the problem of finding an exact covering set for a sparse matrix, a problem which Donald Knuth developed an algorithm to solve. Without further ado, I decided that this was to be my next programming challenge.

(Note: Before reading further, you might want to at least glance at the Wikipedia article on the algorithm)

As I began considering the program I was to write, I decided to first make an implementation of sparse matrices. I'm sure that there are other Common Lisp packages for that far better than mine, but I figured that having control over the representation of the matrix would make it much easier to write the solver. I'm glad I did too, as I've since had to change my matrix implementation several times. Initially, I had the matrix as a class with slots for the number of rows and columns, and a hash table keyed on the (1-indexed) rows, with the values being a list of integers corresponding to the occupied columns in that row. However, some time later, I realized a fundamental flaw: The way I had designed the algorithm, I had no way of knowing which row of the original matrix the reduced rows I was choosing in the recursive stage corresponded to. The quick hack that I'm currently using was to change the values stored in the hash table to cons pairs of (<occupied columns list> . <id character>). The idea is that all the places (which were few, by design) that were directly accessing the hash would just have the (gethash …) replaced with (car (gethash …)).

Of course, there ended up being other places where things went weird, such as when mapping over the hash, and one annoying bug that took me a while to track down: One of the common operations the algorithm performs is dropping rows and columns from the matrix. Of course, we don't want to trash the original matrix, so we make a copy of the matrix to pass to the recursive stage. Okay, but to copy the matrix, we need to copy the hash table that maps the rows to occupied columns. Okay, we can do that too, thanks to a helpful soul on #lisp:

(defun copy-hash-table (ht)
  "Shallow hashtable copy."
  (let ((nht (make-hash-table :size (hash-table-size ht))))
    (maphash (lambda (key value)
               (setf (gethash key nht) (if (listp value) (copy-list value) value)))
             ht)
    nht))
The problem was that, since the value was now a list, I needed to copy the list, otherwise the destructive drop-* operations would alter the original matrix. Once I figured out that this was the problem, the solution was a simple replacing of the setf in the code above with (setf (gethash key nht) (if (listp value) (copy-list value) value)). You may (fairly) point out that I don't need the check if value is a list, since it always will be. Remember what I said at the beginning about specific solutions though? Even that makes me unhappy…

Well, it's after 1:00 AM now, so I think it's time to wrap this up. Tune it next time to learn the current state of my implementation!

I guess I need to figure out how to get syntax-highlighting with Blogger...Let's see if I can get it working for the next post! Update:Looks like I've got emacs htmlize-region to play nicely with Blogger, huzzah

A New Beginning?

Well, after creating this blog some time ago, with the intention of never using it, I think I may have changed my mind. I have no idea how many times I've decided to try writing and then never come back to it, but whatever. We'll see how it goes.

So, introduction. I'm James Cash, EngSci 1T1 at ye olde University of Toronto. I like programming. Alot. Especially in obscure languages. Currently, I'm using Common Lisp mostly, but I'll probably end up using Python in the job I'm hoping to get. My friend David Wolever hooked me up with Greg Wilson, who is quite a nice guy. High hopes there. As long as I don't have to use C++, I'm happy. Quite frankly though, the notion that someone would pay me to program boggles the mind, so I'll be glad regardless.

So, Common Lisp...obvious, I'm also a big fan of Emacs (both of those can be traced back to my reading of Steve Yegge). I used to use Scheme a fair bit, mostly Gambit Scheme, but moved to Common Lisp when I realized that it's alot more fun to write programs than libraries. I've played around with Haskell, which was tons of fun, quite beautiful, but I do enjoy dynamic typing so. Currently, I'm playing around with implementing Knuth's Algorithm X in CL, which is fun, albeit a bit of a challenge. Planning to make a sudoku solver out of that when it's complete.

I bought the Dragon Book a while ago, but haven't been able to get very far in it (EngSci does not lend itself very well to hobbies). I very much enjoy writing parsers, so I'm planning on writing some sort of compiler after I finish with the Algorithm X dealie. Thinking of doing the front-end it OCaml, which is fun to write parsers in, maybe back-end in Lisp. Probably try to write an ECMAScript 4 to (Assembly | Some sort of bytecode). Which reminds me, I need to read up on Parrot or JVM bytecode. Thank Cthulhu for reading week...

Well, there's a brief introduction, to the no-one who will read this. Hopefully I'll actually update this. Thanks for reading!