如何检查一个整数是否在一个范围内?

81

有没有一种方法可以测试一个区间,而不需要重复编写代码:

if ($int>$min && $int<$max)

像一个函数:

function testRange($int,$min,$max){
    return ($min<$int && $int<$max);
}

使用方法:

if (testRange($int,$min,$max)) 

PHP 有这样的内置函数吗?或者有其他方法可以实现吗?


9
除了使用 in_array()range() 的可怕方法外,我认为没有其他方法。 - Pekka
8
为什么你称之为“冗余代码”?这是必须被评估的内容,不是吗? - Nanne
3
确实。那原始的快速可读代码中哪一部分是多余的呢?而且,仅仅从调用testRange()这个函数就能知道testRange(1, 1, 2)是真还是假吗? - Matt Gibson
4
他所指的是在条件语句中需要重复声明相同的变量。尽管我同意一种能让你说if($int between $min and $max)的语言结构会很好,但我担心我见过唯一实现这个功能的语言是SQL。 - Wes P
2
@是的,就我个人而言,我认为使用函数会引入更多可能出错的可能性——例如,可能会将参数顺序搞错。此外,你是否想再添加另一个函数来比较($int >= $min && $int <= $max),这也是非常常见的比较?如果你一遍又一遍地编写这个特定的代码,那么如果你不介意额外的开销,函数就是走的方式,但是要自己编写,因为 PHP 中没有内置的函数。(顺便说一句,我不是匿名的投票者,只是对交流感兴趣 ;) ) - Matt Gibson
显示剩余8条评论
10个回答

105

用1000000次循环测试了你的3种方法。

t1_test1: ($val >= $min && $val <= $max): 0.3823毫秒

t2_test2: (in_array($val, range($min, $max)): 9.3301毫秒

t3_test3: (max(min($var, $max), $min) == $val): 0.7272毫秒

T1最快,基本上就是这个:

function t1($val, $min, $max) {
  return ($val >= $min && $val <= $max);
}

这正是我所期望的 :) - alex
1
不错!为了让它告诉我是否为真或假,我添加了以下内容:function t1($val, $min, $max) { if ($val >= $min && $val <= $max) {return true;} else {return false;} } - Pathros
13
以上函数已经返回了 truefalse,不需要编写其他代码。 - SBH
也不必使用括号。"return $val >= $min && $val <= $max;" 就足够了。 - ElChupacabra
3
我认为问题并不是在问性能方面的事情。其他语言有本地运算符来处理这个问题,我认为这就是 OP 所寻找的。 - Josh Johnson
另一个使用这个变量(filter_var)的测试仍然很有趣: https://dev59.com/WW445IYBdhLWcg3w0dZD - jMike

48

我认为没有比你的函数更好的方法。

它清晰易懂,容易理解,并返回条件的结果(没有复杂的return (...) ? true : false)。


你所说的“返回混乱”是什么?能详细解释一下吗?可以举个好与坏实践的例子吗?谢谢! - elbowlobstercowstand

16

虽然没有内置函数,但你可以通过适当地调用min()max()函数来轻松实现。

// Limit integer between 1 and 100000
$var = max(min($var, 100000), 1);

9
非常有趣。然而,我不会仅仅为了可读性而使用它。 - aalaap
如上所述,这个想法确实很有趣,但它很难阅读,并且调用了两个可以避免的函数。 - AnthonyB

12

大多数给定的例子都假设测试范围[$a..$b],$a <= $b,即范围的极端值是按低到高的顺序排列的,并且大多数假设所有值都是整数。


但我需要一个函数来测试$n$是否在$a$和$b$之间,如此处所述:

Check if $n is between $a and $b even if:
    $a < $b  
    $a > $b
    $a = $b

All numbers can be real, not only integer.

有一种简单的测试方法。
我基于这样一个事实进行测试,即当$n$在$a$和$b$之间时,($n-$a)($n-$b)的符号不同,而当$n$在$a$..$b$范围之外时,它们的符号相同。
此函数适用于测试递增、递减、正数和负数,而不仅限于测试整数。

function between($n, $a, $b)
{
    return (($a==$n)&&($b==$n))? true : ($n-$a)*($n-$b)<0;
}

2
这很好。请注意,如果$n,$a和$b可能是相同的数字,那么此函数将返回false,而大多数其他函数将返回true。 - bksunday
1
感谢 @bksunday 的评论。我已经将该情况的处理添加到了代码中。 - Luis Rosety
我需要对@bksunday的评论和我的回答进行精确说明。区间可以是开区间或闭区间。开区间不包括限制,而闭区间则包括它们。因此,在dynamic的原始问题的情况下,它是一个开区间,因此当$a=$b=$n时,它应该返回false,就像我的原始函数一样,甚至更简单:一个开区间($a, $b) 且$a=$b是一个空集。 - Luis Rosety
如果$n=0$,$a=0$,且$b=1$,那么这个函数将返回FALSE。为什么?因为0在范围内(介于0和1之间),所以应该返回TRUE。 - Tikky

9

我无法发表评论(声望不够),所以在此修改Luis Rosety的回答:

function between($n, $a, $b) {
    return ($n-$a)*($n-$b) <= 0;
}

