PHP:公开“获取”;并“设置”对于具有嵌套关联数组的对象
我有一个使用多级关联数组存储值的类:
我需要添加一种访问和修改嵌套值的方法。这是解决我的问题的可行解决方案,但是速度相当慢。有没有更好的方法来做到这一点?
注意:get/set 函数的使用不是强制性的,但需要有一种有效的方法来定义默认值。
class Demo {
protected $_values = array();
function __construct(array $values) {
$this->_values = $values;
}
public function get($name, $default = null) {
$token = strtok($name, '.#');
$node = $this->_values;
while ($token !== false) {
if (!isset($node[$token]))
return $default;
$node = $node[$token];
$token = strtok('.#');
}
return $node;
}
public function set($name, $value) {
$next_token = strtok($name, '.#');
$node = &$this->_values;
while ($next_token !== false) {
$token = $next_token;
$next_token = strtok('.#');
if ($next_token === false) {
$node[ $token ] = $value;
break;
}
else if (!isset($node[ $token ]))
$node[ $token ] = array();
$node = &$node[ $token ];
}
unset($node);
}
}
其用法如下:
$test = new Demo(array(
'simple' => 27,
'general' => array(
0 => array(
'something' => 'Hello World!',
'message' => 'Another message',
'special' => array(
'number' => 27
)
),
1 => array(
'something' => 'Hello World! #2',
'message' => 'Another message #2'
),
)
));
$simple = $test->get('simple'); // === 27
$general_0_something = $test->get('general#0.something'); // === 'Hello World!'
$general_0_special_number = $test->get('general#0.special.number'); === 27
注意:'general.0.something' 与 'general#0.something' 相同,替代标点符号是为了清楚起见。
I have a class which stores values with a multi-level associative array:
I need to add a way to access and modify nested values. Here is a working solution for my problem, but it is rather slow. Is there a better way of doing this?
Note: The use of get / set functions is not mandatory, but there needs to be an efficient way to define a default value.
class Demo {
protected $_values = array();
function __construct(array $values) {
$this->_values = $values;
}
public function get($name, $default = null) {
$token = strtok($name, '.#');
$node = $this->_values;
while ($token !== false) {
if (!isset($node[$token]))
return $default;
$node = $node[$token];
$token = strtok('.#');
}
return $node;
}
public function set($name, $value) {
$next_token = strtok($name, '.#');
$node = &$this->_values;
while ($next_token !== false) {
$token = $next_token;
$next_token = strtok('.#');
if ($next_token === false) {
$node[ $token ] = $value;
break;
}
else if (!isset($node[ $token ]))
$node[ $token ] = array();
$node = &$node[ $token ];
}
unset($node);
}
}
Which would be used as follows:
$test = new Demo(array(
'simple' => 27,
'general' => array(
0 => array(
'something' => 'Hello World!',
'message' => 'Another message',
'special' => array(
'number' => 27
)
),
1 => array(
'something' => 'Hello World! #2',
'message' => 'Another message #2'
),
)
));
$simple = $test->get('simple'); // === 27
$general_0_something = $test->get('general#0.something'); // === 'Hello World!'
$general_0_special_number = $test->get('general#0.special.number'); === 27
Note: 'general.0.something' is the same as 'general#0.something', the alternative punctuation is for the purpose of clarity.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
嗯,这个问题很有趣,我忍不住再修改一下。 :-)
所以,这是我的结论。 您的实现可能是最直接和清晰的。而且它正在工作,所以我不会真正费心去寻找另一个解决方案。事实上,你最终会接到多少电话?性能上的差异是否值得这么麻烦(我的意思是“超级快”和“几乎一半快”)?
不过,如果性能确实是一个问题(收到数千个调用),那么如果您重复查找数组,就有一种方法可以减少执行时间。
在您的版本中,最大的负担落在
get
函数中的字符串操作上。在这种情况下,任何涉及字符串操作的事情都注定会失败。我最初尝试解决这个问题时确实是这样。如果我们想要这样的语法,很难不碰字符串,但我们至少可以限制我们进行的字符串操作。
如果您创建一个哈希映射(哈希表),以便可以将多维数组展平为一层深度结构,那么完成的大部分计算都是一次性费用。它是有回报的,因为这样您几乎可以通过
get
调用中提供的字符串直接查找您的值。我想出了大致这样的东西:
setter 尚未实现,您必须修改它以使用替代语法(
#
分隔符),但我认为它传达了这个想法。至少在我的测试平台上,与原始实现相比,执行此操作需要一半的时间。仍然原始数组访问速度更快,但在我的情况下差异约为 30-40%。目前这是我能达到的最好成绩。我希望你的实际情况还不够大,以至于我在途中遇到了一些内存限制。 :-)
Well, the question was interesting enough that I couldn't resist tinkering a bit more. :-)
So, here are my conclusions. Your implementation is probably the most straightforward and clear. And it's working, so I wouldn't really bother about searching for another solution. In fact, how much calls are you gonna get in the end? Is the difference in performance worth the trouble (I mean between "super ultra blazingly fast" and "almost half as fast")?
Put aside though, if performance is really an issue (getting thousands of calls), then there's a way to reduce the execution time if you repetitively lookup the array.
In your version the greatest burden falls on string operations in your
get
function. Everything that touches string manipulation is doomed to fail in this context. And that was indeed the case with all my initial attempts at solving this problem.It's hard not to touch strings if we want such a syntax, but we can at least limit how much string operations we do.
If you create a hash map (hash table) so that you can flatten your multidimensional array to a one level deep structure, then most of the computations done are a one time expense. It pays off, because this way you can almost directly lookup your values by the string provided in your
get
call.I've come up with something roughly like this:
The setter is not yet implemented, and you would have to modify it for alternative syntax (
#
separator), but I think it conveys the idea.At least on my testbed it takes half the time to execute this compared to the original implementation. Still raw array access is faster, but the difference in my case is around 30-40%. At the moment that was the best I could achieve. I hope that your actual case is not big enough that I've hit some memory constraints on the way. :-)
好吧,我的第一次尝试没有达到我的目标。这是使用本机 PHP 数组语法(至少用于访问)并且仍然能够设置默认值的解决方案。
更新:添加了获取/设置和动态转换缺少的功能。
顺便说一句,如果您正在优化性能,这不是一种可以采取的方法。这可能比常规数组访问慢 20 倍。
初始化
结果
Ok, my first approached missed the goal I was aiming for. Here is the solution to using native PHP array syntax (at least for access) and still being able to set a default value.
Update: Added missing functionality for get/set and on the fly converting.
By the way, this is not an approach to take if you are optimizing for performance. This is perhaps 20 times slower than regular array access.
Init
Result
您正在寻找类似的东西吗?本质上,
get()
方法使用引用下降到$values
数组中,并在无法满足要求时退出该方法。You're looking for something like that? Essentially the
get()
method uses references to descend into the$values
array and breaks out of the method if a requirement could not be met.这就是 PHP 中多维数组的一般工作方式:
接收
Hello World
:This is how multidimensional array work in general in PHP:
To receive
Hello World
: