Re:Learn Lisp Without Installing Anything on Your
on
Practical Common Lisp
·
· Score: 1
I went through Why's guide a few months back to learn enough to work my way through the scripts I run across these days. The only thing I came away from the tutorial was a deep sense of wondering why people find the language so great.
I suppose compared to C(++), Perl, VB, or whatever it's actually pretty nice. But compared to languages like OCaml, Oz, and Haskell it's... well... it tries hard -- and it's kind of cute. So I guess that's worth something.
O'Reilly doesn't give a shit anymore. It's been ages since they cared about promoting things that were technologically interesting. All they do anymore is find what's popular and crank out 100 volumes of crap on the same subjects every other publisher is churning out titles for.
Meh... I guess they have to make money somehow though...
Tridge did nothing to linus. It was Larry who took away the license, not tridge.
Tridge was just minding his business doing something he had full legal right to do.
Last time I checked, I have the full legal right to sleep with my best friend's girlfriend. That doesn't mean that a) I should do it, or b) it won't affect my friend.
B) These folks said "naw, it won't happen." It did and now they're blaming others for their own stupid decision.
Not really. This is more of a variant on the "I know she's going to dump me, so I'll break up with her first," self-fulfilling prophecies that a lot of neurotic losers foist upon themselves. Granted, this time it was a bit more of an "I know she's going to dump my buddy sooner or later, so I'll force her to do it now," scenario, but the point is still the same. McVoy didn't pull support on a whim (and there's no evidence he would anytime soon), he was pushed into doing it. It may have been a teeny push, but it was still a push.
So you've never downloaded papers from services like CiteSeer or the IACR eprint archive then?
They tend to have such informative names as 127.pdf , BRICS-RS-04-9.pdf, or if you're real lucky, Monniaux_CSFW12.pdf. Sure, you can rename them all as you download them, but that gets real old real fast...
Great. Now use tab completion to pull up all of the files containing references to, say, Alexander Hamilton. Or all of the PDFs on your system covering, say, "denatational semantics" or "testing equivalence".
I'm guessing it's going to take just a wee bit longer, no?
Maybe it's possible to do that with static typing, but if so it would add so much grief in making sure all the type related stuff fits together as to be barely worth doing. And it is worth doing.
I'm not sure why you think static typing implies grief for this kind of code. Now I'm not sure, as I've only typed about 10 lines of Python in my life, but it looks like you're mapping a list of arrays to a list of the second elements of those arrays. Here's the equivalent code in OCaml:
let bar = List.map (fun x -> x.(1)) foo
Being a bit more general, we can write:
let snds lst = List.map (fun x -> x.(1)) lst
To which OCaml assigns the type:
val snds : 'a array list -> 'a list = <fun>
That is, firsts takes a list of arrays of some unknown type 'a, and maps it into a list of elements of type 'a. No grief, plenty of static typing... Issues like this have nothing to do with static v. dynamic typing.
This: That is to say, given a base value b, an operator, op, and a list [l1, l2, l3,.., ln], we compute ((((l1 op l2) op l3)...) op l(n-1)) op ln).
Should be: That is to say, given a base value b, an operator, op, and a list [l1, l2, l3,.., ln], we compute (((((b op l1) op l2) op l3)...) op l(n-1)) op ln).
And consequently this: (f1 (f2 (f3 (... (fn (id 0))))))), or more correctly, computing the function f x = (f1 (f2 (f3 (... (fn (id x))))))) and applying it to 0
Should be: (id (f1 (f2 (f3 (... (fn 0)))))), or more correctly, computing the function f x = (id (f1 (f2 (f3 (... (fn (id x)))))) and applying it to 0.
Looking for other screw-ups is left as an exercise to the reader...
I suppose this example from your code is one line right? Someone tell me please whether this kind of mess is standard Haskell coding practice. Because to me it looks like theres a bunch of different things going on in that line, that would be better readable spread over a few lines with a bit of indentation, a few comments, sensible variable names etc. Or is it just my non-functional-programming ignorance that makes me think this way?
It's not really all that bad (though I would have put a couple of newlines in there...) let's look at what it does (Word of warning: I'm sure I'm going to screw up on something minor below...).
invert c =
foldl (.) id
[if(testBit c i)then(flip setBit (collength-1-i))else id|i<-[0..collength-1]]
0
Let's deconstruct it, shall we?
invert c = : This creates a function named invert with a parameter c and a body defined by what follows.
foldl (.) id [list description] 0 : foldl "folds up" a list, here that long thing we'll discuss below, using a binary operator, in this case (.) we'll get to that soon, and a base value, here id, and it does so in a left associative manner. That is to say, given a base value b, an operator, op, and a list [l1, l2, l3,.., ln], we compute ((((l1 op l2) op l3)...) op l(n-1)) op ln). We then take the result of this folding and apply it to zero (so we know that the result of the folding is a function that takes an integer argument).
(.): This is the composition operator, it composes two functions. That is to say (f.g) x = f (g x).
id: This is the identity function, so id x = x.
Put it all together (assuming for the moment that the big ol' listy thing is a list of functions, [f1, f2, f3,..., fn]), and we see that we are computing the function (f1 (f2 (f3 (... (fn (id 0)))))), or more correctly, computing the function f x = (f1 (f2 (f3 (... (fn (id x)))))) and applying it to 0 (which in this case represents an all zero bit-vector, the all empty state). It ends up being the same thing...
OK, so what functions are we composing here? Well, lets look at the expression...
[if(testBit c i)then(flip setBit (collength-1-i))else id|i<-[0..collength-1]]
First of all, this is a list comprehension. It essentially says "take the expression to the left of the | and give me one of those for every value on the right of the |", in this case for each value i in the range 0 to collength - 1. So what does the expression on the left say? Well, first, it checks to see if the ith bit of the bit-vector c, (again just another representation of an integer) is set -- that's the if(testBit c i) part. If it is set, then we return the function (flip setBit (collength-1-i)), which we'll explain in a second, and if the ith bit of c is not set, then we just return the identity function. Now, just to finish up, (flip setBit (collength-1-i)) evaluates to a function that sets the (collength-1-i))th bit of the bit-vector that will be passed into the function. We need the flip, because setBit is defined to take the bit-vector as its first argument, then the position of the bit to set. However, we don't have the bit vector yet -- that's what we will be applying our resulting function to -- so we need to "flip" the order of the arguments to setBit. Note that flip f x y = f y x.
So all together, we are creating a function that, given a bitvector c, will start with an empty bit-vector (call it z) and checks if the (collength-1)st bit of c is set. If so, it flips bit 0 of z, otherwise it leaves z alone, cal
Okay... but here's my point: Every single example that shows how elegant Haskell and OCaml are uses lists. The 4-line Quicksort example for Haskell uses lists. All of the code that demonstrates easy reuse of functions and functions taken as arguments uses lists (like how easy it is to implement quite complicated algorithms using only map and filter, for example).
Or perhaps more correctly, "every single example that you've seen". For a real quick one, look at Jason Hickey's Intro to OCaml (pdf) and have a quick peek at his Red/Black tree implementation. Or even cooler (if you're into that sort of thing) is the ever famous One Day Compilers talk.
But what you're saying in #1 above is that in "production," speed-sensitive code, no one is using lists... this would mean that no one is using map, filter, or any other pieces of reusable primitive code. So, they are instead all using mutable data structures... I.e., they are programming with side-effects and loops (random access instead of recursion, even when ever element of an array/list needs to be accessed/processed).
No. What he's saying in that you should use the best data structure for the job. Your best bet would have been to use the Hashtbl module from the standard library, or if you wanted to stay in the purely applicative, the Map module (also in the standard library) would have been loads faster...
So... maybe you can re-write higher-order memoization code using more efficient data structures? I would love to see that code, and I'm sure the OCaml community would benefit from having that in their toolbox.
I don't think I spread any falsehoods. I mean, my experiment was real, and the results are real, and the code is there for people to inspect and try on their own. I also talked in my/. post about OCaml code that is isomorphic to C being fast, but functional code perhaps not being fast.
Yes. And we inspected it, found it to be poorly written, and told you so. The "falsehood" here is that you claim that code written in a functional style is slow, when you really should have said "my code written in a naively functional style is slow". If I fill my gas tank with water, my car sure is slower than walking, therefore all cars are slower than walking... right?
Trust me... I am *dying* to use OCaml or Haskell for real-world programming. I have spent the past month or so exploring these languages and trying to apply them to real programming problems. Especially when shootout results showed that OCaml was sometimes faster than C, and when I discovered that OCaml was much faster than Hasell, I was really starting to think that OCaml was a possibility.
I put a link to tho OCaml mailing lists above. Use it. Ask questions of the list (you may want to start with the beginner's list). They can help you learn the language faster and better than google will.
However, the ONLY reason why I would want to use OCaml is to take advantage of the expressiveness of pure functional programmin
Erm... what the hell does that have to do with "functional languages"?
I know that template meta-programming and Boost allow you to do functional programming in C++. However, it's a layer of ugliness (Template MP) on top of a layer of ugliness (C++), on top of a layer of ugliness (C) and hardly the same thing as actually using a functional language like OCaml, Haskell, or Scheme.
under no circumstances allow yourself to get near functional languages, or you will collapse in sheer incomprehension. There's More Than Perl's Number Of Ways To Do It in those languages.
Huh?
I find functional languages to be much cleaner and more consistently coded in than Perl[1]. Could you provide a couple of examples of this syntax smorgasbord you're talking about?
[1] Of course, there are always examples of arcane and overly complicated ways of doing things, such as this example. But I'm speaking in generalities here...
Regular expressions - This is a common feature in almost all languages these days, but the ease with which they are integrated into Perl syntax makes them more common in Perl programs.
And this is exacerbated when people pull out all sorts of jiu-jitsu to apply regexes to problems that are better and more cleanly handled with a general purpose lexer. But I can see where pulling out a lex-alike doesn't work too well when you're dashing off a script.
Perl is weakly typed, and that frustrates many who are used to strong typing.
Actually this isn't quite right. [confession_mode=on]A couple of weeks back I ranted a bit about this and the whole "a number is a string is a boolean..." nature of Perl and how this implied weak typing. Well after thinking about it for a while, I realized that Perl is actually strongly typed with respect to its (fairly unconventional) type system. The fact that the type system is so unusual is what troubles most people (myself included).[confession_mode=off]
Oh, don't worry, I'm not blaming you or calling you out or anything like that. Hell that column at the least opened a few eyes, and that's a good thing. It's just that the ST term is one of the triggers that set off some variant of a bitter "programmers need to learn more paradigms/languages" rant in me...
And the voices in my head won't go away until I vent it somehow...
I've seen some nasty Python and Java code in my time, so I don't get your point
I'm guessing that his point is that while you can indeed "write Fortran in any language" (and I've got a horrific chunk of OCaml code for generating random provable primes that illustrates this very well), some languages make it harder than others...
Randal also discusses formatting and the nifty "Schwartzian Transform" (Perl's map-sort-map idiom for sorting on almost anything) which was named for him, but not by him.
This is one of the things that's always annoyed me about Perl and its practitioners (well, most programmers, really)...
Given the long history of functional programming (hell, Lisp is closing in on 50 years old), this map/sort/map idiom had to be second nature to any Lisp/Scheme/Haskell/ML/etc. programmer at the time Schwartz wrote about it back in 1996[1]. And the fact that this was such a revelation to the people who read his column that they named the idiom after him just exposes the lack of a diverse programming background for most people. I'm guessing that 50% of the coders out there wouldn't know a map or a fold if it bit them on the ass, and even more wouldn't have a clue as to how to solve something so basic as the missionaries and cannibals problem in a declarative/logical manner (without the help of Dr. Google that is).
Meh... Bitter rant over, I guess...
[1] I could be wrong, I didn't really do much coding before 1995-ish.
How does QS help me pick the ssh window I want when I have 3 of them open to different destinations?
Well, QS may not be able to do that in isolation (though it might, I don't know how myself -- ask Alcor), but used in concert with other apps it is simple. For one thing, if you have a limited number of terminal windows open, then Ctrl, (pause), T, (enter) will bring all terminal windows to the front and you can use Cmd-` to cycle through them to get the one you want. Or if you use something like iTerm (or screen), you can keep your different sessions in tabs, so Ctrl, (pause), I, (enter) will bring iTerm to the front and Cmd-2, for example, will take you to your second session, and your hands never leave the keyboard. Throw in one of these, and you're doubly golden...
Can you cite any of those studies? I'm not saying they don't exist, I'm just saying I've never seen one and would like to.
So, you don't hang out at Llambda The Ultimate then?
Or CTM.
I went through Why's guide a few months back to learn enough to work my way through the scripts I run across these days. The only thing I came away from the tutorial was a deep sense of wondering why people find the language so great.
I suppose compared to C(++), Perl, VB, or whatever it's actually pretty nice. But compared to languages like OCaml, Oz, and Haskell it's... well... it tries hard -- and it's kind of cute. So I guess that's worth something.
O'Reilly doesn't give a shit anymore. It's been ages since they cared about promoting things that were technologically interesting. All they do anymore is find what's popular and crank out 100 volumes of crap on the same subjects every other publisher is churning out titles for.
Meh... I guess they have to make money somehow though...
Tridge did nothing to linus. It was Larry who took away the license, not tridge.
Tridge was just minding his business doing something he had full legal right to do.
Last time I checked, I have the full legal right to sleep with my best friend's girlfriend. That doesn't mean that a) I should do it, or b) it won't affect my friend.
B) These folks said "naw, it won't happen." It did and now they're blaming others for their own stupid decision.
Not really. This is more of a variant on the "I know she's going to dump me, so I'll break up with her first," self-fulfilling prophecies that a lot of neurotic losers foist upon themselves. Granted, this time it was a bit more of an "I know she's going to dump my buddy sooner or later, so I'll force her to do it now," scenario, but the point is still the same. McVoy didn't pull support on a whim (and there's no evidence he would anytime soon), he was pushed into doing it. It may have been a teeny push, but it was still a push.
So you've never downloaded papers from services like CiteSeer or the IACR eprint archive then?
They tend to have such informative names as 127.pdf , BRICS-RS-04-9.pdf, or if you're real lucky, Monniaux_CSFW12.pdf. Sure, you can rename them all as you download them, but that gets real old real fast...
Great. Now use tab completion to pull up all of the files containing references to, say, Alexander Hamilton. Or all of the PDFs on your system covering, say, "denatational semantics" or "testing equivalence".
I'm guessing it's going to take just a wee bit longer, no?
bar = map( lambda x: x[1], foo )
Maybe it's possible to do that with static typing, but if so it would add so much grief in making sure all the type related stuff fits together as to be barely worth doing. And it is worth doing.
I'm not sure why you think static typing implies grief for this kind of code. Now I'm not sure, as I've only typed about 10 lines of Python in my life, but it looks like you're mapping a list of arrays to a list of the second elements of those arrays. Here's the equivalent code in OCaml:Being a bit more general, we can write:To which OCaml assigns the type:That is, firsts takes a list of arrays of some unknown type 'a, and maps it into a list of elements of type 'a. No grief, plenty of static typing... Issues like this have nothing to do with static v. dynamic typing.
What would you have to say about Paul Erdos?
You know... that coffe drinking homeless guy who did a little math.
[my Erdos number=3]
Gerf... I knew I'd screw something up.
.., ln], we compute ((((l1 op l2) op l3)...) op l(n-1)) op ln).
.., ln], we compute
This:
That is to say, given a base value b, an operator, op, and a list [l1, l2, l3,
Should be:
That is to say, given a base value b, an operator, op, and a list [l1, l2, l3,
(((((b op l1) op l2) op l3)...) op l(n-1)) op ln).
And consequently this:
(f1 (f2 (f3 (... (fn (id 0))))))), or more correctly, computing the function f x = (f1 (f2 (f3 (... (fn (id x))))))) and applying it to 0
Should be:
(id (f1 (f2 (f3 (... (fn 0)))))), or more correctly, computing the function
f x = (id (f1 (f2 (f3 (... (fn (id x)))))) and applying it to 0.
Looking for other screw-ups is left as an exercise to the reader...
It's not really all that bad (though I would have put a couple of newlines in there...) let's look at what it does (Word of warning: I'm sure I'm going to screw up on something minor below...).
Let's deconstruct it, shall we?
.., ln], we compute ((((l1 op l2) op l3)...) op l(n-1)) op ln). We then take the result of this folding and apply it to zero (so we know that the result of the folding is a function that takes an integer argument).
..., fn]), and we see that we are computing the function (f1 (f2 (f3 (... (fn (id 0)))))), or more correctly, computing the function f x = (f1 (f2 (f3 (... (fn (id x)))))) and applying it to 0 (which in this case represents an all zero bit-vector, the all empty state). It ends up being the same thing...
invert c = : This creates a function named invert with a parameter c and a body defined by what follows.
foldl (.) id [list description] 0 : foldl "folds up" a list, here that long thing we'll discuss below, using a binary operator, in this case (.) we'll get to that soon, and a base value, here id, and it does so in a left associative manner. That is to say, given a base value b, an operator, op, and a list [l1, l2, l3,
(.): This is the composition operator, it composes two functions. That is to say (f.g) x = f (g x).
id: This is the identity function, so id x = x.
Put it all together (assuming for the moment that the big ol' listy thing is a list of functions, [f1, f2, f3,
OK, so what functions are we composing here? Well, lets look at the expression...
First of all, this is a list comprehension. It essentially says "take the expression to the left of the | and give me one of those for every value on the right of the |", in this case for each value i in the range 0 to collength - 1. So what does the expression on the left say? Well, first, it checks to see if the ith bit of the bit-vector c, (again just another representation of an integer) is set -- that's the if(testBit c i) part. If it is set, then we return the function (flip setBit (collength-1-i)), which we'll explain in a second, and if the ith bit of c is not set, then we just return the identity function. Now, just to finish up, (flip setBit (collength-1-i)) evaluates to a function that sets the (collength-1-i))th bit of the bit-vector that will be passed into the function. We need the flip, because setBit is defined to take the bit-vector as its first argument, then the position of the bit to set. However, we don't have the bit vector yet -- that's what we will be applying our resulting function to -- so we need to "flip" the order of the arguments to setBit. Note that flip f x y = f y x.
So all together, we are creating a function that, given a bitvector c, will start with an empty bit-vector (call it z) and checks if the (collength-1)st bit of c is set. If so, it flips bit 0 of z, otherwise it leaves z alone, cal
And if you weight all tests evenly, rather than giving some more importance than others you get an even different picture...
r k. php?test=all&lang=all&sort=fullcpu&xfullcpu=1&xmem =1&xloc=0&ackermann=1&wc=1&echo=1&fannkuch=0&fasta =0&fibo=1&harmonic=0&heapsort=1&knucleotide=0&mand elbrot=0&matrix=1&nbody=0&nsieve=0&nsievebits=0&ob jinst=1&methcall=1&pidigits=0&random=1®exmatch= 0&revcomp=0&reversefile=1&spellcheck=0&hello=0&mom ents=0&sumcol=0&takfp=1&tcpecho=0&tcprequest=0&tcp stream=0&process=0&message=0&wordfreq=1
Also with cut and paste goodness (mind the injected spaces)...
http://shootout.alioth.debian.org/great/benchma
Okay... but here's my point: Every single example that shows how elegant Haskell and OCaml are uses lists. The 4-line Quicksort example for Haskell uses lists. All of the code that demonstrates easy reuse of functions and functions taken as arguments uses lists (like how easy it is to implement quite complicated algorithms using only map and filter, for example).
/. post about OCaml code that is isomorphic to C being fast, but functional code perhaps not being fast.
Or perhaps more correctly, "every single example that you've seen". For a real quick one, look at Jason Hickey's Intro to OCaml (pdf) and have a quick peek at his Red/Black tree implementation. Or even cooler (if you're into that sort of thing) is the ever famous One Day Compilers talk.
But what you're saying in #1 above is that in "production," speed-sensitive code, no one is using lists... this would mean that no one is using map, filter, or any other pieces of reusable primitive code. So, they are instead all using mutable data structures... I.e., they are programming with side-effects and loops (random access instead of recursion, even when ever element of an array/list needs to be accessed/processed).
No. What he's saying in that you should use the best data structure for the job. Your best bet would have been to use the Hashtbl module from the standard library, or if you wanted to stay in the purely applicative, the Map module (also in the standard library) would have been loads faster...
You are aware that there are more purely functional data structures (pdf) (OCaml implementations) than the list, don't you?
So... maybe you can re-write higher-order memoization code using more efficient data structures? I would love to see that code, and I'm sure the OCaml community would benefit from having that in their toolbox.
Here's a pretty neat example that uses arrays in a naive way, but you could certainly use, say, a map instead... And I'm pretty sure the OCaml community (by which I mean the people who would have helped you improve your code had you asked them) know about things like this.
I don't think I spread any falsehoods. I mean, my experiment was real, and the results are real, and the code is there for people to inspect and try on their own. I also talked in my
Yes. And we inspected it, found it to be poorly written, and told you so. The "falsehood" here is that you claim that code written in a functional style is slow, when you really should have said "my code written in a naively functional style is slow". If I fill my gas tank with water, my car sure is slower than walking, therefore all cars are slower than walking... right?
Trust me... I am *dying* to use OCaml or Haskell for real-world programming. I have spent the past month or so exploring these languages and trying to apply them to real programming problems. Especially when shootout results showed that OCaml was sometimes faster than C, and when I discovered that OCaml was much faster than Hasell, I was really starting to think that OCaml was a possibility.
I put a link to tho OCaml mailing lists above. Use it. Ask questions of the list (you may want to start with the beginner's list). They can help you learn the language faster and better than google will.
However, the ONLY reason why I would want to use OCaml is to take advantage of the expressiveness of pure functional programmin
Um... you do realize that you don't need the
And as already mentioned, there's the revised syntax if you prefer it, or hell... feel free to go plum loco and use camlp4 to write your own syntax.
Erm... what the hell does that have to do with "functional languages"?
I know that template meta-programming and Boost allow you to do functional programming in C++. However, it's a layer of ugliness (Template MP) on top of a layer of ugliness (C++), on top of a layer of ugliness (C) and hardly the same thing as actually using a functional language like OCaml, Haskell, or Scheme.
Would you like to try again?
But, as anybody who writes in Assembler knows there are no data types, just arbitrary bytes and word in memory.
Pssst... assembly languages have got types now...
Pass it on.
under no circumstances allow yourself to get near functional languages, or you will collapse in sheer incomprehension. There's More Than Perl's Number Of Ways To Do It in those languages.
Huh?
I find functional languages to be much cleaner and more consistently coded in than Perl[1]. Could you provide a couple of examples of this syntax smorgasbord you're talking about?
[1] Of course, there are always examples of arcane and overly complicated ways of doing things, such as this example. But I'm speaking in generalities here...
People who use design patterns effectively will write more maintainable code.
Yawn... Insert obligatory response to someone touting the utility of design patterns here.
Regular expressions - This is a common feature in almost all languages these days, but the ease with which they are integrated into Perl syntax makes them more common in Perl programs.
And this is exacerbated when people pull out all sorts of jiu-jitsu to apply regexes to problems that are better and more cleanly handled with a general purpose lexer. But I can see where pulling out a lex-alike doesn't work too well when you're dashing off a script.
Perl is weakly typed, and that frustrates many who are used to strong typing.
Actually this isn't quite right. [confession_mode=on]A couple of weeks back I ranted a bit about this and the whole "a number is a string is a boolean..." nature of Perl and how this implied weak typing. Well after thinking about it for a while, I realized that Perl is actually strongly typed with respect to its (fairly unconventional) type system. The fact that the type system is so unusual is what troubles most people (myself included).[confession_mode=off]
Just trying to set the record straight.
Oh, don't worry, I'm not blaming you or calling you out or anything like that. Hell that column at the least opened a few eyes, and that's a good thing. It's just that the ST term is one of the triggers that set off some variant of a bitter "programmers need to learn more paradigms/languages" rant in me...
And the voices in my head won't go away until I vent it somehow...
I've seen some nasty Python and Java code in my time, so I don't get your point
I'm guessing that his point is that while you can indeed "write Fortran in any language" (and I've got a horrific chunk of OCaml code for generating random provable primes that illustrates this very well), some languages make it harder than others...
Randal also discusses formatting and the nifty "Schwartzian Transform" (Perl's map-sort-map idiom for sorting on almost anything) which was named for him, but not by him.
This is one of the things that's always annoyed me about Perl and its practitioners (well, most programmers, really)...
Given the long history of functional programming (hell, Lisp is closing in on 50 years old), this map/sort/map idiom had to be second nature to any Lisp/Scheme/Haskell/ML/etc. programmer at the time Schwartz wrote about it back in 1996[1]. And the fact that this was such a revelation to the people who read his column that they named the idiom after him just exposes the lack of a diverse programming background for most people. I'm guessing that 50% of the coders out there wouldn't know a map or a fold if it bit them on the ass, and even more wouldn't have a clue as to how to solve something so basic as the missionaries and cannibals problem in a declarative/logical manner (without the help of Dr. Google that is).
Meh... Bitter rant over, I guess...
[1] I could be wrong, I didn't really do much coding before 1995-ish.
How does QS help me pick the ssh window I want when I have 3 of them open to different destinations?
Well, QS may not be able to do that in isolation (though it might, I don't know how myself -- ask Alcor), but used in concert with other apps it is simple. For one thing, if you have a limited number of terminal windows open, then Ctrl, (pause), T, (enter) will bring all terminal windows to the front and you can use Cmd-` to cycle through them to get the one you want. Or if you use something like iTerm (or screen), you can keep your different sessions in tabs, so Ctrl, (pause), I, (enter) will bring iTerm to the front and Cmd-2, for example, will take you to your second session, and your hands never leave the keyboard. Throw in one of these, and you're doubly golden...