这个函数在n等于a或n等于b的情况下也有效。
证明: 假设n属于[a,b]范围,其中[a,b]是实数的子集。
现在有a <= n <= b。那么n-a >= 0并且n-b <= 0。这意味着(n-a)*(n-b) <= 0。
情况b <= n <= a同样适用。

区间类型(开放或闭合)非常重要。开放区间不包括限制,而闭合区间则包括限制。请参考https://en.wikipedia.org/wiki/Interval_(mathematics)。 - Luis Rosety
当使用浮点数时,这是正确的。但是,当使用整数(如问题所述)时,这是无关紧要的。请记住,(a,b) 实际上是所有整数 a 和 b 的 [a+1, b-1]。 - Esa Lindqvist
最适合我的答案 - Tikky

3
您可以使用in_array()range()来完成此操作。
if (in_array($value, range($min, $max))) {
    // Value is in range
}

注意 然而,正如评论中所指出的那样,如果你关注性能,这并不是一个好的解决方案。生成一个数组(特别是在较大的范围内)会减慢执行速度。


2
@Yukulélé 这个问题是:如何检查一个整数是否在一个范围内? - Peter
请看我对Nebuloasar的回答。 - Patrick Allaert
3
这个想法很有趣且易于理解,但使用这种方式的性能较差。 - AnthonyB
1
我认为这是一个不错的选择,感谢您的理解。因此,我取消了我的反对票。 - AnthonyB

3

除此之外,还有一个叫做filter_var()的本地函数可以检查范围。它不能返回完全符合要求的值(永远不会返回true),但我们可以通过“欺骗”的方式进行更改。

我认为这不是一段好的代码,因为可读性不够好,但我展示它作为一种可能性:

return (filter_var($someNumber, FILTER_VALIDATE_INT, ['options' => ['min_range' => $min, 'max_range' => $max]]) !== false)

只需填写$someNumber$min$max,使用该过滤器的filter_var函数将在数字超出范围时返回布尔值false,而在范围内时返回数字本身。表达式(!== false)使函数在数字在范围内时返回true。
如果您想缩短代码,请记住进行类型转换。如果使用!=,则在范围为-5;+5内的数字0会返回false(而应返回true)。如果使用类型转换((bool))也会发生同样的情况。
// EXAMPLE OF WRONG USE, GIVES WRONG RESULTS WITH "0"
(bool)filter_var($someNumber, FILTER_VALIDATE_INT, ['options' => ['min_range' => $min, 'max_range' => $max]])
if (filter_var($someNumber, FILTER_VALIDATE_INT, ['options' => ['min_range' => $min, 'max_range' => $max]])) ...

想象一下(来自其他回答):

if(in_array($userScore, range(-5, 5))) echo 'your score is correct'; else echo 'incorrect, enter again';

如果用户输入空值($userScore = ''),这是正确的,因为in_array在这里被设置为默认的非严格模式,这意味着范围也会创建0,而'' == 0(非严格模式),但'' !== 0(如果您使用严格模式)。很容易错过这样的事情,这就是为什么我写了一点关于这个的原因。我学到的是严格运算符是默认的,程序员只能在特殊情况下使用非严格运算符。我认为这是一个好的教训。这里的大多数例子都会在某些情况下失败,因为没有使用严格检查。

尽管如此,我喜欢filter_var,您可以使用上面(或下面,如果我已经“上升”)的函数并创建自己的回调函数,将其用作FILTER_CALLBACK过滤器。您可以返回布尔值,甚至添加openRange参数。另一个好处是,您可以使用其他函数,例如检查数组或POST/GET值的每个数字的范围。这是一个非常强大的工具。


2

使用比较运算符比调用任何函数都要快得多。我不确定这是否存在,但我认为它不存在。


实际上,使用比较运算符比调用函数更快,因为在PHP中函数很慢。另一方面,只有一个独立的函数可以避免每次重复操作符。一种折衷的方法可能是在其他语言(如C、C++或Rust)中使用宏。但是PHP没有这个功能,也没有内置函数“between”,因此必须创建函数才能使用函数,或者像答案中所说的那样使用“filter_var”,即使在这种情况下,“filter_var”写起来并不方便。 - AnthonyB

0

这是对@Mark Paspirgilis提供的答案进行扩展,允许可选择的包含并返回布尔响应。

public function between($p_value, $p_min, $p_max, $p_inclusive=0) {
  ## 0 = all inclusive; -1 = left inclusive; 1 = right inclusive
  if ($p_inclusive == -1) {
    return (bool)($p_value >= $p_min && $p_value < $p_max);
  }
  else if ($p_inclusive == 1) {
    return (bool)($p_value > $p_min && $p_value <= $p_max);
  }
  else {
    return (bool)($p_value >= $p_min && $p_value <= $p_max);
  }
} # END FUNCTION between

0

一种可能性(不是解决方案):

$mysqli->query("SELECT $value BETWEEN $min and $max as test")->fetch_object()->test;

如果您使用此代码,请务必检查 $value$min$max 的类型。如果其中一个可以是任意字符串(最坏的情况是来自用户),那么这就是 SQL 注入风险,您将会被黑客攻击。 - Matmarbon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接