这个问题是用伪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
)
那是丑陋的。但是,这是可以接受的吗?数组是否应该“碎片整理”,即重新排列的键以消除空格?
或者我应该注意另一种程序化结构吗?我想要一个结构,我可以添加东西,删除东西,引用变量中的键,迭代等,这不是一个数组。这样的事情存在吗?
我已经编码好几年了,但这个问题在很多年里一直困扰着我,我仍然没有意识到答案。对于一些程序员来说这可能是显而易见的,但对我来说这是非常重要的。
答案 0 :(得分:4)
PHP的“关联数组”的问题在于它们根本不是数组,它们是Hashmaps。有洞是完全没问题的。您也可以查看linked list,但Hashmap似乎非常适合您正在做的事情。
答案 1 :(得分:2)
维护您的下载器阵列是什么?
如果将数组封装在下载程序完成时通知的类中,则不必担心对已销毁对象的过时引用。
这个类可以在内部管理数组的组织,并为其用户提供一个看起来更像迭代器而不是数组的接口。
答案 2 :(得分: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;
}
}
答案 3 :(得分:1)
从C#角度来看,我首先想到的是你需要一个与数组不同的数据结构 - 你需要使用更高级别的数据结构来思考问题。或许队列,列表或堆栈更适合您的目的?
答案 4 :(得分:1)
对您的问题的简短回答是,PHP数组几乎用于所有内容,您很少使用其他数据结构。在数组索引中有漏洞并不需要担心。在Java等其他编程语言中,您可以选择更多样化的数据结构:集合,散列,列表,向量等。看来你还需要在Array和DownloaderObject类之间进行更密切的交互。仅仅因为对象$ download2已经“destroy()”本身,数组将保持对该对象的引用。
答案 5 :(得分:0)
这个问题的一些好的答案,反映了对答复者的相对经验。非常感谢 - 他们证明非常有教育意义。
差不多三年前我发布了这个问题。事后来看,我可以看到我在该领域的知识缺乏严重。我遇到的最大问题是我来自PHP的角度,它没有任意弹出元素的能力。这个问题的另一个答案让我发现,一个基本上优越的模型是'链表'。
对于C,我写了一个blog post about linked lists,其中包含代码示例(此处发布的数量太多),但会整齐地填充原始问题的用例。
对于PHP,链接列表实现显示为here,我从未尝试过,但想象它也是处理上述问题的正确方法。
有趣的是,Python lists包含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'