php 优化条件价格计算工作没有这么多的如果分支

5hcedyr0  于 2023-05-05  发布在  PHP
关注(0)|答案(4)|浏览(103)

我如何优化这大量的如果?CO2支付从131开始并且在301结束,因此直到131为0并且超过301是与301相同的支付。尝试使用array,但不知道如何从数组中获取价格,如果数字对应于co2,则需要知道fuel_id。无法想象这段代码在switch()中应该是什么样子。

<?php
$co2 = '170'; // co2 number taking from DB

// fuel id taking from DB
(1, 'Petrol'),
(2, 'Diesel'),
(3, 'Electic'),
(4, 'Petrol / Gas'),
(5, 'Petrol / Hibrid'),
(6, 'Diesel / Hibrid'),
(7, 'Petrol / Hibrid / Plug-in'),
(8, 'Petrol / Hibrid / Gas'),
(9, 'Diesel / Hibrid / Plug-in');

echo $co2 ? 'Your co2 price is ' : '';

// Payment going from 131 co2
if($co2 >= 131 && $co2 <= 140){
    echo $fuel_id == 2 ? '40.47' : '';
    if($fuel_id == 1 || $fuel_id == 5 || $fuel_id == 7  || $fuel_id == 8) {echo '20.24';}
    if($fuel_id == 4 || $fuel_id == 8) {echo '18.21';}
}else if($co2 >= 141 && $co2 <= 150){
    echo $fuel_id == 2 ? '80.94' : '';
    if($fuel_id == 1 || $fuel_id == 5 || $fuel_id == 7  || $fuel_id == 8) {echo '40.47';}
    if($fuel_id == 4 || $fuel_id == 8) {echo '36.42';}
}else if($co2 >= 151 && $co2 <= 160){
    echo $fuel_id == 2 ? '121.41' : '';
    if($fuel_id == 1 || $fuel_id == 5 || $fuel_id == 7  || $fuel_id == 8) {echo '60.71';}
    if($fuel_id == 4 || $fuel_id == 8) {echo '54.63';}
}else if($co2 >= 161 && $co2 <= 170){
    echo $fuel_id == 2 ? '161.88' : '';
    if($fuel_id == 1 || $fuel_id == 5 || $fuel_id == 7  || $fuel_id == 8) {echo '80.94';}
    if($fuel_id == 4 || $fuel_id == 8) {echo '72.85';}
}else if($co2 >= 171 && $co2 <= 180){
    echo $fuel_id == 2 ? '202.35' : '';
    if($fuel_id == 1 || $fuel_id == 5 || $fuel_id == 7  || $fuel_id == 8) {echo '101.18';}
    if($fuel_id == 4 || $fuel_id == 8) {echo '91.06';}
}else if($co2 >= 181 && $co2 <= 190){
    echo $fuel_id == 2 ? '242.82' : '';
    if($fuel_id == 1 || $fuel_id == 5 || $fuel_id == 7  || $fuel_id == 8) {echo '121.41';}
    if($fuel_id == 4 || $fuel_id == 8) {echo '109.27';}
}else if($co2 >= 191 && $co2 <= 200){
    echo $fuel_id == 2 ? '283.29' : '';
    if($fuel_id == 1 || $fuel_id == 5 || $fuel_id == 7  || $fuel_id == 8) {echo '141.65';}
    if($fuel_id == 4 || $fuel_id == 8) {echo '127.48';}
}else if($co2 >= 201 && $co2 <= 210){
    echo $fuel_id == 2 ? '323.76' : '';
    if($fuel_id == 1 || $fuel_id == 5 || $fuel_id == 7  || $fuel_id == 8) {echo '161.88';}
    if($fuel_id == 4 || $fuel_id == 8) {echo '145.69';}
}else if($co2 >= 211 && $co2 <= 220){
    echo $fuel_id == 2 ? '364.23' : '';
    if($fuel_id == 1 || $fuel_id == 5 || $fuel_id == 7  || $fuel_id == 8) {echo '182.12';}
    if($fuel_id == 4 || $fuel_id == 8) {echo '163.9';}
}else if($co2 >= 221 && $co2 <= 230){
    echo $fuel_id == 2 ? '404.7' : '';
    if($fuel_id == 1 || $fuel_id == 5 || $fuel_id == 7  || $fuel_id == 8) {echo '202.35';}
    if($fuel_id == 4 || $fuel_id == 8) {echo '182.12';}
}else if($co2 >= 231 && $co2 <= 240){
    echo $fuel_id == 2 ? '445.17' : '';
    if($fuel_id == 1 || $fuel_id == 5 || $fuel_id == 7  || $fuel_id == 8) {echo '222.59';}
    if($fuel_id == 4 || $fuel_id == 8) {echo '200.33';}
}else if($co2 >= 241 && $co2 <= 250){
    echo $fuel_id == 2 ? '485.64' : '';
    if($fuel_id == 1 || $fuel_id == 5 || $fuel_id == 7  || $fuel_id == 8) {echo '242.82';}
    if($fuel_id == 4 || $fuel_id == 8) {echo '218.54';}
}else if($co2 >= 251 && $co2 <= 260){
    echo $fuel_id == 2 ? '526.11' : '';
    if($fuel_id == 1 || $fuel_id == 5 || $fuel_id == 7  || $fuel_id == 8) {echo '263.06';}
    if($fuel_id == 4 || $fuel_id == 8) {echo '236.75';}
}else if($co2 >= 261 && $co2 <= 270){
    echo $fuel_id == 2 ? '566.58' : '';
    if($fuel_id == 1 || $fuel_id == 5 || $fuel_id == 7  || $fuel_id == 8) {echo '283.29';}
    if($fuel_id == 4 || $fuel_id == 8) {echo '254.96';}
}else if($co2 >= 271 && $co2 <= 280){
    echo $fuel_id == 2 ? '607.05' : '';
    if($fuel_id == 1 || $fuel_id == 5 || $fuel_id == 7  || $fuel_id == 8) {echo '303.53';}
    if($fuel_id == 4 || $fuel_id == 8) {echo '273.17';}
}else if($co2 >= 281 && $co2 <= 290){
    echo $fuel_id == 2 ? '647.52' : '';
    if($fuel_id == 1 || $fuel_id == 5 || $fuel_id == 7  || $fuel_id == 8) {echo '323.76';}
    if($fuel_id == 4 || $fuel_id == 8) {echo '291.38';}
}else if($co2 >= 291 && $co2 <= 300){
    echo $fuel_id == 2 ? '687.99' : '';
    if($fuel_id == 1 || $fuel_id == 5 || $fuel_id == 7  || $fuel_id == 8) {echo '344';}
    if($fuel_id == 4 || $fuel_id == 8) {echo '309.6';}
}else if($co2 >= 301){
    echo $fuel_id == 2 ? '728.46' : '';
    if($fuel_id == 1 || $fuel_id == 5 || $fuel_id == 7  || $fuel_id == 8) {echo '364.23';}
    if($fuel_id == 4 || $fuel_id == 8) {echo '327.81';}
}

