布尔表达式:多个中缀字符串的后缀

时间:2014-05-09 15:42:52

标签: php algorithm postfix-notation infix-notation shunting-yard

给出以下(中缀)表达式:

(country = be or country = nl) and 
(language = en or language = nl) and 
message contains twitter

我想创建以下4个中缀符号:

message contains twitter and country = be and language = en
message contains twitter and country = be and language = en
message contains twitter and country = nl and language = nl
message contains twitter and country = nl and language = nl

所以,基本上,我想摆脱所有的OR。

我已经有第一个表达式的后缀表示法,所以我目前正在尝试处理它以获得所需的表示法。然而,这种特殊情况会带来麻烦。

(为了便于说明,此查询的后缀表示法为:)

country be = country nl = or language en = language = nl or and message twitter contains and

有没有人知道实现此目的的算法?

2 个答案:

答案 0 :(得分:0)

将问题分为两个步骤:postfix到多个postfix,postfix到infix。每个步骤都是通过“解释”后缀表达式来执行的。

对于多个后缀解释器的后缀:堆栈值是后缀表达式的集合。解释规则如下。

<predicate>: push a one-element collection containing <predicate>.
AND: pop the top two collections into C1 and C2. With two nested loops,
     create a collection containing x y AND for all x in C1 and y in C2.
     Push this collection.
OR: pop the top two collections into C1 and C2. Push the union of C1 and C2.

对于后缀到中缀解释器:堆栈值是中缀表达式。

<predicate>: push <predicate>.
AND: pop two expressions into x and y. Push the expression (x) and (y).

这些步骤可以合并,但我想提出这个技术的两个例子。

答案 1 :(得分:0)

使用树形表示可能最简单。使用分流码算法构建表示等式的二叉树。树中的节点可能是:

class Node {
    const OP = 'operator';
    const LEAF = 'leaf';
    $type = null; // Will be eight Node::OP or Node::LEAF
    $op = null; // could be 'or' or 'and' 'contains'; 
    $value = null; // used for leaf eg 'twitter'
    $left = null;
    $right = null;

}

虽然你可以使用子类。在调车场算法中,您希望更改输出步骤以生成树。

一旦你有树表示,你需要几个算法。

首先,您需要一种算法来复制树

public function copy($node) {
    if($node->type == Node::LEAF) {
        $node2 = new Node();
        $node2->type = Node::LEAF;
        $node2->value = $node->value;
        return $node2;
    }
    else {
        $left = copy($node->left);
        $right = copy($node->right);
        $node2 = new Node();
        $node2->type = Node::OP;
        $node2->op = $node->op;
        $node2->left = $node->left;
        $node2->right = $node->right;
        return $node2;
    }
}

接下来算法找到第一个&#39;或&#39;运营商节点。

function findOr($node) {
    if($node->type == Node::OP && $node->op == 'or') {
       return $node;
    } else if($node->type == Node::OP ) {
       $leftRes = findOr($node->$left);
       if( is_null($leftRes) ) {
           $rightRes = findOr($node->$right); // will be null or a found node
           return $rightRes;
       } else {
           return $leftRes; // found one on the left, no need to walk rest of tree
       }
    } else {
       return null;
    }
}

最后算法copyLR给出左(true)或右(false)分支。它表现为副本,除非在返回左或右分支时节点与$ target匹配。

public function copyLR($node,$target,$leftRight) {
    if($node == $target) {
        if($leftRight)
            return copy($node->left);
        else
            return copy($node->right);
    }
    else if($node->type == Node::LEAF) {
        $node2 = new Node();
        $node2->type = Node::LEAF;
        $node2->value = $node->value;
        return $node2;
    }
    else {
        $left = copy($node->left,$target,$leftRight);
        $right = copy($node->right,$target,$leftRight);
        $node2 = new Node();
        $node2->type = Node::OP;
        $node2->op = $node->op;
        $node2->left = $node->left;
        $node2->right = $node->right;
        return $node2;
    }
}

这些碎片现在放在一起

$root = parse(); // result from the parsing step 
$queue = array($root);
$output = array();
while( count($queue) > 0) {
    $base = array_shift($queue);
    $target = findOr($base);
    if(is_null($target)) {
        $output[] = $base; // no or operators found so output
    } else {
        // an 'or' operator found
        $left = copyLR($base,$target,true); // copy the left
        $right = copyLR($base,$target,false); // copy the right
        array_push($left); // push both onto the end of the queue
        array_push($right);
    }
}