Create a loop for a specific number of iterations, *without* a variable?

You see, I’ve been thinking.

Is there a way to create a loop, with a predetermined number of iterations, without creating a variable in global space?

I’m sure this can be done in 5.3, but what about prior to this?

Something like this would be nice…


while(loop(5)){
  
}

Before everyone puts their thinking caps on, may I ask… why?

You may not.

I jest, of course.

Well, it seems a little strange to need a (disposable) variable for the sole purpose of a loop.

Maybe I’ve had too much time to think about this, what are your feelings?

Something like this? Untested, might not work at all :slight_smile:


while(Loop::decreaseFrom(10)) {
    // do stuff
}

class Loop {
    
    static private $counter = false;
    
    static function decreaseFrom($number) {
        if(self::$counter===false) {
            self::$counter = $number;
        }
        self::$number = self::$number - 1;
        return self::$number > 0;
    }
}


:smiley:

That wouldn’t work for nested loops though, also untested. :stuck_out_tongue:

How can this be achieved in 5.3?

Somehow, somewhere the loop count information needs to be handled. A while loop, I assume and have so heard, is faster than a for loop per se since it does not intrinsically have that overhead. What you are describing is a for loop, and if you want to achieve that with a while loop you need to add functionality to the while construct which will give it that overhead, as well. And I really doubt that something rather whimful like that would be better optimized than the established for loops already are when it comes to achieving that task.

Personally I practically never use for loops though.

A closure.


function() {
  $a = 1;
  while($a < 11) {
    echo $a.'<br>';
  }
}();

$loop();


Anthony, I think there’s no way other than to use a function in some way. Why can’t you just check to see if the var isset and then unset it when you’re done with it if your worried about impacting other code?

Well you could define a cleaner loop construct:


class Loop {
    public function __construct(Closure $function, $num) {
	$c = 0;

	while ($c < $num) {
		$function();
		$c++;
	}
    }

}


new Loop(function() {
	echo 'looped<br />';
}, 5);

Of course it’s horribly pointless and just abstracts the problem away.

I can think of a convoluted method involving class_alias and count(get_defined_classes()) but all it would do is move the count to a stupid place. :stuck_out_tongue:

You need some way of keeping track of the number of iterations. Even if you didn’t, PHP would need to internally.

Naturally. I think what Anthony is gunning at is he’s got some code he wants to be able to run without affecting or being affected by other PHP scripts. The only way to be sure of this is to avoid making any global vars and using a namespace or psuedo namespaces.

One thing I find irksome about PHP is that namespaces have no effect on the variables - it applies only to functions and constants. This works differently from all other languages that use namespaces and is flat out a bad design decision by the PHP lead team in my opinion.

But Anthony was also asking how to do this without PHP 5.3. As far as I know these are the only two options.

  1. Use a static class as a psuedo-namespace.

class myVars {
  public static $counter = 0;
}

myVars::$counter = 0;

while (myVars::$counter < 11) {
  //do something
  myVars::$counter++
}

  1. Get creative with create_function and call_user_function.

call_user_func(create_function(null, '$a = 0; while($a < 11) {  }'));

This will get messy fast. Using file_get_contents to get the code from a file will ease the pain somewhat, but it will need to be tag less because of the odd manner of its inclusion. That will play head games with most IDE’s.


call_user_func(create_function(null, file_get_contents('code.php')));

I agree entirely but, even after a good nights sleep, I still find it strange that there isn’t something native to do this. How this would be implemented though is another issue, but after a quick scan of some other languages none seem to support this either.

Having to declare a variable ‘just’ for the purposes of creating a loop seems wrong to me, almost messy.

“That’s just the way it is” is not going to appease me at this point. :slight_smile: Should this be a userland variable, I personally don’t think so.

Nice, it seems Ruby has this feature.

3.times { puts "Hi!" }

Thanks Kyberfabrikken for the heads-up.

I’d be damned if I couldn’t fix it.

Not taking no for an answer only makes it a nice exercise, you should not use this in production. Now, I still highly doubt the practical benefit of this functionality: though I loop using $i or $n, I tend to use $i and $n inside the loop a lot.


<?php

