我在PHP中有一个嵌套数组:
array (
'0' => "+5x",
'1' => array (
'0' => "+",
'1' => "(",
'2' => "+3",
'3' => array (
'0' => "+",
'1' => "(",
'2' => array ( // I want to find this one.
'0' => "+",
'1' => "(",
'2' => "+5",
'3' => "-3",
'4' => ")"
),
'3' => "-3",
'4' => ")"
),
'4' => ")"
)
);
我需要在这里处理最里面的数组,注释:“我想找到这个。”那是否有功能?
我考虑过这样做(写作一个想法,而不是正确的PHP):
foreach ($array as $id => $value) {
if ($value is array) {
$name = $id;
foreach ($array[$id] as $id_2 => $value_2) {
if ($value_2 is array) {
$name .= "." . $id_2;
foreach ($array[$id][$id_2] as $id_3 => $value_3) {
if ($value_3 is array) {
$name .= "." . $id_3;
foreach ($array[$id][$id_2][$id_3] as $id_4 => $value_4) {
if ($value_4 is array) {
$name .= "." . $id_4;
foreach [and so it goes on];
} else {
$listOfInnerArrays[] = $name;
break;
}
}
} else {
$listOfInnerArrays[] = $name;
break;
}
}
} else {
$listOfInnerArrays[] = $name;
break;
}
}
}
}
它的作用是使$name
成为数组中的当前键。如果值是数组,则使用foreach进入数组并添加“。”和数组的id。所以我们在示例数组中最终得到:
array (
'0' => "1.3.2",
)
然后我可以处理这些值以访问内置数组。
问题是我正在尝试查找内部数组的数组是动态的,并由用户输入组成。 (它将输入字符串拆分为+或 - ,如果它包含括号,则将其放在单独的嵌套数组中。因此,如果用户键入了很多括号,则会有很多嵌套数组。) 因此,我需要让这个模式减少20次,但无论如何它仍然只能捕获20个嵌套数组。
那还有一个功能吗?或者有没有办法让它在没有我的长代码的情况下做到这一点?也许制作一个循环来制作必要数量的foreach模式并通过eval()
运行它?
答案 0 :(得分:16)
有关“内部”和“外部”节点的说明,请考虑:
__1__ / \ 2 5 / \ / \ 3 4 6 73和7总是外部节点。 6是相对于5的外部,但是相对于内部的内部。
这里的困难更多地在于表达格式不均匀而不是嵌套。如果使用表达式树,示例5x+3=(x+(3+(5-3)))
等式将解析为:
array(
'=' => array(
'+' => array( // 5x + 3
'*' => array(
5, 'x'
),
3
)
'+' => array( // (x+(3+(5-3)))
'x',
'+' => array( // (3+(5-3))
3,
'-' => array(
5, 3
) ) ) ) )
请注意,二进制操作的节点是二进制的,并且一元操作将具有一元节点。如果二进制可交换操作的节点可以组合成n-ary节点,5x+3=x+3+5-3
可以解析为:
array(
'=' => array(
'+' => array( // 5x + 3
'*' => array(
5, 'x'
),
3
)
'+' => array( // x+3+5-3
'x',
3,
'-' => array(
5, 3
) ) ) )
然后,您将编写一个可以简化节点的后序递归函数。 “后序”意味着节点处理在处理其子节点之后发生;还有预订(在其子节点之前处理节点)和按顺序(在节点之前处理一些子节点,之后处理其余子节点)。以下是粗略概述。其中,“thing:Type”表示“thing”的类型为“Type”,“&”表示按引用传递。
simplify_expr(expression : Expression&, operation : Token) : Expression {
if (is_array(expression)) {
foreach expression as key => child {
Replace child with simplify_expr(child, key);
key will also need to be replaced if new child is a constant
and old was not.
}
return simplify_node(expression, operation);
} else {
return expression;
}
}
simplify_node(expression : Expression&, operation : Token) : Expression;
在某种程度上,真正的挑战是撰写simplify_node
。它可以在表达式节点上执行许多操作:
+ + + + / \ / \ / \ / \ \+ 2 ---> + 2 + y ---> + y / \ / \ / \ / \ 1 x x 1 x 1 1 x
如果节点和子节点是相同的可交换操作,则可以重新排列节点。例如,有旋转:
+ + / \ / \ \+ c ---> a + / \ / \ a b b c
这对应于将“(a + b)+ c”改变为“a +(b + c)”。当“a”与“b”和“c”的常量不匹配时,您将要旋转。它允许将下一个转换应用于树。例如,此步骤会将“(x + 3)+1”转换为“x +(3 + 1)”,因此下一步可以将其转换为“x + 4”。
总体目标是将const子树作为兄弟姐妹。如果一个可交换节点有两个const后代,它们可以相互旋转。如果一个节点只有一个const后代,那么让它成为一个子节点,这样层次结构中的一个节点可以将const节点与另一个祖先的const子节点组合起来(即const节点浮动直到它们是兄弟节点,此时它们像苏打水中的气泡一样。
处理具有多个复合子节点和n-ary节点的节点作为读者练习。
OO方法(使用对象而不是数组来构建表达式树)将具有许多优点。操作将与节点关联得更紧密;它们是节点对象的属性,而不是节点键。将辅助数据与表达式节点相关联也会更容易,这对优化很有用。您可能不需要深入了解OOP范例来实现它。可以使以下简单类型层次结构起作用:
Expression / \ SimpleExpr CompoundExpr / \ ConstantExpr VariableExpr
操纵树的现有自由函数将成为方法。接口看起来像下面的伪代码。在其中:
Child < Parent
表示“Child”是“Parent”的子类。 isConstant
)可以是方法或字段;在PHP中,您可以使用overloading实现此目的。(...){...}
表示函数,括号和括号之间的参数之间的参数(非常类似于Javascript中的function (...){...}
)。此语法用于作为方法的属性。简单方法只是为方法体使用括号。现在为样本:
Expression {
isConstant:Boolean
simplify():Expression
}
SimpleExpr < Expression {
value:Varies
/* simplify() returns an expression so that an expression of one type can
be replaced with an expression of another type. An alternative is
to use the envelope/letter pattern:
http://users.rcn.com/jcoplien/Patterns/C++Idioms/EuroPLoP98.html#EnvelopeLetter
http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Envelope_Letter
*/
simplify():Expression { return this }
}
ConstantExpr < SimpleExpr {
isConstant:Boolean = true
}
VariableExpr < SimpleExpr {
isConstant:Boolean = false
}
CompoundExpr < Expression {
operation:Token
children:Expression[]
commutesWith(op:Expression):Boolean
isCommutative:Boolean
isConstant:Boolean = (){
for each child in this.children:
if not child.isConstant, return false
return true
}
simplify():Expression {
for each child& in this.children {
child = child.simplify()
}
return this.simplify_node()
}
simplify_node(): Expression {
if this.isConstant {
evaluate this, returning new ConstExpr
} else {
if one child is simple {
if this.commutesWith(compound child)
and one grand-child doesn't match the constness of the simple child
and the other grand-child matches the constness of the simple child
{
if (compound child.isCommutative):
make odd-man-out among grand-children the outer child
rotate so that grand-children are both const or not
if grand-children are const:
set compound child to compound child.simplify_node()
}
} else {
...
}
}
return this
}
}
例如,SimpleExpr
和ConstantExpr
的PHP实现可能是:
class SimpleExpr extends Expression {
public $value;
function __construct($value) {
$this->value = $value;
}
function simplify() {
return $this;
}
}
class ConstantExpr extends SimpleExpr {
// Overloading
function __get($name) {
switch ($name) {
case 'isConstant':
return True;
}
}
}
ConstantExpr
的替代实现:
function Expression {
protected $_properties = array();
// Overloading
function __get($name) {
if (isset($this->_properties[$name])) {
return $this->_properties[$name];
} else {
// handle undefined property
...
}
}
...
}
class ConstantExpr extends SimpleExpr {
function __construct($value) {
parent::construct($value);
$this->_properties['isConstant'] = True;
}
}
答案 1 :(得分:1)
请尝试以下代码并告诉我结果。
您只需将数组传递给find_deepest
函数。
function find_deepest( $array )
{
$index = ''; // this variable stores the current position (1.2, 1.3.2, etc.)
$include = true; // this variable indicates if the current position should be added in the result or not
$result = array(); // this is the result of the function, containing the deepest indexes
$array_stack = array(); // this is a STACK (or LIFO) to temporarily store the sub-arrays - see http://en.wikipedia.org/wiki/LIFO_%28computing%29
reset( $array ); // here we make the array internal POINTER move to the first position
// each loop interaction moves the $array internal pointer one step forward - see http://php.net/each
// if $current value is null, we reached the end of $array; in this case, we will also continue the loop, if the stack contains more than one array
while ( ( $current = each( $array ) ) || ( count( $array_stack ) > 1 ) )
{
// we are looping $array elements... if we find an array (a sub-array), then we will "enter it...
if ( is_array( $current['value'] ) )
{
// update the index string
$index .= ( empty ( $index ) ? '' : '.' ) . $current['key'];
// we are entering a sub-array; by default, we will include it
$include = true;
// we will store our current $array in the stack, so we can move BACK to it later
array_push( $array_stack, $array );
// ATTENTION! Here we CHANGE the $array we are looping; here we "enter" the sub-array!
// with the command below, we start to LOOP the sub-array (whichever level it is)
$array = $current['value'];
}
// this condition means we reached the END of a sub-array (because in this case "each()" function has returned NULL)
// we will "move out" of it; we will return to the previous level
elseif ( empty( $current ) )
{
// if we reached this point and $include is still true, it means that the current array has NO sub-arrays inside it (otherwise, $include would be false, due to the following lines)
if ( $include )
$result[] = $index;
// ATTENTION! With the command below, we RESTORE $array to its precedent level... we entered a sub-array before, now we are goin OUT the sub-array and returning to the previous level, where the interrupted loop will continue
$array = array_pop( $array_stack );
// doing the same thing with the $index string (returning one level UP)
$index = substr( $index, 0, strrpos( $index, '.' ) );
// if we are RETURNING one level up, so we do NOT want the precedent array to be included in the results... do we?
$include = false;
}
// try removing the comment from the following two lines! you will see the array contents, because we always enter this "else" if we are not entering a sub-array or moving out of it
// else
// echo $index . ' => ' . $current['value'] . '<br>';
}
return $result;
}
$result = find_deepest( $my_array );
print_r( $result );
代码中最重要的部分是:
each
循环while
命令
array_push
函数调用,我们将当前数组存储在“数组堆栈”中,以便稍后返回array_pop
函数调用,我们通过从“数组堆栈”恢复当前数组返回一个级别答案 2 :(得分:1)
RecursiveIteratorIterator
知道任何孩子的当前深度。因为你只对有孩子的孩子感兴趣,过滤没有孩子的孩子并寻找最大深度。
然后根据深度再次过滤最大深度:
$ritit = new RecursiveIteratorIterator(new RecursiveArrayIterator($arr), RecursiveIteratorIterator::SELF_FIRST);
$cf = new ChildrenFilter($ritit);
$maxdepth = NULL;
foreach($cf as $v)
{
$maxdepth = max($maxdepth, $cf->getDepth());
}
if (NULL === $maxdepth)
{
throw new Exception('No children found.');
}
$df = new DepthFilter($cf, $maxdepth);
foreach($df as $v)
{
echo "Array with highest depth:\n", var_dump($v), "\n";
}
答案 3 :(得分:1)
递归foreach函数,来自http://php.net/manual/en/control-structures.foreach.php
的评论/* Grab any values from a multidimensional array using infinite recursion. --Kris */
function RecurseArray($inarray, $result) {
foreach ($inarray as $inkey => $inval) {
if (is_array($inval)) {
$result = RecurseArray($inval, $result);
} else {
$result[] = $inval;
}
}
return $result;
}
请注意,上述实现会生成扁平数组。为了保护嵌套:
function RecurseArray($inarray) {
$result = array();
foreach ( $inarray as $inkey => $inval ) {
if ( is_array($inval) ) {
$result[] = RecurseArray($inval);
} else {
// process $inval, store in result array
$result[] = $inval;
}
}
return $result;
}
就地修改数组:
function RecurseArray(&$inarray) {
foreach ( $inarray as $inkey => &$inval ) {
if ( is_array($inval) ) {
RecurseArray($inval);
} else {
// process $inval
...
}
}
}