I wrote this function for Ten Green Bottles. Not the most useful of functions, but it is another use of preg_replace()'s /e modifier. I found it amusing, anyway. I'm good at amusing myself. The important bit (for a suitable value of "important") is the analysis.

It assumes that it is being passed a string of the form [1-9][0-9]* - that is, decimal numeric representation of a positive integer.

What it does is decrements that positive integer, returning a string representing the result in the same way.

At first glance, one might wonder why I don't just go $n--. PHP would cast the string to an integer, and decrement it. If I used $n in a string context, it would be cast appropriately. Fine so far, but what if the string was '193275689789678975892566'? PHP wouldn't cast that to an integer because that number is too big to fit into an integer; it would be cast into a double instead. When that happens (a) decrementing is nowhere near as accurate, and (b) casting it back into a string sees characters like 'e' and '+' creeping in. For my purposes when writing this function, I wanted (a) accuracy, and (b) strictly decimal numerals.

Before you ask, no, it doesn't check for things like leading zeros, or zero values, or negative integers. It would be easy enough to accommodate those, but it was unnecessary for me at the time. Exercise for the reader.PHP Code:

`function l1($n)`

{

/*1*/ if(!$n{strlen($n)-1})

/*2*/ $n = preg_replace('/(0+)$/e', 'strtr("$1","0","_")', $n);

/*3*/ $n = preg_replace( "/(\d)(_*)$/e", '($1-1)."$2"', $n);

/*4*/ $n = strtr($n, '_', '9');

/*5*/ if(!$n{0})

/*6*/ $n = substr($n,1);

/*7*/ if($n=='') return '0';

/*8*/ return $n;

}

Line-by line:

- This is a test for the sake of efficiency: it succeeds if the last digit of $n is a zero. It's not necessary, but if the last digit of $n is
nota zero, then the second line will be a somewhat expensive no-op.- This line matches all the trailing zeros of $n, and replaces them with underscores. '12300' becomes '123__', '1010011000' becomes '1010011___', and '42' becomes '42'. There are other ways of writing this line with a regexp - preg_replace('/0(?=0*$)/', '_', $n), for example - and it may be interesting to see which of them is quickest.
- This takes the last digit in the string and decrements it: '2' becomes '1', '6' becomes '5', and '9' becomes '8'. Again, this could be done differently. Oh, I do like the /e modifier. I probably use it way too often....
- This line is obvious: replace all underscores with '9's. If you read '_' as "negative one", you can think of lines 3 and 4 as each '_' borrowing from the digit to its left to become '9'. Then again, each '_' is '_' because it was borrowed
fromby the digit to its right (except the last digit which had be summarily decremented, that being the whole point of this function); accountants might like to see line 3 as recording debits, and line 4 as recording credits.- If the original numeral was '1000', we'd now have '0999'. That's not quite right, so we check the first digit, and if it's a zero...
- ...decapitate it.
- Oh, what started out as '1' has now become ''. We return '0' instead of that empty string.
- Exercise for the reader.

[Edit:Ten Years Later... the /e modifier is deprecated as of PHP 5.5 in favour of preg_replace_callback - like eval(), it's way too powerful and dangerous and evil to be allowed out (it's all too easy to inject arbitrary code that will be evaluated).]