首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >递归(?)算法设计

递归(?)算法设计
EN

Stack Overflow用户
提问于 2015-09-26 08:38:38
回答 3查看 159关注 0票数 5

我有一个要求,允许我的最终用户输入公式,就像一个电子表格。我有这样一个数组:

代码语言:javascript
复制
$table = array(
    1=>array(
            "id"=>1,
            "Name"=>"Regulating",
            "Quantity"=>"[2]Quantity+[3]Value",
            "Value"=>"[2]Cost"
        ),
...)

第一级数组键总是与该数组中的id键相同的值。

表列的例子如下:

代码语言:javascript
复制
id  Name        Quantity                Value
1   Regulating  [2]Quantity+[3]Value    [2]Cost
2   Kerbs       3                       6
3   Bricks      9                       7
4   Sausages    [3]Cost                 3
5   Bamboo      [4]Quantity             [7]Cost
6   Clams       [4]Quantity             NULL
7   Hardcore    [3]Quantity*0.5         12
8   Beetles     [6]Quantity*[4]Value    [2]Value

数量键和值键表示引用id和数量、值或成本的公式。

成本是通过将价值和数量相乘得出的。

我正在使用:

代码语言:javascript
复制
preg_match_all("/\[(.*?)\]([A-Z]*[a-z]*)/", $string, $matches, PREG_SET_ORDER);

,它输出类似于[1][Quantity]的数组。

代码语言:javascript
复制
Array
(
    [0] => Array
        (
            [0] => [2]Quantity
            [1] => 2
            [2] => Quantity
        )

    [1] => Array
        (
            [0] => [3]Value
            [1] => 3
            [2] => Value
        )

)

