Greg Heo

Schwartzian transform in PHP

It's no Perl for sure, but can PHP speak with a Lisp?

Ah, the Schwartzian transform. Is there some way to do this in PHP, that is “speak PHP with a Lisp”?

Use the Schwartz, Luke

Let’s say we have an array of thousands of things, numbers in this case:

$array = array();
for ($i=0; $i < 100000; $i++) {
  $array[] = $i;
}

For some reason, we want to sort this array by each array element’s MD5 hash. Here’s the naïve PHP sort:

function custom_sort($a, $b) {
  return strcmp(md5($a), md5($b));
}
usort($array, 'custom_sort');

In PHP 5.3 and above, we could use a closure. Otherwise, create_function() does almost the same thing very succinctly:

usort($array, create_function('$a,$b', 'return strcmp(md5($a), md5($b));'));

Now that I know how to benchmark PHP, I wanted some timings to compare. Here on my traveling laptop (a trusty old Powerbook G4 1.5GHz) the sort takes about 33 seconds.

Decorate-sort-undecorate

The point of the Schwartzian transform is to perform the expensive operation (the MD5 hash) just once per object. Here’s how it looks in PHP:

// decorate
array_walk($array, create_function('&$v, $k', '$v = array($v, md5($v));'));
// sort
usort($array, create_function('$a,$b', 'return strcmp($a[1], $b[1]);'));
// undecorate
array_walk($array, create_function('&$v, $k', '$v = $v[0];'));

It doesn’t look all nicely chained together as it does in Perl, but it works.

How’s the timing? 15 seconds, a more than 50% reduction!

How it works

PHP’s little-used array_walk() takes the place of Perl’s map: it performs some action on every element in an array. If you pass in an array reference (&$v rather than plain old $v) you can also edit the array elements in-place.

Have a look at the array_walk() documentation for more details.

Useful? Got a better way to do it? Share!