echo $co2 ? ' $' : '';
?>
pn9klfpd

pn9klfpd1#

$co2 = '170';
$fuel_id = 1;

$type = match ( $fuel_id ) {
  2          => 't2',
  1, 5, 7, 8 => 't1578',
  4, 9       => 't49',
  default    => ''
};

$result = match ( true ) {
  $co2 >= 131 && $co2 <= 140 => [ 't2' => '40.47', 't1578' => '20.24', 't49' => '18.21' ],
  $co2 >= 141 && $co2 <= 150 => [ 't2' => '80.94', 't1578' => '40.47', 't49' => '36.42' ],
  $co2 >= 151 && $co2 <= 160 => [ 't2' => '121.41', 't1578' => '60.71', 't49' => '54.63' ],
  $co2 >= 161 && $co2 <= 170 => [ 't2' => '161.88', 't1578' => '80.94', 't49' => '72.85' ],
  // ...
  $co2 >= 301                => [ 't2' => '728.46', 't1578' => '364.23', 't49' => '327.81' ],
  default                    => [ 't2' => '', 't1578' => '', 't49' => '' ]
};

echo $result[$type] ?: 'no match';

所以看起来数据的结构如下:
每个范围(131 - 140、141 - 150等)具有$fuel_id的值,其是第一条目(范围131 - 140的条目)的值的倍数。
因此,基本上可以从$co2中减去130,将其除以每个范围的长度(10),将结果取最大值,这给出了第一个条目中的这些“开始”值的乘数,范围为131 - 140。
请注意,与OP中的数据相比,结果将略微偏离(仅对于某些条目,偏离0.01到0.0x)。