使用类似于:$calcString = $table1;`的方法迭代表

代码语言:javascript
复制
foreach ($matches as $match) {
    $calcString = str_replace($match[0], $table[$match[1]][$match[2]], $calcString);
}

我可以得到要计算的字符串,并使用matheval类进行和。

例如

代码语言:javascript
复制
[1]Quantity = [2]Quantity + [3]Value
[2]Quantity = 3
[3]Value = 7 // [1]Quantity = 3 + 7 = 10

[1]Value = [2]Cost
[2]Cost = [2]Quantity * [2]Value // 3 * 6 = 18

基本上,表中的变量引用同一表中的其他[id]key

但这是我的问题

我需要解决对表中其他部分的引用(它们本身可能是公式,也可能不是公式)来填补空白。这是在我的舒适范围之外,我会感谢任何建议(甚至更好的功能代码),为我如何能够实现这一点提供启示。

谢谢

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2015-09-27 06:37:18

一个简单的危险地,和您的具体情况,执行良好的解决方案!

代码语言:javascript
复制
<?php
class solver {
    private
            // The final output array
            $arr_evaled,
            // When a cell gains its final value, the corresponding entry in the following array gets marked as being done!
            $arr_done;

    private $solving_iterations_count;

    public function solver($array) {
        $this->arr_done = array();

        foreach($array as $k => $arr)
            $this->arr_done[$k] = array('Quantity' => false, 'Value' => false);

        // Firstly,expand all of the "[x]Cost"s to "([x]Quantity*[x]Value)"s!
        $this->arr_evaled = array_map(
            function($v){ return preg_replace('#\[(\d*?)\]Cost#', '([$1]Quantity*[$1]Value)', $v); },
            $array
        );

        $this->solving_iterations_count = 0;
        $this->solve();
    }

    private function isDone() {
        foreach($this->arr_done as $a)
            if($a['Quantity'] == false || $a['Value'] == false)
                return false;
        return true;
    }
    private function isCellDone($id, $fieldName) {
        return $this->arr_done[$id][$fieldName];
    }
    private function markCellAsDone($id, $fieldName, $evaluation) {
        $this->arr_done[$id][$fieldName] = true;
        $this->arr_evaled[$id][$fieldName] = $evaluation;
    }
    private function isEvaluable($str) {
        return preg_match('#^[0-9*+-\/\(\)\.]*$#', $str) == 1 || strtolower($str)=='null';
    }
    private function replace($from, $to) {
        foreach($this->arr_evaled as &$arr) {
            $arr['Quantity'] = str_replace($from, $to, $arr['Quantity']);
            $arr['Value'] = str_replace($from, $to, $arr['Value']);
        }
    }

    private function solve() {
        $isSolvable = true; // YOUR TODO: I believe coding this part is also fun!) (e.g: check for "reference cycles")
        if(!$isSolvable) return null;

        while( !$this->isDone() )
        {
            foreach($this->arr_evaled as $arr) {
                foreach(['Quantity', 'Value'] as $fieldName) {
                    if(!$this->isCellDone($arr['id'], $fieldName)) {
                        if($this->isEvaluable($arr[$fieldName])) {
                            $evaluation = eval("return {$arr[$fieldName]};");
                            $this->markCellAsDone($arr['id'], $fieldName, $evaluation);
                            $this->replace("[{$arr['id']}]$fieldName", "$evaluation");
                        }
                    }
                }
            }
            $this->solving_iterations_count++;
        }
        foreach($this->arr_evaled as &$row)
            $row['Cost'] = $row['Quantity'] * $row['Value'];
        return $this->arr_evaled;
    }

    public function print_tabulated() {
        echo "The count of solving iterations: {$this->solving_iterations_count}<br/><br/>";
        echo '<table border="1"><tr><th>id</th><th>Name</th><th>Quantity</th><th>Value</th><th>Cost</th></tr>';
        foreach($this->arr_evaled as $arr)
            echo "<tr><td>{$arr['id']}</td><td>{$arr['Name']}</td><td>{$arr['Quantity']}</td><td>{$arr['Value']}</td><td>{$arr['Cost']}</td></tr>";
        echo '</table>';
    }
}

// Testing
$arr = array(
    1 => array( 'id' => 1, 'Name' => 'Regulating', 'Quantity' => '[2]Quantity+[3]Value', 'Value' => '[2]Cost'  ),
    2 => array( 'id' => 2, 'Name' => 'Kerbs',      'Quantity' => '3',                    'Value' => '6'        ),
    3 => array( 'id' => 3, 'Name' => 'Bricks',     'Quantity' => '9',                    'Value' => '7'        ),
    4 => array( 'id' => 4, 'Name' => 'Sausages',   'Quantity' => '[3]Cost',              'Value' => '3'        ),
    5 => array( 'id' => 5, 'Name' => 'Bamboo',     'Quantity' => '[4]Quantity',          'Value' => '[7]Cost'  ),
    6 => array( 'id' => 6, 'Name' => 'Clams',      'Quantity' => '[4]Quantity',          'Value' => 'NULL'     ),
    7 => array( 'id' => 7, 'Name' => 'Hardcore',   'Quantity' => '[3]Quantity*0.5',      'Value' => '12'       ),
    8 => array( 'id' => 8, 'Name' => 'Beetles',    'Quantity' => '[6]Quantity*[4]Value', 'Value' => '[2]Value' ),
);
echo '<pre>';
(new solver($arr))->print_tabulated();

这是输出:

票数 1
EN

Stack Overflow用户

发布于 2015-09-26 10:30:40

在内心深处,你已经知道如何解决这个问题了,你只是被任务吓到了。

递归方法是立即扩展引用。例如,

代码语言:javascript
复制
expand('[1]Value') # returns '[2]Cost'
  expand('[2]Cost') # returns '[2]Quantity * [2]Value'
    expand('[2]Quantity') # returns 3
    expand('[2]Value') # returns 6
    eval('3 * 6')
    # returns 18
  # returns 18
# returns 18

一种迭代(非递归)方法是一次展开一个引用并重复,直到字符串中有未解决的引用。

代码语言:javascript
复制
expand('[1]Value') // returns '[2]Cost'
expand('[2]Cost')  // returns '[2]Quantity + [2]Value'
expand('[2]Quantity + [2]Value') // returns 3 for [2]Quantity
expand('3 * [2]Value')  // returns 6 for [2]Value
eval('3 * 6') 
# returns 18

通常,我更喜欢迭代解决方案,因为它们不太容易发生堆栈溢出。然而,递归解决方案通常更容易编写。

下面是一个快速叠加的递归评估器:https://gist.github.com/stulentsev/b270bce4be67bc1a96ae (不过是用红宝石写的)

票数 3
EN

Stack Overflow用户

发布于 2015-09-26 19:28:02

如果calcString的大小合理,并且不期望替换变得过于精细,您可以使用一个while循环来模拟递归。下面是一个在修改字符串的过程中输出字符串的示例:

代码语言:javascript
复制
$calcString = $table[8]['Quantity'];

preg_match_all("/\[(.*?)\]([A-Z]*[a-z]*)/", $calcString, $matches, PREG_SET_ORDER);

print_r($calcString . "\n");

while (!empty($matches)){
  foreach ($matches as $match) {
    preg_match_all("/\[(.*?)\](Cost)/", $match[0], $matchCost, PREG_SET_ORDER);

    if (!empty($matchCost)){
      $cost = $table[$matchCost[0][1]]['Quantity'] . "*" . $table[$matchCost[0][1]]['Value'];
      $calcString = str_replace($match[0], $cost, $calcString);
    } else {
      $calcString = str_replace($match[0], $table[$match[1]][$match[2]], $calcString);
    }

    print_r($calcString . "\n");

  }
  preg_match_all("/\[(.*?)\]([A-Z]*[a-z]*)/", $calcString, $matches, PREG_SET_ORDER);
}

输出:

代码语言:javascript
复制
[6]Quantity*[4]Value
[4]Quantity*[4]Value
[4]Quantity*3
[3]Cost*3
9*7*3

表变量:

代码语言:javascript
复制
$table = array(
  1 => array(
         "id" => 1,
         "Name" => "Regulating",
         "Quantity" => "[2]Quantity+[3]Value",
         "Value" => "[2]Cost"
       ),
  2 => array(
         "id" => 2,
         "Name" => "Kerbs",
         "Quantity" => 3,
         "Value" => 6
       ),
  3 => array(
         "id" => 3,
         "Name"=>"Bricks",
         "Quantity"=> 9,
         "Value"=> 7
       ),
  4 => array(
         "id" => 2,
         "Name" => "Sausages",
         "Quantity" => "[3]Cost",
         "Value" => 3
       ),
  5 => array(
         "id" => 2,
         "Name" => "Bamboo",
         "Quantity" => "[4]Quantity",
         "Value" => "[7]Cost"
       ),
  6 => array(
         "id" => 2,
         "Name" => "Clams",
         "Quantity" => "[4]Quantity",
         "Value" => NULL
       ),
  7 => array(
         "id" => 2,
         "Name" => "Hardcore",
         "Quantity" => "[3]Quantity*0.5",
         "Value" => 12
       ),
  8 => array(
         "id" => 2,
         "Name" => "Beetles",
         "Quantity" => "[6]Quantity*[4]Value",
         "Value" => "[2]Value"
       )
);
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/32795217

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档