1

我着手围绕一组返回生成器的类(php 5.5)制作一个小项目。

这个小项目的主要动机是扩展我的 TDD 之旅,摆弄生成器,并有一个我可以扔到 packagist 上供以后使用的包。

整个“项目”的当前状态可以在Github找到

所有测试都是绿色的,方法可以满足我的要求。现在我想重构,因为我有很多重复。

    /**
    *   Returns a Generator with a even range.
    *
    *   getEven(10); // 10,12,14,16,18,20,22 ...
    *   getEven(null, 10); // 10,8,6,4,2,0,-2,-4 ...
    *   getEven(10, null, 2); // 10,6,2, -2 ...
    *   getEven(10,20); // 10,12,14,16,18,20
    *   getEven(20,10); // 20,18,16,14,12,10
    *   getEven(10,20,2); // 10,14,18
    *
    *   @param int|null $start
    *   @param int|null $end
    *   @param int $step
    *   @throws InvalidArgumentException|LogicException
    *   @return Generator
    */
    public function getEven( $start = null, $end = null, $step = 1 )
    {
        // Throws LogicException
        $this->throwExceptionIfAllNulls( [$start, $end] );
        $this->throwExceptionIfInvalidStep($step);

        // Throws InvalidArgumentException
        $this->throwExceptionIfNotNullOrInt( [$start, $end] );

        // infinite increase range
        if(is_int($start) && is_null($end))
        {
            // throw LogicException
            $this->throwExceptionIfOdd($start);

            $Generator = function() use ($start, $step)
            {
                for($i = $start; true; $i += $step * 2)
                {
                    yield $i;
                }
            };
        }
        // infinite decrease range
        elseif(is_int($end) && is_null($start))
        {
            // throws LogicException
            $this->throwExceptionIfUneven($end);

            $Generator =  function() use ($end, $step)
            {
                for($i = $end; true; $i -= $step * 2)
                {
                    yield $i;
                }
            };
        }
        // predetermined range
        else 
        {
            // throws LogicException
            $this->throwExceptionIfUneven($start);
            $this->throwExceptionIfUneven($end);

            // decrease
            if($start >= $end)
            {
                $Generator = function() use ($start, $end, $step)
                {
                    for($i = $start; $i >= $end; $i -= $step * 2)
                    {
                        yield $i;
                    }
                };
            }
            // increase
            else
            {
                $Generator = function() use ($start, $end, $step)
                {
                    for($i = $start; $i <= $end; $i += $step * 2)
                    {
                        yield $i;
                    }
                };
            }
        }

        return $Generator();
    }

该类还有一个名为 getOdd 的方法(是的,它看起来很像;))

主要的重复是闭包$Generator = function() ...,不同之处主要在于 for 循环中的运算符+ - * /和参数。这在其余班级中基本相同。

我阅读了 PHP 中的动态比较运算符,得出的结论是没有像这样的本地方法compare(...)

我是否应该使用私有/受保护的方法进行比较。如果是这样,我应该为此创建一个新的类/函数吗?我认为它不属于当前班级。

我还缺少其他东西吗,我不确定如何以适当的方式将其干燥?

顺便提一句。知道一个 getEven,当我得到一个带有 step 函数的 getRange 时,getOdd 有点傻,但这是一个更一般的重构/模式问题。

更新 @github,getEven 和 getOdd 现在已被删除...

4

1 回答 1

1

下面的代码尚未经过测试或验证可以工作,但我相信它,至少它显示了删除多个生成器函数的一种可能方法。

正如您所说,您要删除的重复项主要在生成器函数中。如果你研究一下,你会发现你拥有的每个生成器函数都可以写成这样:

function createGenerator($index, $limit, $step) {
    return function() use($index, $limit, $step) {
        $incrementing = $step > 0;
        for ($i = $index; true; $i += 2 * $step) {
            if (($incrementing && $i <= $limit) || (!$incrementing && $i >= $limit)) {
                yield $i;
            }else {
                break;
            }
        }
    };
}

为了利用它,您需要对输入参数做一些魔术,它有助于(至少使它漂亮)定义一些常量。PHP 已经获得了一个PHP_INT_MAX常量,该常量可以保持整数的最大可能值,但是它没有PHP_INT_MIN. 所以我会把它定义为它自己的常数。

define('PHP_INT_MIN', ~PHP_INT_MAX);

现在让我们看一下函数中的四种情况。

1)无限增加范围

Infinte 在这里是一个相当大胆的主张,如果我们将其更改为“给定 int 的约束可能的最大值”,我们将得到一个从$index到的有限范围PHP_INT_MAX,因此通过设置$limit = PHP_INT_MAX;上述生成器函数仍然是相同的。

//$limit = PHP_INT_MAX;
createGenerator($index, PHP_INT_MAX, $step);

2) 无限下降范围

在这里可以再次使用与上面相同的参数,但使用否定$step和交换$indexand $limit;

//$index = $limit;
//$limit = PHP_INT_MIN;
//$step *= -1;
createGenerator($limit, PHP_INT_MIN, -1 * $step);

3) 预定递减范围

再次交换和否定。

//$temp = $index;
//$index = $limit;
//$limit = $temp;
//$step *= -1;
createGenerator($limit, $index, -1 * $step);

4) 预定增加范围

好吧,这只是默认情况,其中给出了所有参数。没有什么需要改变的。

createGenerator($index, $limit, $step);

修改后的代码

public function getEven($index = null, $limit = null, $step = 1) {
    // Throws LogicException
    $this->throwExceptionIfAllNulls([$index, $limit]);
    $this->throwExceptionIfInvalidStep($step);

    // Throws InvalidArgumentException
    $this->throwExceptionIfNotNullOrInt([$index, $limit]);

    //Generator function
    function createGenerator($index, $limit, $step) {
        return function() use($index, $limit, $step) {
            $incrementing = $step > 0;
            for ($i = $index; true; $i += 2 * $step) {
                if (($incrementing && $i <= $limit) || (!$incrementing && $i >= $limit)) {
                    yield $i;
                }else {
                    break;
                }
            }
        };
    }
    // infinite increase range
    if (is_int($index) && is_null($limit)) {
        // throw LogicException
        $this->throwExceptionIfodd($index);
        return createGenerator($index, PHP_INT_MAX, $step);
    }
    // infinite decrease range
    elseif (is_int($limit) && is_null($index)) {
        // throws LogicException
        $this->throwExceptionIfodd($limit);
        return createGenerator($limit, PHP_INT_MIN, -1*$step);
    }
    // predetermined range
    else {
        // throws LogicException
        $this->throwExceptionIfodd($index);
        $this->throwExceptionIfodd($limit);

        // decrease
        if ($index >= $limit) {
            return createGenerator($limit, $index, -1 * $step);
        }
        return createGenerator($index, $limit, $step);
    }
}
于 2013-10-22T00:03:46.313 回答