显示php中的所有计算步骤

时间:2018-03-31 03:05:34

标签: php math

是否可以显示php如何计算问题的解决方案。

例如:

<?php
$equation = (5+1)*3/9; // outputs 2
?>

如果希望看到它处理的所有步骤都来到该解决方案: IE:

(5 + 1)= 6

6 * 3 = 18

18/9 = 2

5 个答案:

答案 0 :(得分:3)

  

简短的回答是“也许”。在运行时期间不可能像你期望的那样挂钩PHP内部。但是,有一种替代方法可以使用外部解析器来接近它。

PHP脚本解析器&amp;抽象语法树(AST)

原因是,PHP在内部使用抽象语法树(AST)来定义处理步骤以及通过创建更好的二进制表示作为OpCache来优化执行,以避免再次解析和处理相同的脚本再一次。

因此,如果您需要了解有关PHP中的处理的更多信息,您可以考虑分析PHP C源代码:

PHP脚本中的

AST - nikic/php-parser

也可以在PHP中重新实现AST解析器,请参阅https://github.com/nikic/PHP-Parser

以下示例将原始问题的表达式$equation = (5+1)*3/9; // outputs 2放入专用的math.php文件中。使用以下CLI命令执行解析器:

composer require nikic/php-parser
vendor/bin/php-parse math.php

将输出$equation = (5+1)*3/9; // outputs 2的解析语法树(基本上是原始问题的答案):

====> File math.php:
==> Node dump:
array(
    0: Stmt_Expression(
        expr: Expr_Assign(
            var: Expr_Variable(
                name: equation
            )
            expr: Expr_BinaryOp_Div(
                left: Expr_BinaryOp_Mul(
                    left: Expr_BinaryOp_Plus(
                        left: Scalar_LNumber(
                            value: 5
                        )
                        right: Scalar_LNumber(
                            value: 1
                        )
                    )
                    right: Scalar_LNumber(
                        value: 3
                    )
                )
                right: Scalar_LNumber(
                    value: 9
                )
            )
        )
    )
    1: Stmt_Nop(
        comments: array(
            0: // outputs 2
        )
    )
)

OpCache analyzis

通过阅读https://nikic.github.io/2017/04/14/PHP-7-Virtual-machine.html(上面提到的PHP AST解析器的作者),可能会发现有关分析PHP处理的更多信息。但是,本节没有给出问题的额外答案 - 更多的是概述替代方案以及它们实际揭示的内容。

php -d opcache.enable_cli=1 -d opcache.opt_debug_level=0x10000 math.php

在CLI上下文中执行的上述命令启用OpCache调试并输出以下内容:

$_main: ; (lines=3, args=0, vars=1, tmps=1)
    ; (before optimizer)
    ; /var/www/developer/math.php:1-4
L0:     EXT_STMT
L1:     ASSIGN CV0($equation) int(2)
L2:     RETURN int(1)

上面的调试输出只包含结果int(2),但不包含已为语法树确定的相应步骤 - 这一点非常明显,因为它们不是优化版本所必需的。

这可以通过查看生成的OpCache二进制文件来验证:

php -d opcache.enable_cli=1 -d opcache.opt_debug_level=0x10000 \
  -d opcache.file_cache=/var/www/developer/opcache/ \
  -d opcache.file_cache_only=1 math.php
hexdump opcache/08202de11af2c60edca0b5438eeefab6/var/www/developer/math.php.bin -C

