这个函数如何从一个数组中返回第一个重复值?

4
我在 Code-Signal 上找到了一项任务(或者更像是一个挑战)。这是谷歌在面试中提出的一个特殊问题:
如果你想自己尝试一下,可以去 Code-Fight
解决问题后,你可以看到其他解决方案。
我的任务是“在数组中找到第一个重复项”。我成功地完成了这个任务(我会展示方法),但我对自己的结果不满意。在调查了顶级解决方案之后,我感到困惑,因为我不明白那里发生了什么。
这是一个给定的输入数组示例。
$a = [2, 1, 3, 5, 3, 2]

我的解决方案:
function firstDuplicate($a) {

    $onlyDupesArray= array();
    $countedValues = array_count_values($a);

    // remove all entries which are only once in the array
    foreach($a as $k => $v) {
        if($countedValues[$v] > 1) {
            $onlyDupesArray[$v] = $v;
        }
    }

    // get rid of dupes
    $uniqueDupesArray = array_unique($onlyDupesArray);
    $firstEncounter = PHP_INT_MAX;

    foreach($uniqueDupesArray as $k => $v) {
        if(array_keys($a, $v)[1] < $firstEncounter) {
            $firstEncounter = array_keys($a, $v)[1];
        }
    }

    if(is_null($a[$firstEncounter])) {
        return -1;
    } else {
        return $a[$firstEncounter];
    }
}

它在每个测试用例中都有效,并且我解决了这个挑战。然而,最优解是这个:
function firstDuplicate($a) {
    foreach ($a as $v)
        if ($$v++) return $v;
    return -1;
}

我知道什么是变量变量, 但在现实生活中还没有看到过,直到现在。
这里的references变量是做什么用的?它如何返回一个重复的值?它是否比较某个键是否已经有了这个类型的值?$$v++是引用数组中的一个键吗?
不用说,我更喜欢这种方法。它似乎更高效、更易于维护。
这是"常见做法"吗?

1
不是一个很好的“top”解决方案:https://repl.it/repls/CrimsonSphericalForm - Script47
我有点同意,但它确实打印了正确的输出。在你的例子中,1是第一个重复项,而且它被打印出来了。是的,有一个警告,但似乎他们没有注意到它。 - DasSaffe
1
它会发出警告,而且我不认为它可读性很高。当然,它至少有点有趣,但我不会称其比某些替代方案更易于维护或更高效。 - Jonnix
不过,$$v++与数组中的任何键都没有关系。 - Jonnix
4
@Script47毫无疑问是被那些喜欢在php.ini中将error_reporting=0设置为编程首选的人所选举为最佳之一。但他仍然很聪明。 - hanshenrik
2个回答

2
它是创建数字变量。$2、$1、$3等。
在foreach中的$v包含当前数字2。通过执行$test = 2; echo $$test,我们可以看到现在$2中有什么。通常为空的。现在,通过执行$$v++,它将返回当前值(空或者实际上变量不存在),但++将把“1”放入其中。整个语句本身将返回0,因为++不在变量前面。
考虑下面的代码:
$arr = [2, 1, 3, 5, 3, 2];
foreach($arr as $v) 
{
  $$v++;
}
$test = 3; 
echo $$test;

这将显示$3的值等于2,因为我们在$3上进行了2次++操作。

之所以有点奇怪,是因为通常情况下你不能使用以数字开头的变量。也许这样更清楚一些:

这将显示$3的值等于2,因为我们对$3进行了两次++操作。

唯一奇怪的原因是通常情况下,你不能使用以数字开头的变量。这样说或许更容易理解:

$arr = [2, 1, 3, 5, 3, 2]; 
foreach($arr as $v) 
{
  $v='a'.$v;
  $$v++;
}
echo "a3 = $a3\n"; // 2
echo "a2 = $a2\n"; // 2
echo "a1 = $a1\n"; // 1
echo "a5 = $a5\n"; // 1

回答“这是‘常规做法’吗?”的问题。不,我个人不会使用可变变量,因为在某些情况下,这可能被视为安全问题。我更喜欢以下解决方案,它使用相同的方法,但使用数组,不会抛出通知:

最初的回答:

function firstDuplicate($a) {
    $arr = []; 
    foreach ($a as $v) 
        if (in_array($v, $arr))
            return $v; 
        else
            $arr[] = $v; 
    return -1; 
}

变量变量的解决方案是一种创造性的解决方法,不过!最初的回答。

0
通过在变量前添加额外的$(变量变量),PHP会创建一个“隐藏/虚假数组”。 在运行时,每个索引都被填充为“0”:
第一次运行后,您会得到类似以下内容的东西
$array[2] = 0;

下一次运行后,索引1将被填充:

$array[1] = 0;
$array[2] = 0;

由于“0”被视为假,因此在第一个重复项(3)之后,您的条件变为有效:

$array[1] = 0;
$array[2] = 0;
$array[3] = 1; // <-- TRUE
$array[5] = 0;

1
PHP 创建了一个“隐藏”的数组。O_o - Jonnix
2
PHP创建一个“隐藏/虚假”数组 O_o。 - Jonnix
1
我讨厌PHP创建隐藏/虚假数组。 - DasSaffe
PHP创建了一个“隐藏”的数组。 - felipsmartins
1
如果你所说的隐藏/虚假数组是指真实变量(这也是未定义变量警告的来源),那么默认值为null。在执行$$v++之后,该值已经变成了1 - Jonnix

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