Hướng dẫn dùng 0 zero trong PHP

Tôi nhận ra đây là một câu hỏi cũ, nhưng nó có liên quan đến ngày nay và tôi không thực sự thích các câu trả lời ở đây.

Cách thích hợp để khắc phục điều này, là thực sự tự đánh giá biểu thức - nghĩa là, bằng cách phân tích cú pháp biểu thức, sau đó đánh giá nó từng bước, thay vì chuyển nó sang PHP. Điều này có thể được thực hiện bằng cách sử dụng https://en.wikipedia.org/wiki/Shunting-yard_algorithm .

Tôi đã viết cách triển khai sau đây, nhưng tôi chưa thử nghiệm nó. Nó dựa trên bài viết Wikipedia ở trên. Không có hỗ trợ cho các toán tử liên kết phải, vì vậy nó được đơn giản hóa một chút.

// You may need to do a better parsing than this to tokenize your expression.
// In PHP, you could for example use token_get_all()
$formula = explode(' ', 'foo + bar * ( baz / ( foz - bak ) )');;
$queue = array();
$operators = array();
$precedence = array('-' => 2, '+' => 2, '/' => 3, '*' => 3, '^' => 4);
$rightAssoc = array('^');
$variables = array('foo' => $foo, 'bar' => $bar, 'baz' => $baz, 'foz' => $foz, 'bak' => $bak);

foreach($formula as $token) {
    if(isset($variables[$token])) {
        $queue[] = $variables[$token];
    } else if(isset($precedence[$token])) {
        // This is an operator
        while(
            sizeof($operators) > 0 && 
            $operators[sizeof($operators)-1] !=  '(' && (
                $precedence[$operators[sizeof($operators)-1]] > $precedence[$token] ||
                (
                    $precedence[$operators[sizeof($operators)-1]] == $precedence[$token] &&
                    !in_array($operators[sizeof($operators)-1], $rightAssoc)
                )
            )
        ) $queue[] = array_pop($operators);
        $operators[] = $token;
    } else if($token == '(') {
        $operators[] = '(';
    } else if($token == ')') {
        while($operators[sizeof($operators)-1] != '(') {
            $queue[] = array_pop($operators);
        }
        array_pop($operators);
    } else if($token == ')') {
        while($operators[sizeof($operators)-1] != ')') {
            $queue[] = array_pop($operators);
        }
        if(null === array_pop($operators))
            throw new \Exception("Mismatched parentheses");
}
$queue = array_merge($queue, array_reverse($operators));
$stack = array();
foreach($queue as $token) {
    if(is_numeric($token)) $stack[] = $token;
    else switch($token) {
        case '+' : 
            $stack[] = array_pop($stack) + array_pop($stack);
            break;
        case '-' :
            // Popped variables come in reverse, so...
            $stack[] = -array_pop($stack) + array_pop($stack);
            break;
        case '*' :
            $stack[] = array_pop($stack) * array_pop($stack);
            break;
        case '/' :
            $b = array_pop($stack);
            $a = array_pop($stack);
            if($b == 0)
                throw new \Exception("Division by zero");
            $stack[] = $a / $b;
            break;                
    }
}
echo "The result from the calculation is ".array_pop($stack)."\n";

Trong trường hợp cụ thể của bạn

Mặc dù tôi thích giải pháp Shunting Yard hơn - nếu tôi vẫn quyết định sử dụng phiên bản eval () -, tôi sẽ tạo phương thức custom_division ($ leftHandSide, $ rightHandSide), phương thức này sẽ đưa ra một ngoại lệ. Mã này:

eval("$foo + $bar * ( $baz / ( $foz - $bak ) )");

trở thành

function custom_division($a, $b) { if($b == 0) throw Exception("Div by 0"); }
eval("$foo + $bar * ( custom_division( $baz, ( $foz - $bak ) )");

1 hữu ích 0 bình luận chia sẻ