$co2 = random_int(131, 400);
$fuel_id = random_int(1, 9);

$start = [
  1 => 20.24,
  2 => 40.47,
  3 => 0,       // no value for $fuel_id = 3 in the OP
  4 => 18.21,
  5 => 20.24,
  6 => 0,       // no value for $fuel_id = 6 in the OP
  7 => 20.24,
  8 => 20.24,   // or 18.21 ( 8 is used in two places
  9 => 18.21    // or 20.24   in the code of the OP )
];

$result = $start[$fuel_id] * ceil(( $co2 - 130 ) / 10);

echo "co2: $co2 & fuel_id: $fuel_id -> $result";
k7fdbhmy

k7fdbhmy2#

打破逻辑。
你有一个$co2值,和一个严格的类型Map,实际上只是3个类别的重复。

class CO2MapThing {

    private const TYPE_HIGH = 0;
    private const TYPE_MED  = 1;
    private const TYPE_LOW  = 2;

    public function __construct(protected array $map) {}

    protected function mapType(int $type) {
        // FYI: your code has no 3/6/9, and 8 is listed twice, but this is how your code shakes out.
        switch($type) {
            case 2: return self::TYPE_HIGH;
            case 1:
            case 5:
            case 7:
            case 8: return self::TYPE_MED;
            case 4: return self::TYPE_LOW;
            default:
                throw new \Exception("Unknown type: $type");
        }
    }

    public function getCost(int $co2, int $type) {
        foreach( $this->map as $key => $value) {
            if( $key > $co2 ) {
                return $this->map[$key][$this->mapType($type)];
            }
        }
    }
}

$map = [
    120 => [   '0.00',   '0.00',   '0.00' ],
    130 => [  '40.47',  '20.24',  '18.21' ],
    140 => [  '80.94',  '40.47',  '36.42' ],
    150 => [ '121.41',  '60.71',  '54.63' ],
    // ...
    PHP_INT_MAX =>
           [ '728.46', '364.23', '327.81' ]
];

$m = new CO2MapThing($map);

var_dump(
    $m->getCost(110,1),
    $m->getCost(120,1),
    $m->getCost(130,1),
    $m->getCost(990,1)
);

输出:

string(4) "0.00"
string(5) "20.24"
string(5) "40.47"
string(6) "364.23"

如果我真的知道这些值中的任何一个实际上意味着什么,或者美元值是如何确定的,我可能会想出一些更清晰的东西,但我认为就清晰度/可读性/可维护性而言,这已经比其他答案领先了一英里。

ohtdti5x

ohtdti5x3#