function loop( $n ) {
	static $__loops = array( ); /** Used to diffentiate loops from each other. */
	$trace = debug_backtrace( );
	$identifier = sprintf( '%s-%d', $trace[0]['file'], $trace[0]['line'] ); /** Identify the current loop */
	$__loops[$identifier] = isset( $__loops[$identifier] ) ? $__loops[$identifier] : 0;
	if( $n >= ( ++ $__loops[$identifier] ) ) {
		return true;
	}
	unset( $__loops[$identifier] );
	return false;
}

while( loop( 3 ) ) {
	echo "Loop.\
";
	while( loop( 2 ) ) {
		echo "\	Inner loop.\
";
		while( loop( 1 ) ) {
			echo "\	\	Inner-inner loop\
";
		}
	}
}

For variation, and inspired by Kyberfabrikken’s tweet:


<?php
/**
 * Kyberfabrikken tweeted: 3.times { puts "yay!" }
 * Here goes.
 */
class Int {
	protected $n;

	public function __construct( $n ) {
		$this->n = $n;
	}

	function times( Closure $closure ) {
		$i = 0;
		while( $i < $this->n ) {
			$closure( ( $i ++ ) );
		}
	}
}

function int( $n ) {
	return new Int( $n );
}

int( 3 )->times( function( ) {
	echo "Loop\
";
} );

No, I’m not drunk, I’m simply waiting till the clock hits midnight :smiley:

You’ve had too much time to think about it. (:

I came up with an approach similar to webaddictz’s loop() function (though, his coding style is painful to read); using debug_backtrace() to create an identifier (mine also took the loop number into account) for each individual loop.

Bravo, but…

<?php
while(loop(3)){ while(loop(2)){  } }
?>

:stuck_out_tongue:


<?php
$var = 'foo';

int( 3 )->times( function( ) {
    #cannot access $var
} );

Liar.

Oh no, that’s not my usual coding style: just writing the code that way to make absolutely sure everyone knows that it’s rubbish and should never ever be used :smiley: If I were to write the code in more than five minutes, it would probably turn up like this:


<?php
function loop( $times ) {
	/** Used to diffentiate loops from each other. */
	static $__loops = array( ); 
	$trace = debug_backtrace( );

	/** Identify the current loop */
	$identifier = $trace[0]['file'] . '-' . $trace[0]['line']; 
	
	/** Initiate the loop, or increment. */
	$__loops[$identifier] = isset( $__loops[$identifier] ) ? ( $__loops[$identifier] + 1 ) : 0;

	/** Limit not reached yet? */
	if( $__loops[$identifier] < $times ) {
		return true;
	}

	/** Limit reached **/
	unset( $__loops[$identifier] );
	return false;
}

while( loop( 2 ) ) {
	echo "Outer.\
";
	while( loop( 3 ) ) {
		echo "Inner.\
";
	}
}

That said: I’ve written worse pieces of code! :slight_smile:

How would you take the loop number into account? I can’t seem to differentiate between the outer and inner loop, apart from using debug_backtrace. That doesn’t provide the number of loops though, nor the column it’s called from? I’d love to see your hacking, even if it is only to critisise on your coding style :stuck_out_tongue:

Figured you might catch up on that. :slight_smile: Still, I think the effort was nice.


<?php
$var = 'it could.';

int( 3 )->times( function( ) {
	global $var;
	echo "Obviously, $var\
";
});

You guys don’t really get the fun of proof-of-concept code, do you? Need you ruin everything? (-:

Not just yet, sorry, I really am still sober. Not for long though :wink:

Happy new year everyone!

@webaddictz nasty trick with debug_backtrace there. Won’t work with a recursive call though.

Btw.:


<?php 
$var = 'it could.'; 
int( 3 )->times( function( ) use ($var) { 
    echo "Obviously, $var\
"; 
});

Won’t even work when the function is called twice on one line, so recursive calling would most definitely fail. :slight_smile:

Nice, didn’t remember that was possible: I don’t use closures a lot (yet).

I find it funny how in order to avoid a global variable, all the solutions create tons of variables in the scope of the functions being called.
Isn’t that defeating the original purpose completely (I know those variables aren’t global)?

I haven’t encountered a loop where I had to loop x times without needing the counting variable in some way.

If you’re just needing to repeat a string there’s [fphp]str_repeat[/fphp]

Come on Michael, you know that’s not what this is about. :cool:

Thanks everyone, I’m currently looking for something to add to PHP and this sounds like a good candidate.

I think a new function simply named loop(int $count) should do the trick.

I’d use it, would anyone else?