42e1363a474387e41af8f8219797e112

I had need for an iterative loop that wasn't based on integers and needed it to stay within the bounds regardless of how many rules are defined. It works nicely with an optional callback function which can terminate the entire loop if req'd.

The code works but I'm not very happy with having to loop through the arrays so many times, esp just to determine whether all rules have been met. Any help would be appreciated.

<?php
/*
 * This is a test of a rules based iterative algorithm...
 *
 */


$rules = array();
/*
 * type = # == numeric, @ = alpha
 * lower = lower boundary
 * upper = upper boundary
 *
 */
$rules[] = array('type'=>'#', 'lower'=>0, 'upper'=>5);
// If a rule is running over characters and it is case-sensitive, 
// then we need to define the sequence to have the upper case letters first
$rules[] = array('type'=>'@', 'lower'=>'A', 'upper'=>'D');
$rules[] = array('type'=>'#', 'lower'=>1, 'upper'=>3);
$rules[] = array('type'=>'@', 'lower'=>'A', 'upper'=>'B');

iterate('/%0%%1%-%2%-%3%/', $rules, "mycall");

function mycall($data, &$rules) {
	echo "<strong>" .$data . "</strong><br />";
	return true;
}

function iterate($format, &$rules, $call = null) {

	updateCounts(array_reverse($rules, true), $rules);


	$vars = array();
	foreach ($rules as $key => $val) {
		$vars["%$key%"] = $val['cur'];
	}
	
	if (!is_null($call)) {
		if (!call_user_func($call, str_replace(array_keys($vars), array_values($vars), $format), $rules)) {
			return;
		}
	} else {
		echo str_replace(array_keys($vars), array_values($vars), $format) . "<br />";
	}
	
	//keep working if we aren't done.
	foreach ($rules as $key => $val) {
		if (!($val['cur'] == $val['upper'])) {
			$iterate = true;
		}
	}
	if ($iterate) {
		iterate($format, $rules, $call);
	}
}

function updateCounts($arr, &$rules) {
	foreach ($arr as $key => $val) {
		if (isset($val['cur'])) {
			if ($val['cur'] == $val['upper']) {
				// Reset to lower, continue
				$rules[$key]['cur'] = $rules[$key]['lower'];
			} else {
				// Add One, exit function
				switch ($rules[$key]['type']) {
					case '#':
						$rules[$key]['cur']++;
						break;
					case '@':
						$chr = ord($rules[$key]['cur']);
						//need to jump over none alphabetic chars here...
						if ($chr == 90) {
							$chr = 97;
						} else {
							$chr++;
						}
						$rules[$key]['cur'] = chr($chr);
						break;
					default:
						die('Unknown Rule Type');
				}
				return;
			}
		} else {
			$rules[$key]['cur'] = $rules[$key]['lower'];
		}
	}
}
?>

Refactorings

No refactoring yet !

D8941b726b5851b8ebad73f458e58268

mikenz.geek.nz

July 4, 2008, July 04, 2008 09:44, permalink

No rating. Login to rate!

Use a while loop instead of recursion. Original code was giving errors due to the nesting depth.

<?php
/*
 * This is a test of a rules based iterative algorithm...
 *
 */


$rules = array();
/*
 * type = # == numeric, @ = alpha
 * lower = lower boundary
 * upper = upper boundary
 *
 */
$rules[] = array('type'=>'#', 'lower'=>0, 'upper'=>5);
// If a rule is running over characters and it is case-sensitive,
// then we need to define the sequence to have the upper case letters first
$rules[] = array('type'=>'@', 'lower'=>'A', 'upper'=>'D');
$rules[] = array('type'=>'#', 'lower'=>1, 'upper'=>3);
$rules[] = array('type'=>'@', 'lower'=>'A', 'upper'=>'B');

iterate('/%0%%1%-%2%-%3%/', $rules, "mycall");

function mycall($data, &$rules) {
    echo "<strong>" .$data . "</strong><br />";
    return true;
}

function iterate($format, &$rules, $call = null) {
    $continue = true;
    while ($continue) {
        updateCounts(array_reverse($rules, true), $rules);

        /* Setup initial values */
        $vars = array();
        $continue = false;
        foreach ($rules as $key => $val) {
            $vars["%$key%"] = $val['cur'];
            if (!($val['cur'] == $val['upper'])) {
                $continue = true;
            }
        }

        if (!is_null($call)) {
            if (!call_user_func($call, str_replace(array_keys($vars), array_values($vars), $format), $rules)) {
                return;
            }
        } else {
            echo str_replace(array_keys($vars), array_values($vars), $format) . "<br />";
        }
    }
}

function updateCounts($arr, &$rules) {
    foreach ($arr as $key => $val) {
        if (!isset($val['cur'])) {
            /* No current value, start with the lower one */
            $rules[$key]['cur'] = $rules[$key]['lower'];
            continue;
        }

        if ($val['cur'] == $val['upper']) {
            /* Reset to lower, continue */
            $rules[$key]['cur'] = $rules[$key]['lower'];
            continue;
        }

        /* Add One, exit function */
        switch ($rules[$key]['type']) {
            case '#':
                $rules[$key]['cur']++;
                break;
            case '@':
                $chr = ord($rules[$key]['cur']);
                //need to jump over none alphabetic chars here...
                if ($chr == 90) {
                    $chr = 97;
                } else {
                    $chr++;
                }
                $rules[$key]['cur'] = chr($chr);
                break;
            default:
                die('Unknown Rule Type');
        }
        return true;
    }
}

Your refactoring





Format Copy from initial code

or Cancel