当前位置: 首页 > 后端技术 > PHP

你知道的array_diff_uassoc真的是你所知道的吗?

时间:2023-03-30 02:53:47 PHP

如果让你用一句话描述PHP函数array_diff_uassoc,也许你会想到,就是同时比较两个或多个函数,返回第一个函数中出现的相同键值和不会同时出现在其他函数中的数据。最近看到一个很有意思的问题,关于array_diff_uassoc的实现。看完这道题,才知道对这个函数的误解有多深。这是问题的简化版本:functioncomparekey($a,$b){return0;}$array1=['a'=>1,'b'=>2,'c'=>3,'d'=>4];$array2=['a'=>2,'d'=>4,'e'=>6];$res=array_diff_uassoc($array1,$array2,'comparekey');var_dump($资源);为什么结果是['a'=>1,'c'=>3,'d'=>4];按照正常的逻辑,array_diff_uassoc返回的是不同key不同value的数组数据。如果自定义比较函数返回0,则认为键值相同。所以正常的逻辑应该返回['a'=>1,'b'=>2,'c'=>3]你真的对吗?1.自定义函数比较两个数组的键?其实说实话,一开始我也是这么想的。直到我在自定义函数中分别输出了a和b,看到了诡异的输出内容,我才意识到比较函数并没有那么简单。为了方便看到内容,用下面的数组替换题中的数组内容functioncomparekey($a,$b){echo$a.'-'.$b;返回0;}$array1=['a'=>1,'b'=>2,'c'=>3,'d'=>4];$array2=['e'=>'2','f'=>5,'g'=>6];$res=array_diff_uassoc($array1,$array2,'comparekey');函数的输出是a-bb-cc-de-ff-ga-eb-ec-ed-e,由此可见自定义比较函数的输入不一定来自不同的数组key。也可能有相同数组的键。2、自定义函数是否只是比较key和value是否相等?当然不是,比较功能本身就比较大。但是比较键值是否相等并不是我们理解的。php会根据自定义的返回结果,在内部调整内部指针位置,所以我们看到下面的比较是a-eb-ec-ed-e3。比较键值时,真的是比较一个具有相同键名的数组元素键值吗?这也不是真的。其实是因为比较函数的数组结果影响了PHP内部数组指针位置的变化。变化方式的不同会导致最终比较的是同键名的值而不是像我们想的那样比较同键名的值。查看php源码,array_diff_uassoc最终是通过php_array_diff函数实现的。静态无效php_array_diff(void*base,size_tnmemb,size_tsiz,compare_func_tcmp,swap_func_tswp){...if(hash->nNumOfElements>1){if(behavior==DIFF_NORMAL){zend_sort((void*)lists[i],hash->nNumOfElements,sizeof(Bucket),diff_data_compare_func,(swap_func_t)zend_hash_bucket_swap);}elseif(behavior&DIFF_ASSOC){/*当DIFF_KEY*/zend_sort((void*)lists[i],hash->nNumOfElements,sizeof(Bucket),diff_key_compare_func,(swap_func_t)zend_hash_bucket_swap)时触发;}}...}可以看到diff_key_compare_func被传递给了排序函数。因此,自定义函数的返回结果会影响临时变量列表的输出。PHP在内部首先对所有输入数组进行排序。所以在自定义函数中,可以看出前面的输出内容是依次比较数组的键名。真面目当对输入数组的键名进行排序时,需要将第一个数组的键名与其他数组的键名进行比较。1)比较第一个数组的当前元素的键名是否与要比较的数组的每个元素的键名相同,直到遇到第一个或比较结束。RETVAL_ARR(zend_array_dup(Z_ARRVAL(args[0])));while(Z_TYPE(ptrs[0]->val)!=IS_UNDEF){for(i=1;ival)!=IS_UNDEF&&(0<(c=diff_data_compare_func(ptrs[0],ptrs[i])))){ptrs[i]++;}}elseif(behavior&DIFF_ASSOC){/*当DIFF_KEY时也触发*/while(Z_TYPE(ptr->val)!=IS_UNDEF&&(0!=(c=diff_key_compare_func(ptrs[0],ptr)))){指针++;}}...}...}2)如果键名相同(键名比较函数返回0),比较键值是否相等。如果不相等,则将c设置为-1并继续比较下一个数组元素。RETVAL_ARR(zend_array_dup(Z_ARRVAL(args[0])));while(Z_TYPE(ptrs[0]->val)!=IS_UNDEF){...for(i=1;ival)!=IS_UNDEF){...if(!c){for(;;){p=ptrs[0];p=点[0];if(p->key==NULL){zend_hash_index_del(Z_ARRVAL_P(return_value),p->h);}else{zend_hash_del(Z_ARRVAL_P(return_value),p->key);}if(Z_TYPE((++ptrs[0])->val)==IS_UNDEF){跳出;}...}}else{for(;;){if(Z_TYPE((++ptrs[0])->val)==IS_UNDEF){gotoout;}...}...}...}下面以数组和自定义函数为例,说明比较过程。functioncomparekey($a,$b){return0;}$array1=['a'=>1,'b'=>2,'c'=>3,'d'=>4];$array2=['a'=>2,'d'=>4,'e'=>6];将返回数组设置为array1比较键名“a”、“a”是否相等,然后比较array1['a']!=$array2['a']。比较键名“b”和“a”,如果相等,则比较array1['b']==$array2['a'],删除返回的数组键值'b'并比较键名"c"和"a"相等,比较array1['c']!=$array2['a']。如果键名“d”和“a”相等,比较array1['c']!=$array2['a']。所以最终返回的数组是$res=['a'=>1,'c'=>3,'d'=>4]综上所述,自定义函数不允许我们完全自定义。自定义函数返回结果会导致不同的输出结果。PHP数组有很多方法来提供自定义函数。但是,如果你自定义函数的返回值是“不合理”的,比如本题中的函数,会一直相等,但是PHP中同一个数组的键值不能相同,所以这个自定义函数的对比结果其实是“有问题的”。在此前提下,php返回的结果也可能有意想不到的输出。下次使用array_diff_uassoc函数时,应该明白这个自定义函数不仅比较两个数组的键名是否相同,还会影响比较前PHP对输入数组的内部排序;自定义函数返回的结果会直接影响php数组指针的变化顺序,导致比较结果不同;文章首发于公众号【写PHP的老王】PS:分享不易,觉得有用记得分享