tags:

views:

134

answers:

6

This question is coded in pseudo-PHP, but I really don't mind what language I get answers in (except for Ruby :-P), as this is purely hypothetical. In fact, PHP is quite possibly the worst language to be doing this type of logic in. Unfortunately, I have never done this before, so I can't provide a real-world example. Therefore, hypothetical answers are completely acceptable.

Basically, I have lots of objects performing a task. For this example, let's say each object is a class that downloads a file from the Internet. Each object will be downloading a different file, and the downloads are run in parallel. Obviously, some objects may finish downloading before others. The actual grabbing of data may run in threads, but that is not relevant to this question.

So we can define the object as such:

class DownloaderObject() {
    var $url = '';
    var $downloading = false;

    function DownloaderObject($v){ // constructor
        $this->url = $v;
        start_downloading_in_the_background(url=$this->$url, callback=$this->finished);
        $this->downloading = true;
    }

    function finished() {
        save_the_data_somewhere();
        $this->downloading = false;
        $this->destroy(); // actually destroys the object
    }
}

Okay, so we have lots of these objects running:

$download1 = new DownloaderObject('http://somesite.com/latest_windows.iso');
$download2 = new DownloaderObject('http://somesite.com/kitchen_sink.iso');
$download3 = new DownloaderObject('http://somesite.com/heroes_part_1.rar');

And we can store them in an array:

$downloads = array($download1, $download2, $download3);

So we have an array full of the downloads:

array(
  1 => $download1,
  2 => $download2,
  3 => $download3
)

And we can iterate through them like this:

print('Here are the downloads that are running:');
foreach ($downloads as $d) {
    print($d->url . "\n");
}

Okay, now suppose download 2 finishes, and the object is destroyed. Now we should have two objects in the array:

array(
  1 => $download1,
  3 => $download3
)

But there is a hole in the array! Key #2 is being unused. Also, if I wanted to start a new download, it is unclear where to insert the download into the array. The following could work:

$i = 0;
while ($i < count($downloads) - 1) {
    if (!is_object($downloads[$i])) {
        $downloads[$i] = new DownloaderObject('http://somesite.com/doctorwho.iso');
        break;
    }
    $i++;
}

However, that is terribly inefficient (and while $i++ loops are nooby). So, another approach is to keep a counter.

function add_download($url) {
    global $downloads;
    static $download_counter;

    $download_counter++;
    $downloads[$download_counter] = new DownloaderObject($url);
}

That would work, but we still get holes in the array:

array(
  1  => DownloaderObject,
  3  => DownloaderObject,
  7  => DownloaderObject,
  13 => DownloaderObject
)

That's ugly. However, is that acceptable? Should the array be "defragmented", i.e. the keys rearranged to eliminate blank spaces?

Or is there another programmatic structure I should be aware of? I want a structure that I can add stuff to, remove stuff from, refer to keys in a variable, iterate through, etc., that is not an array. Does such a thing exist?

I have been coding for years, but this question has bugged me for very many of those years, and I am still not aware of an answer. This may be obvious to some programmers, but is extremely non-trivial to me.

+3  A: 

The problem with PHP's "associative arrays" is that they aren't arrays at all, they're Hashmaps. Having holes there is perfectly fine. You might look at a linked list, as well, but a Hashmap seems perfectly suited to what you're doing.

eplawless
Today, I used a linked list in an C application I wrote, and they're very awesome. Thanks! :)
Jeremy Visser
+1  A: 

"$i++ loops" are nooby, but only because the code becomes much clearer if you use a for loop:

$i = 0;
while ($i < count($downloads) - 1) {
    if (!is_object($downloads[$i])) {
       $downloads[$i] = new DownloaderObject('http://somesite.com/doctorwho.iso');
        break;
    }
    $i++;
}

Becomes

for($i=0;$i<count($downloads)-1;++$i){
    if (!is_object($downloads[$i])) {
        $downloads[$i] = new DownloaderObject('http://somesite.com/doctorwho.iso');
        break;
    }
}
Stefan Mai
Thanks. Yeah, I actually did know about `for` loops, but I can never remember the syntax, and it was really only for the example anyway. You can see that the code more or less runs the same way.
Jeremy Visser
+1  A: 

Coming from a C# perspective, my first thought would be that you need a different data structure to an array - you need to think about the problem using a higher-level data structure. Perhaps a Queue, List or Stack would suit your purposes better?

cbp
In PHP, there are arrays, and only arrays.
eplawless
As I said, this question is abstract. The only reason I wrote the question in PHP is because that is what I'm mostly familiar with. If I were writing an app doing anything remotely similar to this, I'd probably want to learn Python to do it.
Jeremy Visser
Queues look cool. From looking at the Wikipedia article on them, you push() onto it, and pop() them. Is there any way to iterate through them without pop()'ing them (and thus removing them from the queue)?
Jeremy Visser
As a follow-up to this question, I have since been doing a lot of Python programming, and it looks like stacks are the correct paradigm for what I want to achieve.I have since used stacks in quite a few applications, and they are actually not all that complex to use. However, I have only used them in Python -- no idea how you would implement them in a language where they are not native (e.g. PHP).
Jeremy Visser
+1  A: 

The short answer to your question is that in PHP arrays are used for almost everything and you rarely end up using other data structures. Having holes in your array indexes isn't anything to worry about. In other programming languages such as Java you have a more diverse set of data structures to choose from: Sets, Hashes, Lists, Vectors and more. It seems that you would also need to have a closer interaction between the Array and DownloaderObject class. Just because the object $download2 has "destroyed()" itself the array will maintain a reference to that object.

Brian Fisher
+2  A: 

What is maintaining your array of downloaders?

If you encapsulate the array in a class that is notified by the downloader when it is finished you won't have to worry about stale references to destroyed objects.

This class can manage the organisation of the array internally and present an interface to its users that looks more like an iterator than an array.

Andrew Kennan
A: 

Some very good answers to this question. Thankyou very much. I chose one question as an "answer", but modded you all up, as you helped very much.

I will look into Stacks and Queues -- they look quite interesting. Basically, what I wanted to know was whether there was some sort of storage thingy that can store a bunch of objects, be able to iterate through them, and not care what order or what 'key' they are stored in. (Though I'm not sure if I directly got that answer. If anybody gives an answer with that as a specific answer, I will mark them as having answered this question.)

Jeremy Visser