Computer Science Canada Meandering through Common Lisp |
Author: | wtd [ Tue Jan 10, 2006 2:32 pm ] | ||||||||||||||||||||||||||||||||||||||||||||
Post subject: | Meandering through Common Lisp | ||||||||||||||||||||||||||||||||||||||||||||
Historical Perspective What good is history when it comes to computer programming? Computer programming is about progress; moving forward; trying new things, right? Absolutely. To make sure we're moving forward, though, we have to understand where computer programming is coming from. If we don't know this, we can't be sure we aren't just falling for marketing propoganda about "the next big thing." Lisp Where does Lisp fit into this? Well, it doesn't get much more historical than computer programming, and fortunately ANSI Common Lisp is still alive and well, so we don't have to learn a dead language. Furthermore, learning Lisp is within the capabilities of mere mortals. Hello, world!
What the...? Well, "format" is a macro. Since the first argument to it is "t" (true) then it prints "Hello, world" to the screen. Let's throw in a variable
So, what's going on here? Well, "setq" is defining a global variable. As a matter of convention, a global variable is surrounded by asterisks. In this case, the variable's value is set to "world". Now, in "format", we have a format string of "~A" where we had previously had "world". This is essentially a placeholder. The value of *name-to-address* then gets inserted where that placeholder is. A local variable But do we need a global variable? Not really. We can keep the name local and not have to worry about that variable outside of the call to "format".
So, what's going on this time? The "let" macro creates bindings of names to values. The rest is pretty straightforward. Replace t with NIL
If we pass NIL to "format", instead of "t", there's no immediate output. Instead we get a string back. In this case we bind it to the "greeting" name, which then gets output. Those nested lets look bad Why can't we just write something like the following?
That's a great idea, but it won't work. Names bound to values in a "let" are't aware of each other, and "greeting" relies on awareness of the value of "name-to-address". Fortunately this is possible. Just not with "let".
Wrapping it up in a function Let's make it possible to reuse this code by making it a "greet" function.
What else can we do with functions Let's say we want the name to be optional, and assume a value of "world"?
What if we want to know if a value was supplied, rather than using the default value?
Now those two calls of "greet" would act differently. The latter example would print the following. The behavior of "when" is reasonably self-explanatory.
What if we want to greet several names?
Here we can see a loop at work in addition to the &rest bit. What if nothing was provided? Well, then "names-to-address" would be NIL.
Conditionals are simple, until they're not In the last example, we saw "if" demonstrated. Let's add to that function so that different names yield different results. If we continued using just "if", we'd end up with a lot of deeplynested parentheses.
Control structures return values
Here we've removed the extraneous calls to "format". Instead the "cond" simply returns a string to write. Taking a quick break The code thus far has probably looked pretty strange with the frequent parentheses, but really, the concepts aren't that odd. Let's translate that last example to Ruby.
Function paramaters, again Let's be able to change the greeting up a bit, based on the arguments. Let's give the parameter a name. For the purposes of this we'll eschew &rest and &optional parameters.
What if I get tired of writing this, and want a default value?
And last but not least, I can make things easier on myself by providing an alternative name for that keyword for use inside the function. Nothing changes in how the function is called.
Recursion Recursion is pretty straightforward. Let's greet a bunch of people. Instead of using &rest, we'll pass the names as a list.
So, what's new here? Well, the "car" and "cdr" functions are new. The "car" function grabs the first element from a list. The "cdr" function gets everything else. So we print out a greeting for the first name, and then greet all of the others. Eventually we end up with an empty list, and an empty list evaluates as false, so with the call to "when" we can terminate the recursion when there's no one left to greet. As long as we're bringing lists into the picture...
What the duck?! The first line looks the same, but then it all changes. Let's first address this piece of code.
Here I've created a lambda function. An anonymous function. It has no name, but it's a perfectly fine function that takes an argument. Now, the "mapcar" function can take this function and apply it to each element in the list of names to address. We could bind that lambda function to a local variable as well if we wanted to.
|
Author: | Hikaru79 [ Tue Jan 10, 2006 4:20 pm ] |
Post subject: | |
And here's a nice free Common Lisp implementation good for beginners using Windows: http://www.cs.utexas.edu/users/novak/gclwin.html |
Author: | wtd [ Wed Jan 11, 2006 2:02 am ] | ||||||||||||||||||||||
Post subject: | |||||||||||||||||||||||
More names... but some structure So far we've been greeting simple names. Let's use more complex names. To accomplish this, we'll create a "name" structure.
It's that simple. Now, we need a function which gets a full name string.
What's going on here? Where did "name-first" and "name-last" come from? These functions were automatically created for us when we created the struct. Now, we need a function which can greet a name struct.
And we need a function that'll greet a bunch of names.
That was pretty easy, wasn't it? Recap
It seems like a lot of parentheses, but when you know how to break it down, it's not that bad. So, how do we actually create some names and greet them?
Greeting simpler names What if we want to be able to just provide the first name, leaving the last name with a default value of an empty string? We can do that. Of course, it also means we'll have to adjust the "full-name" function.
Packages Now the above is all well and good, but we should keep this stuff to ourselves, rather than just adding these names directly to the Lisp environment. Let's create a package.
Now when we test this code, we can write the following.
Or perhaps more concisely...
|
Author: | wtd [ Fri Jan 13, 2006 4:38 pm ] | ||||
Post subject: | |||||
A simple look at CLOS (Common Lisp Object System). Ruby:
CLOS:
|
Author: | wtd [ Sat Jan 14, 2006 6:46 pm ] | ||||||||||||||||||||||||||||
Post subject: | |||||||||||||||||||||||||||||
The Evil Laugh So, I want to print "Muwahahahaha". How should I do this without having to write out a really long string for the "ha" parts? Well, there's the simple answer.
Now, let's say I want it to last a bit longer. Well, copying and pasting the code and changing the number seems silly, so I'll write a function instead.
This still seems a bit cludgy. What if I could do it with a single format? Well, first I'd need all of the "ha"s in a single string. But that's tough, so first let's concentrate on getting a list of them of a certain length.
That will easily give us:
How, how would we join all of these into a single string? Well, we can easily concatenate a bunch of strings.
But there the strings to concatenate are passed as arguments to the function, rather than in one list. Fortunately, we have reduce. The reduce function is immensely useful. Let's look at a simple example.
This gives us 6, but how does it do that? Well, let's break it down step by step.
So, how can we apply this idea to getting a "hahahaha" string? Well, just as we used the + function, we can create a lambda function that will add two strings.
And that will give us "hahahaha".
But let's clean it up by factoring out that lambda function.
We can even make this look a bit better with flet.
Now we can incorporate this back into our function.
It can't be that simple! Lisp originally meant "list processor", and we've seen that Lisp has some pretty funky list handling capabilities. It's a shame, then, that it doesn't have any of those nifty format specifiers that work on lists. Yessir, a real shame... Oh, it does? Nifty.
And simply printing the laugh is kind of inflexible. So I'll just generate a string.
|