在嵌套数组中查找内部数组

Fri*_*Kim 4 php arrays nested

我在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' => ")"
       )
);       
Run Code Online (Sandbox Code Playgroud)

我需要在这里处理最里面的数组,一个带有注释的数组:"我想找到这个." 有功能吗?

我考虑过做(写作一个想法,而不是正确的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;
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

它的作用是它使$name数组中的当前键.如果值是数组,则使用foreach进入数组并添加"." 和数组的id.所以我们在示例数组中最终得到:

array (
    '0' => "1.3.2",
)
Run Code Online (Sandbox Code Playgroud)

然后我可以处理这些值来访问内部数组.

问题是我试图找到内部数组的数组是动态的,并由用户输入组成.(它将输入字符串拆分为+或 - ,如果它包含括号,则将其放在单独的嵌套数组中.因此,如果用户键入了很多括号,则会有很多嵌套数组.)因此我需要使这个模式向下移动20次,并且无论如何它仍然只能捕获20个嵌套数组.

那还有一个功能吗?或者有没有办法让它在没有我的长代码的情况下做到这一点?也许制作一个循环来制作必要数量的foreach模式并运行它eval()

out*_*tis 16

定义

简单:
描述没有子表达式的表达式(例如"5","x").
复合:
描述具有子表达式的表达式(例如"3 + x","1 + 2").
常量性:
表达式是否具有恒定值(例如"5","1 + 2")或不具有(例如"x","3 + x").
外节点:
在表达式树中,通过始终遍历左或始终遍历右侧可到达的节点."外"总是相对于给定节点; 节点可能相对于一个节点是"外部",但相对于该节点的父节点是"内部".
内节点:
在表达式树中,不是外部节点的节点.

有关"内部"和"外部"节点的说明,请考虑:

       __1__
      /     \ 
     2       5
    / \     / \
   3   4   6   7
3和7总是外部节点.6是相对于5的外部,但是相对于1的内部.

回答

这里的困难更多地在于表达格式不均匀而不是嵌套.如果使用表达式树,示例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
)   )   )   )   )
Run Code Online (Sandbox Code Playgroud)

请注意,二进制操作的节点是二进制的,并且一元操作将具有一元节点.如果二进制可交换操作的节点可以组合成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
)   )   )   )
Run Code Online (Sandbox Code Playgroud)

然后,您将编写一个可以简化节点的后序递归函数."后序"意味着节点处理在处理其子节点之后发生; 还有预订(在其子节点之前处理节点)和按顺序(在节点之前处理一些子节点,之后处理其余子节点).以下是粗略概述.其中,"thing:Type"表示"thing"的类型为"Type","&"表示pass-by-reference.

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;
Run Code Online (Sandbox Code Playgroud)

在某种程度上,真正的挑战是写作simplify_node.它可以在表达式节点上执行许多操作:

  1. 如果一个内在的大孩子与另一个孩子的常数不匹配,但是它的兄弟姐妹确实如此,则交换兄弟姐妹.换句话说,将奇数输出作为外部节点.这一步是为下一步做准备.
        +            +                +            +
       / \          / \              / \          / \
      \+   2  --->  +   2            +   y  --->  +   y
     / \          / \              / \          / \
    1   x        x   1            x   1        1   x
    
  2. 如果节点和子节点是相同的可交换操作,则可以重新排列节点.例如,有旋转:

        +            +
       / \          / \
      \+   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节点浮动直到它们是兄弟节点,此时它们像苏打水中的气泡一样结合在一起.

  3. 如果所有子节点都是常量,请评估节点并将其替换为结果.

处理具有多个复合子节点和n-ary节点的节点作为读者的练习.

面向对象的替代方案

OO方法(使用对象而不是数组来构建表达式树)将具有许多优点.操作将与节点关联得更紧密; 它们是节点对象的属性,而不是节点键.将辅助数据与表达式节点相关联也会更容易,这对优化很有用.您可能不需要深入了解OOP范例来实现它.可以使用以下简单类型层次结构:

                   Expression
                  /          \
        SimpleExpr            CompoundExpr
        /        \
ConstantExpr    VariableExpr

操纵树的现有自由函数将成为方法.接口看起来像下面的伪代码.在里面:

  • Child < Parent 表示"Child"是"Parent"的子类.
  • 属性(例如isConstant)可以是方法或字段; 在PHP中,您可以使用重载来实现它.
  • (...){...}表示函数,括号和括号之间的参数之间的参数(很像function (...){...}Javascript).此语法用于作为方法的属性.简单方法只是使用方括号的括号.

现在为样本:

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
    }
}
Run Code Online (Sandbox Code Playgroud)

对于PHP实现SimpleExprConstantExpr,例如,可以是:

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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

另一种实现方式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;
    }
}
Run Code Online (Sandbox Code Playgroud)