0

这个问题是用伪 PHP 编写的,但我真的不介意我用什么语言得到答案(Ruby :-P 除外),因为这纯粹是假设性的。事实上,PHP 很可能是执行此类逻辑的最糟糕的语言。不幸的是,我以前从未这样做过,所以我无法提供真实世界的示例。因此,假设的答案是完全可以接受的。

基本上,我有很多执行任务的对象。对于此示例,假设每个对象都是一个从 Internet 下载文件的类。每个对象将下载不同的文件,并且下载是并行运行的。显然,某些对象可能会先于其他对象完成下载。数据的实际抓取可能在线程中运行,但这与这个问题无关。

所以我们可以这样定义对象:

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
    }
}

好的,所以我们有很多这样的对象在运行:

$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');

我们可以将它们存储在一个数组中:

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

所以我们有一个充满下载的数组:

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

我们可以像这样遍历它们:

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

好的,现在假设下载 2 完成,并且对象被销毁。现在我们应该在数组中有两个对象:

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

但是阵中有一个洞!密钥 #2 未被使用。此外,如果我想开始新的下载,不清楚将下载插入到数组的哪个位置。以下可以工作:

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

然而,这是非常低效的(并且while $i++循环是nooby)。所以,另一种方法是保留一个计数器。

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

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

这会起作用,但我们仍然会在数组中找到漏洞:

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

太丑了 然而,这样可以接受吗?是否应该对数组进行“碎片整理”,即重新排列键以消除空格?

还是我应该注意另一种程序结构?我想要一个结构,我可以添加东西,从中删除东西,引用变量中的键,迭代等等,这不是一个数组。这样的事情存在吗?

我已经编码多年了,但是这个问题多年来一直困扰着我,我仍然不知道答案。这对一些程序员来说可能很明显,但对我来说却非常重要。

4

6 回答 6

4

PHP 的“关联数组”的问题在于它们根本不是数组,它们是Hashmaps。那里有洞是完全没问题的。您也可以查看链接列表,但 Hashmap 似乎非常适合您正在做的事情。

于 2008-12-08T07:07:00.653 回答
2

什么是维护您的下载器阵列?

如果将数组封装在下载器完成时通知的类中,则不必担心对已破坏对象的过时引用。

此类可以在内部管理数组的组织,并向其用户提供一个看起来更像迭代器而不是数组的接口。

于 2008-12-08T08:00:18.660 回答
1

"$i++ 循环" 是小菜一碟,但这只是因为如果你使用 for 循环,代码会变得更清晰:

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

变成

for($i=0;$i<count($downloads)-1;++$i){
    if (!is_object($downloads[$i])) {
        $downloads[$i] = new DownloaderObject('http://somesite.com/doctorwho.iso');
        break;
    }
}
于 2008-12-08T07:07:30.327 回答
1

从 C# 的角度来看,我的第一个想法是您需要与数组不同的数据结构 - 您需要使用更高级别的数据结构来考虑问题。也许队列、列表或堆栈更适合您的目的?

于 2008-12-08T07:10:58.837 回答
1

对您的问题的简短回答是,在 PHP 中,数组几乎用于所有事物,并且您很少最终使用其他数据结构。数组索引中存在漏洞无需担心。在 Java 等其他编程语言中,您可以选择更多样化的数据结构集:集合、哈希、列表、向量等。看来您还需要在 Array 和 DownloaderObject 类之间进行更密切的交互。仅仅因为对象 $download2 本身具有“destroyed()”,数组将维护对该对象的引用。

于 2008-12-08T07:45:51.590 回答
0

这个问题的一些很好的答案,反映了回答者的相对经验。非常感谢——事实证明他们很有教育意义。

大约三年前,我发布了这个问题。事后看来,我可以看到我在这方面的知识严重缺乏。我遇到的最大问题是我是从 PHP 角度来看的,它没有能力任意弹出元素。这个问题的其他答案帮助我发现了一个根本上优越的模型是“链表”

对于 C,我写了一篇关于链表的博客文章,其中包含代码示例(太多无法在此处发布),但可以巧妙地填充原始问题的用例。

对于 PHP,这里出现了一个链表实现,我从未尝试过,但想象它也是处理上述问题的正确方法。

有趣的是,Python 列表包含的pop()方法与 PHP 不同array_pop(),它可以弹出任意元素并保持一切井井有条。例如:

>>> x = ['baa', 'ram', 'ewe'] # our starting point
>>> x[1]                      # making sure element 1 is 'ram'
'ram'
>>> x.pop(1)                  # let's arbitrarily pop an element in the middle
'ram'
>>> x                         # the one we popped ('ram') is now gone
['baa', 'ewe']
>>> x[1]                      # and there are no holes: item 2 has become item 1
'ewe'
于 2008-12-09T13:03:46.330 回答