启发式对 PHP 中的任何 "kind" 技术测量值进行排序
Heuristic to sort any "kind" of technical measurements in PHP
我用相同尺寸的不同列表测量但有点混合单位,如
“1 米,200 毫米,1 英尺”
或者也可能
“1 °C, 273 K”等等。
现在我想按绝对顺序对它们进行排序
“200 毫米,1 英尺,1 米”和“273 K,1 °C”
我想知道这是否是一个已经解决的问题,因为我不想重新发明轮子。恐怕,这可能是某种 "shopping for PHP extensions" 问题,但我已经找到了一些有用的软件包:
https://github.com/PhpUnitsOfMeasure/php-units-of-measure 可以在度量单位之间进行各种对话。
我已经创建了代码来分隔单位和数字。
所以我在想什么,要"brute force"单位到某个维度的那些:
https://github.com/PhpUnitsOfMeasure/php-units-of-measure/tree/master/source/PhysicalQuantity
接下来我可以选择第一个维度并将所有内容转换为第一个 "main" SI 单位并对其进行排序。
对吗?
一般来说,您需要做的是将这些单位换算成一些通用的计量单位,但只是为了排序。
使用usort()
和自定义回调函数。在您的回调中,进行转换以进行比较。
返回结果时一定要保持原来的单位,否则会出现舍入误差。
这是我根据建议提出的解决方案
public function testCompareLength()
{
$this->assertLessThan(0, $this->objectDe->compareFunction('100 mm', '1 m'));
}
public function testCompareTemperature()
{
$this->assertLessThan(0, $this->objectDe->compareFunction('1 K', '0 °C'));
$this->assertGreaterThan(0, $this->objectDe->compareFunction('0 °C', '1 K'));
$this->assertEquals(0, $this->objectDe->compareFunction('-273 °C', '0 K'));
}
/**
* @param $numberString
*
* @return array
*/
public function parseNumber($numberString): array
{
$values = preg_split('/(?<=[0-9.,])(?=[^0-9,.]+)/i', $numberString);
$float = $values[0];
$unit = $values[1] ?? '';
$decPos = strpos($float, '.');
if ($decPos === false) {
$precision = 0;
} else {
$precision = strlen($float) - $decPos - 1;
}
return ['float' => $float, 'unit' => $unit, 'precision' => $precision];
}
private function heuristicMeasureFactory($measure)
{
$prioritizedDimensions = [
Temperature::class,
Length::class,
];
$unit = trim($measure['unit']);
foreach ($prioritizedDimensions as $class) {
foreach ($class::getUnitDefinitions() as $definition) {
if ($definition->getName() == $unit) {
return new $class($measure['float'], $unit);
}
}
}
// now process aliases
foreach ($prioritizedDimensions as $class) {
foreach ($class::getUnitDefinitions() as $definition) {
foreach ($definition->aliases as $alias) {
if ($alias == $unit) {
return new $class($measure['float'], $unit);
}
}
}
}
return null; // NaN
}
/**
* Sort apples and oranges -- kind of. Not.
*
* Compares two strings which represend a measurement of the same physical dimension
*/
public function compareFunction($a, $b)
{
$definitions = Temperature::getUnitDefinitions();
$aParsed = $this->parseNumber($a);
$aVal = $this->heuristicMeasureFactory($aParsed);
$bParsed = $this->parseNumber($b);
$bVal = $this->heuristicMeasureFactory($bParsed);
if ($aVal == null || $bVal == null) {
return strnatcmp($aVal, $bVal); // fallback to string comparision
}
return bccomp($aVal->subtract($bVal)->toNativeUnit(), 0, 36);
}
我用相同尺寸的不同列表测量但有点混合单位,如
“1 米,200 毫米,1 英尺”
或者也可能
“1 °C, 273 K”等等。
现在我想按绝对顺序对它们进行排序
“200 毫米,1 英尺,1 米”和“273 K,1 °C”
我想知道这是否是一个已经解决的问题,因为我不想重新发明轮子。恐怕,这可能是某种 "shopping for PHP extensions" 问题,但我已经找到了一些有用的软件包:
https://github.com/PhpUnitsOfMeasure/php-units-of-measure 可以在度量单位之间进行各种对话。
我已经创建了代码来分隔单位和数字。
所以我在想什么,要"brute force"单位到某个维度的那些:
https://github.com/PhpUnitsOfMeasure/php-units-of-measure/tree/master/source/PhysicalQuantity
接下来我可以选择第一个维度并将所有内容转换为第一个 "main" SI 单位并对其进行排序。
对吗?
一般来说,您需要做的是将这些单位换算成一些通用的计量单位,但只是为了排序。
使用usort()
和自定义回调函数。在您的回调中,进行转换以进行比较。
返回结果时一定要保持原来的单位,否则会出现舍入误差。
这是我根据建议提出的解决方案
public function testCompareLength()
{
$this->assertLessThan(0, $this->objectDe->compareFunction('100 mm', '1 m'));
}
public function testCompareTemperature()
{
$this->assertLessThan(0, $this->objectDe->compareFunction('1 K', '0 °C'));
$this->assertGreaterThan(0, $this->objectDe->compareFunction('0 °C', '1 K'));
$this->assertEquals(0, $this->objectDe->compareFunction('-273 °C', '0 K'));
}
/**
* @param $numberString
*
* @return array
*/
public function parseNumber($numberString): array
{
$values = preg_split('/(?<=[0-9.,])(?=[^0-9,.]+)/i', $numberString);
$float = $values[0];
$unit = $values[1] ?? '';
$decPos = strpos($float, '.');
if ($decPos === false) {
$precision = 0;
} else {
$precision = strlen($float) - $decPos - 1;
}
return ['float' => $float, 'unit' => $unit, 'precision' => $precision];
}
private function heuristicMeasureFactory($measure)
{
$prioritizedDimensions = [
Temperature::class,
Length::class,
];
$unit = trim($measure['unit']);
foreach ($prioritizedDimensions as $class) {
foreach ($class::getUnitDefinitions() as $definition) {
if ($definition->getName() == $unit) {
return new $class($measure['float'], $unit);
}
}
}
// now process aliases
foreach ($prioritizedDimensions as $class) {
foreach ($class::getUnitDefinitions() as $definition) {
foreach ($definition->aliases as $alias) {
if ($alias == $unit) {
return new $class($measure['float'], $unit);
}
}
}
}
return null; // NaN
}
/**
* Sort apples and oranges -- kind of. Not.
*
* Compares two strings which represend a measurement of the same physical dimension
*/
public function compareFunction($a, $b)
{
$definitions = Temperature::getUnitDefinitions();
$aParsed = $this->parseNumber($a);
$aVal = $this->heuristicMeasureFactory($aParsed);
$bParsed = $this->parseNumber($b);
$bVal = $this->heuristicMeasureFactory($bParsed);
if ($aVal == null || $bVal == null) {
return strnatcmp($aVal, $bVal); // fallback to string comparision
}
return bccomp($aVal->subtract($bVal)->toNativeUnit(), 0, 36);
}