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.


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!