<?php
private function calculateMedian($aValues) {
$aToCareAbout = array();
foreach ($aValues as $mValue) {
if ($mValue >= 0) {
$aToCareAbout[] = $mValue;
}
}
$iCount = count($aToCareAbout);
sort($aToCareAbout, SORT_NUMERIC);
if ($iCount > 2) {
if ($iCount % 2 == 0) {
return ($aToCareAbout[floor($iCount / 2) - 1] + $aToCareAbout[floor($iCount / 2)]) / 2;
} else {
return $aToCareAbout[$iCount / 2];
}
} elseif (isset($aToCareAbout[0])) {
return $aToCareAbout[0];
} else {
return 0;
}
}
#1 楼
函数的第一部分过滤掉负值。这与计算中位数本身无关,因此应远离此函数。我会怎么做。
在全局范围(或静态方法)中创建
array_median()
函数,如下所示:/**
* Adapted from Victor T.'s answer
*/
function array_median($array) {
// perhaps all non numeric values should filtered out of $array here?
$iCount = count($array);
if ($iCount == 0) {
throw new DomainException('Median of an empty array is undefined');
}
// if we're down here it must mean $array
// has at least 1 item in the array.
$middle_index = floor($iCount / 2);
sort($array, SORT_NUMERIC);
$median = $array[$middle_index]; // assume an odd # of items
// Handle the even case by averaging the middle 2 items
if ($iCount % 2 == 0) {
$median = ($median + $array[$middle_index - 1]) / 2;
}
return $median;
}
这样,我们通常可以使用通用目的的函数,其命名与核心php函数一致。
您的方法看起来像
/**
* The name should probably be changed, to reflect more your business intent.
*/
private function calculateMedian($aValues) {
return array_median(
array_filter(
$aValues,
function($v) {return (is_numeric($v) && $v >= 0);}
// You can skip is_numeric() check here, if you know all values in $aValues are actually numeric
)
);
}
无论是在
calculateMedian()
内还是在调用它的代码中,您都应注意捕获如果数组为空时可能引发的DomainException
)。#2 楼
我想知道是否可以将上面的内容压缩为:private function calculateMedian($aValues) {
$aToCareAbout = array();
foreach ($aValues as $mValue) {
if ($mValue >= 0) {
$aToCareAbout[] = $mValue;
}
}
$iCount = count($aToCareAbout);
if ($iCount == 0) return 0;
// if we're down here it must mean $aToCareAbout
// has at least 1 item in the array.
$middle_index = floor($iCount / 2);
sort($aToCareAbout, SORT_NUMERIC);
$median = $aToCareAbout[$middle_index]; // assume an odd # of items
// Handle the even case by averaging the middle 2 items
if ($iCount % 2 == 0)
$median = ($median + $aToCareAbout[$middle_index - 1]) / 2;
return $median;
}
我不是写PHP的,而是从在线手册中获取计数:
count()对于未设置的变量
可能返回0,但对于用
初始化的变量也可能返回
0。一个空数组。使用
isset()测试是否设置了变量。
但是在您的情况下,该函数似乎并不关心数组是空还是变量是否为空。未设置-在两种情况下均返回0。通过检查返回的计数,我们可以消除一些
if
分支。评论
\ $ \ begingroup \ $
我不喜欢它为空的$ aToCareAbout返回0。在这种情况下,我希望返回null。 (或者可能引发异常?)
\ $ \ endgroup \ $
– Mchl
2011-1-26在8:21
\ $ \ begingroup \ $
@Mchl是的,这是困扰我的其他要点之一。原始代码使用0表示某种问题。但是,零填充数组也可以具有有效的中位数。
\ $ \ endgroup \ $
–狼人
2011年1月26日上午8:26
\ $ \ begingroup \ $
平均值应该为+1吗? (或者对于2个元素,它将尝试访问[-1]?不久将提供包含所有反馈的第一次迭代。
\ $ \ endgroup \ $
– edorian
2011年1月26日上午8:43
\ $ \ begingroup \ $
@edorian通过查看您的原始代码,我假设php数组是基于0的,例如C。因此,如果一个数组包含2个元素,iCount为2,middle_index将为1,middle_index-1将为0然后将这两个索引中的元素平均。
\ $ \ endgroup \ $
–狼人
2011年1月26日9:00
\ $ \ begingroup \ $
@edorian:不,这是正确的方法。两个简单的例子:奇数:(3个元素,索引0到2,预期= 1)中位数是元素floor(3/2)==1。通过。偶数:(4个元素,索引0到3,预期=平均1和2)对于4个元素,它将使用floor(4/2)==2。元素数量为偶数,因此使用元素2-1 = 1。中位数是元素1和2的平均值。
\ $ \ endgroup \ $
–doppelgreener
2011年1月26日9:03
#3 楼
就我个人而言,这就是构建函数的方式:function calculateMedian($Values)
{
//Remove array items less than 1
$Values = array_filter($Values,array($this,"callback"));
//Sort the array into descending order 1 - ?
sort($Values, SORT_NUMERIC);
//Find out the total amount of elements in the array
$Count = count($Values);
//Check the amount of remainders to calculate odd/even
if($Count % 2 == 0)
{
return $Values[$Count / 2];
}
return (($Values[($Count / 2)] + $Values[($Count / 2) - 1]) / 2);
}
我做了什么更改?
我已经使用过减少变量,覆盖
$Values
将条件语句从2减少到1 *
使代码看起来更具可读性和可理解性。在回调中必须使用
foreach
和if语句,但要进行逻辑检查。回调将是类中的一种简单方法,如下所示:public function callback($value)
{
return $value > 0;
}
不幸的是,由于本地函数
empty
实际上是一种语言构造,因此不是有效的回调,但是您可以在回调方法中使用return !empty($value);
来删除其他实体,例如NULL
,FALSE
等。可以按照说明将其删除,并放置在函数外部。
*注意:建议您进行某种线性数组检查,以确保数组基于整数索引(正如我们的代码假定的那样),可以像这样进行线性处理:
if(array_keys($Values) !== range(0,($Count-1)))
{
return null;
}
将在
$Count
值起作用后添加。我用来测试的示例测试是:
$values = array(
0,4,7,5,6,9,5,3,2,7,5,6,4,3,7
);
echo calculateMedian($values);
得出正确答案为5
评论
\ $ \ begingroup \ $
我不是故意要挑剔,但您是否有特定的原因将变量名大写(其中的一些)?我认为通常使用与问题相同的变量命名约定作为答案的好主意(除非问题的命名约定与语言的约定相矛盾)。
\ $ \ endgroup \ $
–sepp2k
2011年1月26日19:42
\ $ \ begingroup \ $
没有理由,这只是我倾向于编写代码的方式,即时通讯显示了我的逻辑的好处,OP可以相应地更改它们,因为大写只是我喜欢使用的命名约定。
\ $ \ endgroup \ $
– RobertPitt
2011年1月27日,0:53
\ $ \ begingroup \ $
通过将> = 0转换为> 0,您已经默默地更改了含义。
\ $ \ endgroup \ $
– Fred Nurk
2011年1月27日,9:13
\ $ \ begingroup \ $
Fred,我相信您对回调的看法是,您是在说计算中应该包含零值吗?
\ $ \ endgroup \ $
– RobertPitt
2011年1月27日在9:17
评论
第一次迭代中的$ iCount应该是$ count。