(我的工作目录是/var/www/developer/,其中有一个子目录opcache/

00000210  2f 76 61 72 2f 77 77 77  2f 64 65 76 65 6c 6f 70  |/var/www/develop|
00000220  65 72 2f 6d 61 74 68 2e  70 68 70 00 75 7f 00 00  |er/math.php.u...|
00000230  28 32 60 aa 75 7f 00 00  30 32 60 aa 75 7f 00 00  |(2`.u...02`.u...|
00000240  38 32 60 aa 75 7f 00 00  40 32 60 aa 75 7f 00 00  |82`.u...@2`.u...|
00000250  02 00 00 00 00 00 00 00  04 00 00 00 ff ff ff ff  |................| <--
00000260  01 00 00 00 00 00 00 00  04 00 00 00 ff ff ff ff  |................|
00000270  e9 0b 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000280  00 00 00 00 00 00 00 00  02 00 00 00 65 08 08 08  |............e...|
00000290  e9 0b 00 00 00 00 00 00  50 00 00 00 00 00 00 00  |........P.......|
000002a0  00 00 00 00 00 00 00 00  02 00 00 00 26 10 01 08  |............&...|
000002b0  e9 0b 00 00 00 00 00 00  10 00 00 00 00 00 00 00  |................|
000002c0  00 00 00 00 ff ff ff ff  04 00 00 00 3e 01 08 08  |............>...|
000002d0  88 02 00 00 00 00 00 00  00 00 00 00 06 02 00 00  |................|
000002e0  cb 5b 29 c4 00 e7 1a 80  08 00 00 00 00 00 00 00  |.[).............|
000002f0  65 71 75 61 74 69 6f 6e  00 32 60 aa 75 7f 00 00  |equation.2`.u...|

上面的hexdump只显示了OpCache文件的结尾。标记行中的第一个字节(0x00000250)已包含优化结果02。对于给定的示例,没有进一步指向运算符(例如ZEND_MUL)和整数文字5139的指针。因此,常数方程的结果已经直接存储在OpCache中。

答案 1 :(得分:2)

据我所知,您正在寻找的是一个数学表达式求值器,能够解析输入字符串并逐步计算,直到达到最终结果。

我花了一些时间来建立一个,这是最终的代码:

<?php

function evaluate($input)
{
    if (!preg_match('/^[+\-*\/\(\)\.\d]+$/', $input))
        return array('NaN');

    $steps = array();
    $steps[] = $input;

    $input = preg_replace('/(\d+(?:\.\d+)?[*\/]\d+(?:\.\d+)?)/', '(\1)', $input);

    while (strpos($input, '(') || strpos($input, ')'))
    {
        $input = preg_replace_callback('/\(([^\(\)]+)\)/', 'evaluate_callback', $input);
        $steps[] = $input;
        $input = preg_replace('/(\d+(?:\.\d+)?[*\/]\d+(?:\.\d+)?)/', '(\1)', $input);
    }

    if (preg_match('/(?:\-?\d+(?:\.?\d+)?[\+\-\*\/])+\-?\d+(?:\.?\d+)?/', $input, $match))
    {
        $steps[] = strval(0 + eval('return '.$match[0].';'));
        return $steps;
    }

    return array('NaN');
}

function evaluate_callback($input)
{
    if (is_numeric($input[1]))
        return $input[1];

    if (preg_match('/(?:\-?\d+(?:\.?\d+)?[\+\-\*\/])+\-?\d+(?:\.?\d+)?/', $input[1], $match))
        return strval(0 + eval('return '.$match[0].';'));

    return '0';
}

?>

这是一个用法示例:

$input = '(7.4-0.2*(1+2.1*0.3))*3-(4.1*2.4-4*0.7)+1*0.4';
$result = evaluate($input);
print_r($result);

Array
(
    [0] => (7.4-0.2*(1+2.1*0.3))*3-(4.1*2.4-4*0.7)+1*0.4
    [1] => (7.4-0.2*(1+0.63))*3-(9.84-2.8)+0.4
    [2] => (7.4-0.2*1.63)*3-7.04+0.4
    [3] => (7.4-0.326)*3-7.04+0.4
    [4] => 7.074*3-7.04+0.4
    [5] => 21.222-7.04+0.4
    [6] => 14.582
)

访问this link,以便亲自尝试一个有效的演示。

我知道,这是非常原始的,但我认为这是一个很好的起点,为了改善它,可以做很多事情。它只处理加法,减法,乘法和除法。嵌套级别只能使用圆括号定义。它可能不太详细,因为可以使用单个eval执行的直接算术步骤不会被分割为子步骤(我在信号上指的是链式加法和/或减法)以及在一次性完成相同的嵌套级别。

答案 2 :(得分:1)

基本的想法是实现你的目标是使用像BODMAS这样的东西,我首先使用括号( ),然后使用'*''/''+'和{{ 1}}最后。

您可以创建一个函数,首先将括号与preg_match_all匹配,然后先执行,然后使用eval()方法逐步执行其余操作,

'-'

上述代码的输出为:<?php $str = '(5+1)*3/9'; $count = 0; echo "step".($count+1)." : $str \n<br/>"; function bodmas($eq,$count,$op){ foreach (['*','/','+','-'] as $key => $symbol) { preg_match_all("!\d+(\.\d+)?\\$symbol\d+(\.\d+)?!", $eq, $matches); if(count($matches[0])){ foreach ($matches[0] as $key2 => $match) { $count++; eval("\$result = $match;"); $eq = str_replace($match, $result, $eq); if($op){ echo "step".($count+1)." : $eq \n<br/>"; } singleton($eq,$count); } } } } preg_match_all("/\([^\^(\)]*\)/", $str, $matches); //print_r($matches); foreach($matches[0] as $key=>$value){ $value = str_replace(["(",")"], ["",""], $value); $eq = bodmas($value,$count,false); $count++; eval("\$result = $value;"); $str = str_replace("($value)", $result, $str); echo "step".($count+1)." : $str \n<br/>"; } bodmas($str,$count,true); function singleton($str,$count){ preg_match_all("/\(\d+(\.\d+)?\)/", $str, $matches); if(count($matches[0])){ foreach($matches[0] as $key=>$value){ $str = str_replace(["(",")"], ["",""], $str); } bodmas($str,$count,true); } } ?>

(5+1)*3/9

Live demo

另一个例子:step1 : (5+1)*3/9 step2 : 6*3/9 step3 : 18/9 step4 : 2 输出是:

((5*1)*3/9)*2

Live demo

注意:这可能有一些软砖,但你可以使用这种结构。不要期望任何能够为您提供所有可能条件的人。我们只能通过小件代码帮助您。祝好运。

答案 3 :(得分:0)

  

是的,一切皆有可能,但是处理很快,你需要建立你的   自己的执行此操作的算法

所以我们都知道算术运算的规则/ * + -

示例

用户输入:8/2+2=6

现在我们知道的输出是8/2=4然后是4+2=6

因此我们需要执行后台执行此操作的操作

你需要执行拆分和计算以及批量堆栈和队列来通过创建算法来处理这个问题。

答案 4 :(得分:0)

这是一个解决方程(solve_equation函数)的示例代码。 这基本上找到了简单的方程(*,/,+, - )并逐个求解(通过使用preg_match,“正则表达式匹配”找到小方程)。最终它通过showing all the steps解决了所有等式。     

    function str_replace_first($from, $to, $content)
    {
        $from = '/'.preg_quote($from, '/').'/';

        return preg_replace($from, $to, $content, 1);
    }
    function solve_equation($equation)
    {
        echo "<pre>Equation. Begin: ".$equation."</pre>";
        $original=$equation;
        $run=true;
        while($run)
        {
            $run=false;
            // multiplication and division with BRACKETS
            while(preg_match('/\((\-)?([0-9])+[\*\/](\-)?([0-9])+\)/',$equation,$m)>0)
            {
                $current=$m[0];
                $split=preg_split("/\*/",$current);
                if(sizeof($split)==2)
                {
                    // multiplication
                    $result=(int)(intval(substr($split[0],1))*intval(substr($split[1],0,strlen($split[1])-1)));
                }
                else
                {
                    $split=preg_split("/\//",$current);
                    // division
                    $result=(int)(intval(substr($split[0],1))/intval(substr($split[1],0,strlen($split[1])-1)));

                }
                $equation=str_replace($current, $result, $equation);
                echo "<pre>".$current."=".$result."</pre>";
                echo "<pre>".$equation."=?</pre>";

            }
            // addition and substraction with BRACKETS
            while(preg_match('/\((\-)?([0-9])+[\+\-](\-)?([0-9])+\)/',$equation,$m)>0)
            {
                $current=$m[0];
                $split=preg_split("/\+/",$current);
                if(sizeof($split)==2)
                {
                    // addition
                    $result=(int)(intval(substr($split[0],1))+intval(substr($split[1],0,strlen($split[1])-1)));
                }
                else
                {
                    $split=preg_split("/\-/",$current);
                    // substraction
                    $result=(int)(intval(substr($split[0],1))-intval(substr($split[1],0,strlen($split[1])-1)));

                }
                $equation=str_replace($current, $result, $equation);
                echo "<pre>".$current."=".$result."</pre>";
                echo "<pre>".$equation."=?</pre>";

            }

            // multiplication and division
            while(preg_match('/(\-)?([0-9])+[\*\/](\-)?([0-9])+/',$equation,$m)>0)
            {
                $current=$m[0];
                $split=preg_split("/\*/",$current);
                if(sizeof($split)==2)
                {
                    // multiplication
                    $result=(int)(intval($split[0])*intval($split[1]));
                }
                else
                {
                    $split=preg_split("/\//",$current);
                    // division
                    $result=(int)(intval($split[0])/intval($split[1]));

                }
                $equation=str_replace($current, $result, $equation);
                echo "<pre>".$current."=".$result."</pre>";
                echo "<pre>".$equation."=?</pre>";

            }
            // addition and substraction
            while(preg_match('/(\-)?([0-9])+[\+\-](\-)?([0-9])+/',$equation,$m)>0)
            {
                $current=$m[0];
                $split=preg_split("/\+/",$current);
                if(sizeof($split)==2)
                {
                    // multiplication
                    $result=(int)(intval($split[0])+intval($split[1]));
                }
                else
                {
                    $split=preg_split("/\-/",$current);
                    // division
                    $result=(int)(intval($split[0])-intval($split[1]));

                }
                $equation=str_replace($current, $result, $equation);
                echo "<pre>".$current."=".$result."</pre>";
                echo "<pre>".$equation."=?</pre>";

            }
            // echo "<pre>Equation=".$equation."</pre>";
            // echo "<pre>Start Brackets:</pre>";
            while(preg_match('/\((\-)?([0-9])+\)/',$equation,$m)>0)
            {
                $run=true;
                $current=$m[0];
                $equation=str_replace($current, substr($current, 1, strlen($current)-2), $equation);
                // echo "<pre>Equation=".$equation."</pre>";
            }
        }
        echo "<pre>$original=$equation</pre>";


    }


    $equation="32+64*(45-12*3)";
    solve_equation($equation);


?>
  

此代码的输出:

Equation. Begin: 32+64*(45-12*3)
-12*3=-36
32+64*(45-36)=?
32+64=96
96*(45-36)=?
45-36=9
96*(9)=?
96*9=864
864=?
32+64*(45-12*3)=864