我有这个函数负责将文件名和mime-type转换成更“人性化”的东西(例如file.png,image / png到[Image,PNG])。我发现有趣的是if() elseif()
语句组的NPath复杂度高于switch(true)
语句。
使用以下代码,PHP Mess Detector输出一个4410的NPath:
public function humanKind()
{
$typeRA = explode("/", strtolower($this->type));
$fileRA = explode(".", $this->name);
$fileType = strtoupper($fileRA[count($fileRA) - 1]);
switch($typeRA[0]) {
case "image":
$humanType = "Image";
break;
case "video":
$humanType = "Video";
break;
case "audio":
$humanType = "Sound";
break;
case "font":
$humanType = "Font";
break;
default:
$humanType = "File";
}
switch ($this->type) {
case "application/msword":
case "application/pdf":
case "applicaiton/wordperfect":
case "text/plain":
case "text/rtf":
case "image/vnd.photoshop":
case "image/psd":
case "image/vnd.adobe.photoshop":
case "image/x-photoshop":
case "application/xml":
case "application/x-mspublisher":
case "text/html":
case "application/xhtml+xml":
case "text/richtext":
case "application/rtf":
case "application/x-iwork-pages-sffpages":
case "application/vnd.apple.pages":
$humanType = "Document";
break;
case "application/vnd.ms-excel":
case "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet":
case "application/x-iwork-numbers-sffnumbers":
case "application/vnd.apple.numbers":
$humanType = "Spreadsheet";
break;
case "application/vnd.ms-powerpoint":
case "application/vnd.openxmlformats-officedocument.presentationml.presentation":
case "application/vnd.openxmlformats-officedocument.presentationml.slideshow":
case "application/x-iwork-keynote-sffkey":
case "application/vnd.apple.keynote":
$humanType = "Slideshow";
break;
case "application/zip":
case "application/x-zip-compressed":
case "application/x-compressed":
case "application/x-compress":
case "application/x-rar-compressed":
case "applicaiton/x-7z-compressed":
case "application/x-ace-compressed":
$humanType = "Archive";
break;
case "text/x-vcard":
case "text/x-ms-contact":
$humanType = "Contact";
break;
case "text/x-php":
case "application/x-dosexec":
case "application/x-xpinstall":
case "application/x-opera-extension":
case "application/x-chrome-extension":
case "application/x-perl":
case "application/x-shockwave-flash":
case "application/java-archive":
$humanType = "Program";
break;
case "application/vnd.ms-fontobject":
case "application/font-woff":
case "application/x-font-truetype":
case "application/x-font-opentype":
case "application/x-font-ttf":
case "application/font-sfnt":
$humanType = "Font";
break;
}
// Special Cases
if ($humanType == "Archive" && $fileType == "APK") { // Android App
$humanType = "App";
} elseif ($humanType == "Archive" && $fileType == "XPS") {
$humanType = "Document";
} elseif ($this->type == "application/xml" && $fileType == "CONTACT") {
$humanType = "Contact";
} elseif ($this->type == "application/octet-stream" && $fileType == "JNT") {
$humanType = "Document";
}
if (strlen($fileType) > 4) {
$fileType = "";
}
return array($humanType, $fileType);
如果我们用以下内容替换特殊情况if elseif
:
// Special Cases
switch(true) {
case ($humanType == "Archive" && $fileType == "APK"): // Android App
$humanType = "App";
break;
case ($humanType == "Archive" && $fileType == "XPS"):
$humanType = "Document";
break;
case ($this->type == "application/xml" && $fileType == "CONTACT"):
$humanType = "Contact";
break;
case ($this->type == "application/octet-stream" && $fileType == "JNT"):
$humanType = "Document";
break;
}
PHP Mess Detector报告了1960年的NPath复杂性。
这是为什么?是什么让switch(true)不像我看来的那样复杂,只是控制结构相同?
答案 0 :(得分:9)
由于NPath复杂性衡量的是完成coverage of your code所需的单元测试次数,应该在您的2个“特殊情况”实现之间没有区别。
但计算方面存在一些差异。让我们逐步完成2个“特殊情况”实现并手动计算NPath复杂度:
if .. elseif ..
if ($humanType == "Archive" && $fileType == "APK") { // Android App
$humanType = "App";
}
elseif ($humanType == "Archive" && $fileType == "XPS") {
$humanType = "Document";
}
elseif ($this->type == "application/xml" && $fileType == "CONTACT") {
$humanType = "Contact";
}
elseif ($this->type == "application/octet-stream" && $fileType == "JNT") {
$humanType = "Document";
}
此声明导致 9 的NPath复杂度:if .. else
为1点,每if(expr)
为1点,每&&
运算符为1点。 (1 + 4 + 4 = 9)
switch(true)
switch(true) {
case ($humanType == "Archive" && $fileType == "APK"): // Android App
$humanType = "App";
break;
case ($humanType == "Archive" && $fileType == "XPS"):
$humanType = "Document";
break;
case ($this->type == "application/xml" && $fileType == "CONTACT"):
$humanType = "Contact";
break;
case ($this->type == "application/octet-stream" && $fileType == "JNT"):
$humanType = "Document";
break;
}
此语句导致switch(true)
的NPath复杂度仅为 4 :0分,因为它不包含&&
或||
个运算符,每个运算符包含1个点case
标签。 (0 + 4 = 4)
humanKind
函数计算每个语句的NPath值,并将值乘以。没有“特殊情况”语句的函数的NPath复杂度是490.乘以if .. else if ..
语句9的NPath值得到4410的NPath复杂度。并乘以{{1的NPath值4,你的复杂性只有1960年。这就是全部!
现在我们知道了: NPath复杂度不会衡量switch(true)
语句中case
标签的表达复杂性!
答案 1 :(得分:1)
通常,switch可能比if / elseif更快,因为switch语句只评估一次条件,然后与每种情况进行比较。
据我所知,switch语句中的情况是内部索引的,因此你可能会因此而获得更好的性能(虽然我找不到原来的文章谈论这个,因此我无法证明它。)
我还想象一下,switch语句的AST比等效的if / elseif更简单。
编辑:
在基于C语言(很可能是其他语言)中,当switch语句变得超过4-5个案例时,它们将被实现为列表/哈希表。这意味着每个项目的访问时间变得相同。而在if / elseif块中,没有这样的优化。
编译器可以更轻松地处理这些类型的switch语句,因为它可以对不同的条件做出更多假设。因此,复杂性较低。任意情况的查找是O(1)。这再次链接到我之前关于如何更简单地转换AST的声明。
编辑#2:
在更多CS术语中,编译器可以使用分支表(或跳转)来减少switch语句的cpu时间:http://en.wikipedia.org/wiki/Branch_table