August 2009 Archives

My Ruby Book on your iPhone. Cheap!

| 3 Comments

O'Reilly has just released The Ruby Programming Language as a standalone iphone app! Looks like you can get your hands on it for just $5 (The cover price of the print edition is $40.) I don't have an iphone, but if you do, I'd love to hear how the book looks and works for you in that format.

At the same time, O'Reilly has dropped the ebook price for other readers from $30 to $10. Amazon's Kindle price is still $18 today. I don't know if that is going to go down or not. (Update 8/27: the kindle price is now $8.)

I'm not sure how I feel about O'Reilly using my book for their marketing experiments, but it's certainly a good deal for readers. So I ask for your help in making up for the reduced price with a massive increase in sales volume! :-)

And if I can engage in some more self-promotion: reviewers on Amazon think that this is the best book I've ever written. The 26 reviewers have all given it a 5-star rating, and the most recent review goes over the top and calls it "the best ever written programming language book". Wow. You know you want it on your iPhone!

Update: my editor says that he thinks this new ebook price is a temporary sale.

Function objects in JavaScript are callable: you can invoke them. Other, non-function objects are allowed to be callable, however. Host objects in IE (things like Window.alert()) are callable, but are not native function objects. (Other browsers implement DOM methods as true native function objects). Also, a number of browsers have followed Firefox's lead in making RegExp objects callable even though they are not functions. Although you can invoke any callable object like a function, the difference is that callable objects don't have function methods call() and apply() (and bind() in ECMAScript 5).

Today, the typeof operator returns "function" for true function objects, and returns "object" for IE's callable host objects. Most browsers return "object" for callable RegExps, but Safari returns "function", and Google Chrome is likely to follow Safari's lead on this. If you want to be sure that something is a true function (and not a regexp) you can use something like this:

function isFunction(x) { 
    return Object.prototype.toString.call(x) === "[object Function]";
}

There is not today a reliable way to write an isCallable() function, however.

Things change in the ECMAScript 5 specification. The typeof operator is required to return "function" for any native or host object that is callable. When IE implements the spec, we can expect "typeof window.alert" to evaluate to "function". The problem is that browsers like Firefox and Opera that have callable regexps are unlikely to implement the spec: the typeof operator on a regular expression will continue to return "object" for those browsers. The committee writing the specification was aware of this problem, but ran out of time to fix it.

So today typeof x === "function" is close to an isFunction() test, but it fails for regular expressions in some browsers. In ECMAScript 5, typeof will be close to an isCallable() test, but it will fail for regular expressions in some other browsers.

Fortunately, the isFunction() test above should continue to work in ECMAScript 5, and
there is a way to write a reliable isCallable() function in ECMAScript 5. It relies on the fact that the Array.prototype.forEach() method checks its argument for callability even when invoked on an empty array. (So this isCallable() function assumes that browser vendors implement the Array.forEach() method as specified.) Here it is:

Object.isCallable = function(o) {
    // Array.prototype.forEach throws TypeError for non-callable arguments
    try {
        [].forEach(o);  // o will never be invoked, but it will be tested for callabilty
        return true;
    } catch (x) {
        if (x instanceof TypeError) return false;
        else throw x;
    }
};

I was recently writing some documentation for the Array.forEach() method (part of ES5, but most browsers other than IE support it now) and worrying about the fact that there is no clean way to terminate the iteration prematurely. Nothing like the break statement, that is. If you really want to get out of the loop, the function you pass to forEach() has to throw something. And the forEach() method won't catch it for you, so you've got to write your own try block.

Then, when working with the new array predicate method Array.some(), I realized that we don't have to think of it as a predicate method. If we ignore the return value, it is an iterator method that works just like Array.forEach() except that if your function returns true (or any truthy value) then the loop terminates. So inside of the function you pass, a plain return statement with no value is like using a continue statement. And "return true" is like a break statement, causing the loop to terminate. The implicit return that occurs at the end of the function body returns undefined, which is like returning false--it keeps the loop going.

The Array.every() method is not so useful this way: you have to explicitly return a truthy value to keep the loop going, so an implicit return at the end of the function body would act like a break statement.

The problem I see with using some() in this way is a stylistic one: the name really doesn't look like the name of an iterator the way that "each" and "every" do.

Good algorithms are better than clever code

| 5 Comments

Yesterday, I posted an entry about a clever way to implement string multiplication in JavaScript using Array.prototype.join()

In comments, redraiment challenged me, suggesting that an implementation based on string doubling would be more efficient. Sure, I thought, for really large values of n, but surely for small n, using the native join() method would be better, wouldn't it?

It turns out that writing a good algorithm is better than being overly clever (at least in these days of really good JIT interpreters). Here's my new string multiplication code:

String.prototype.times = function(n) {
    var s = this, total = "";
    while(n > 0) {
	if (n % 2 == 1) total += s;
	if (n == 1) break;
	s += s;
	n = n>>1;
    }
    return total;
};

By my simple benchmarks, this implementation is significantly faster than using join(), even when only multiplying by 1 or 2. I've tested it in Firefox 3.5, IE 8 and Safari 3.

String Multiplication in JavaScript

| 13 Comments

In Ruby, the "*" operator used with a string on the left and a number on the right does string repetition. "Ruby"*2 evaluates to "RubyRuby", for example. This is only occasionally useful (when creating lines of hyphens for ASCII tables, for example) but it seems kind of neat. And it sure beats having to write a loop and concatenate n copies of a string one at a time--that just seems really inefficient.

I just realized that there is a clever way to implement string multiplication in JavaScript:

String.prototype.times = function(n) {
    return Array.prototype.join.call({length:n+1}, this);
};

"js".times(5) // => "jsjsjsjsjs"

This method takes advantage of the behavior of the Array.join() method for arrays that have undefined elements. But it doesn't even bother creating an array with n+1 undefined elements. It fakes it out using and object with a length property and relies on the fact that Array.prototype.join() is defined generically. Because this object isn't an array, we can't invoke join() directly, but have to go through the prototype and use call(). Here's a simpler version that might be just as efficient:

String.prototype.times = function(n) { return (new Array(n+1)).join(this);};

When you call the Array() constructor with a single numeric argument, it just sets the length of the returned array, and doesn't actually create any elements for the array.

I've only tested these in Firefox. I'm assuming that either is more efficient than anything that involves an actual loop, but I haven't run any benchmarks.

Books

Comprehensive coverage of Ruby 1.8 and 1.9

"The New Most Important Ruby Book"
Peter Cooper,
rubyinside.com

Completely updated for Ajax and Web 2.0

"A must-have reference"
Brendan Eich,
creator of JavaScript

The classic Java quick-reference

Advertising

Pages

Hosted By

Powered by Movable Type 4.21-en