使用大量静态写入的数字是将错别字和人为错误引入算法的一种方式。更明智的做法是依靠数学,而不是手工编写这么多条件结果。
与人类可读性和可维护性相关的另一个重要问题是脚本中的所有“魔法数字”。他们是什么?这是什么意思?什么时候可以安全地修改?为了清晰起见,在文件的顶部设计一些指示性常数可能是一个好主意。
综上所述,我注意到您的代码注解表明输入数据来自您的数据库。(// co2 number taking from DB// fuel id taking from DB)我发现自己同意YourCommonSense的观点,即整个任务可以而且应该在SQL中处理。以下脚本演示如何编写单个静态查询来计算所需的输出。这种方法的优点在于,您可能永远不需要更改代码库(或者不经常),因为您可能进行的任何调整都将基于实际数据(而不是过程)。
这里有一些基本的表格(我不打算在这里对设计策略大惊小怪;按您的意愿调整)

CREATE TABLE users (
    id INT AUTO_INCREMENT,
    name VARCHAR(100),
    fuel_id INT,
    co2 INT,
    PRIMARY KEY (id)
);

INSERT INTO users(name, fuel_id, co2) VALUES
('Abe', 5, 170),
('Bud', 4, 111),
('Cid', 2, 333),
('Dot', 1, 301),
('Eli', 8, 250),
('Fay', 9, 266);

CREATE TABLE fuel (
    id INT AUTO_INCREMENT,
    name VARCHAR(100),
    cost DECIMAL(5,2),
    PRIMARY KEY (id)
);

INSERT INTO fuel(name, cost) VALUES
('Petrol',                    20.24),
('Diesel',                    40.47),
('Electic',                    0.00),
('Petrol / Gas',              18.21),
('Petrol / Hibrid',           20.24),
('Diesel / Hibrid',            0.00),
('Petrol / Hibrid / Plug-in', 20.24),
('Petrol / Hibrid / Gas',     20.24),
('Diesel / Hibrid / Plug-in',  0.00);

CREATE TABLE limitations (
    id INT AUTO_INCREMENT,
    term VARCHAR(100),
    amount INT,
    PRIMARY KEY (id)
);

INSERT INTO limitations(term, amount) VALUES
('billable_co2_min', 130),
('billable_co2_max', 301),
('billable_co2_level_size', 10);

现在处理SQL(PHPize-online Demo

SELECT users.name,
       users.co2,
       fuel.name AS fuel_name,
       fuel.cost AS fuel_type_cost,
       ROUND(
           fuel.cost *
           CEIL(
            (
             GREATEST(
                 billable_co2_min,
                 LEAST(users.co2, billable_co2_max)
             )
             - billable_co2_min
            )
            / billable_co2_level_size
           ),
           2
       ) AS total
FROM users
JOIN fuel ON users.fuel_id = fuel.id
JOIN (SELECT amount AS billable_co2_min FROM limitations WHERE term = 'billable_co2_min' LIMIT 1) config1
JOIN (SELECT amount AS billable_co2_max FROM limitations WHERE term = 'billable_co2_max' LIMIT 1) config2
JOIN (SELECT amount AS billable_co2_level_size FROM limitations WHERE term = 'billable_co2_level_size' LIMIT 1) config3

我的结果可能与你的略有不同,但那是因为我不相信20.24 + 20.24 = 40.47。如果你真的需要不一致的定价,那么你可以/应该在你的数据库中创建一个定价表,并加入其中。

0yg35tkg

0yg35tkg4#

如果您使用的是PHP版本8或更高版本的match,则可以使代码更紧凑:

echo match (true) {
    ($co2 < 131) => '',

    ($co2 <= 140) => 
        match ($fuel_id) {
            2 => '40.47',
            1, 5, 7, 8 => '20.24',
            4 => '18.21',
            default => '',
        },
    ($co2 <= 150) => 
        match ($fuel_id) {
            2 => '80.94',
            1, 5, 7, 8 => '40.47',
            4 => '36.42',
            default => '',
        },
    // etc for other boundary points on $co2
     
    default => '',
};

顶层match中的条件是按顺序计算的,所以不需要继续检查下限(即如果它不匹配$co2 <= 140,那么当我们计算下一个条件$co2 <= 150时,我们已经知道$co2大于140,依此类推)

相关问题