zoukankan      html  css  js  c++  java
  • 深入PHP内核之array_multisort

    这个函数是我第一次看手册的时候,没看明白是怎么回事,所以有必要记录一下

    用法

    bool array_multisort ( array &$arr [, mixed $arg = SORT_ASC [, mixed $arg = SORT_REGULAR [, mixed $... ]]] )

    array_multisort() 可以用来一次对多个数组进行排序,或者根据某一维或多维对多维数组进行排序。

    关联(string)键名保持不变,但数字键名会被重新索引。

    排序顺序标志:

    • SORT_ASC - 按照上升顺序排序
    • SORT_DESC - 按照下降顺序排序

    排序类型标志:

    • SORT_REGULAR - 将项目按照通常方法比较
    • SORT_NUMERIC - 将项目按照数值比较
    • SORT_STRING - 将项目按照字符串比较

    每个数组之后不能指定两个同类的排序标志。每个数组后指定的排序标志仅对该数组有效 - 在此之前为默认值 SORT_ASCSORT_REGULAR

    输入数组被当成一个表的列并以行来排序——这类似于 SQL 的 ORDER BY 子句的功能。第一个数组是要排序的主要数组。数组中的行(值)比较为相同的话就按照下一个输入数组中相应值的大小来排序,依此类推。

    本函数的参数结构有些不同寻常,但是非常灵活。第一个参数必须是一个数组。接下来的每个参数可以是数组或者是下面列出的排序标志。

    示例一:

    <?php
    function my_sort($arrays, $sort_key, $sort_order = SORT_ASC, $sort_type = SORT_NUMERIC)
    {
        if (is_array($arrays)) {
            foreach ($arrays as $array) {
                if (is_array($array)) {
                    $key_arrays[] = $array[$sort_key];
                } else {
                    return false;
                }
            }
        } else {
            return false;
        }
        array_multisort($key_arrays, $sort_order, $sort_type, $arrays);
        return $arrays;
    }
    
    $person = array(
        array('id' => 1, 'name' => 'fj', 'weight' => 100, 'height' => 180),
        array('id' => 2, 'name' => 'tom', 'weight' => 53, 'height' => 150),
        array('id' => 3, 'name' => 'jerry', 'weight' => 120, 'height' => 156),
        array('id' => 4, 'name' => 'bill', 'weight' => 110, 'height' => 190),
        array('id' => 5, 'name' => 'linken', 'weight' => 80, 'height' => 200),
        array('id' => 6, 'name' => 'madana', 'weight' => 95, 'height' => 110),
        array('id' => 7, 'name' => 'jordan', 'weight' => 70, 'height' => 170)
    );
    
    $person = my_sort($person, 'name', SORT_ASC, SORT_STRING);
    
    var_dump($person);
    
    ?>
    

     结果

    array (size=7)
      0 => 
        array (size=4)
          'id' => int 4
          'name' => string 'bill' (length=4)
          'weight' => int 110
          'height' => int 190
      1 => 
        array (size=4)
          'id' => int 1
          'name' => string 'fj' (length=2)
          'weight' => int 100
          'height' => int 180
      2 => 
        array (size=4)
          'id' => int 3
          'name' => string 'jerry' (length=5)
          'weight' => int 120
          'height' => int 156
      3 => 
        array (size=4)
          'id' => int 7
          'name' => string 'jordan' (length=6)
          'weight' => int 70
          'height' => int 170
      4 => 
        array (size=4)
          'id' => int 5
          'name' => string 'linken' (length=6)
          'weight' => int 80
          'height' => int 200
      5 => 
        array (size=4)
          'id' => int 6
          'name' => string 'madana' (length=6)
          'weight' => int 95
          'height' => int 110
      6 => 
        array (size=4)
          'id' => int 2
          'name' => string 'tom' (length=3)
          'weight' => int 53
          'height' => int 150
    

     示例二:

    <?php
    $grade = array(
        "score" => array(70, 95, 70.0, 60, "70"),
        "name" => array("Zhang San", "Li Si", "Wang Wu", "Zhao Liu", "Liu Qi")
    );
    array_multisort($grade["score"], SORT_NUMERIC, SORT_DESC,
    // 将分数作为数值,由高到低排序
        $grade["name"], SORT_STRING, SORT_ASC);
    // 将名字作为字符串,由小到大排序
    var_dump($grade);
    

     结果

    array (size=2)
      'score' => 
        array (size=5)
          0 => int 95
          1 => string '70' (length=2)
          2 => float 70
          3 => int 70
          4 => int 60
      'name' => 
        array (size=5)
          0 => string 'Li Si' (length=5)
          1 => string 'Liu Qi' (length=6)
          2 => string 'Wang Wu' (length=7)
          3 => string 'Zhang San' (length=9)
          4 => string 'Zhao Liu' (length=8)
    

     我看来,这算一个比较神奇的函数了

    /* {{{ proto bool array_multisort(array ar1 [, SORT_ASC|SORT_DESC [, SORT_REGULAR|SORT_NUMERIC|SORT_STRING|SORT_NATURAL|SORT_FLAG_CASE]] [, array ar2 [, SORT_ASC|SORT_DESC [, SORT_REGULAR|SORT_NUMERIC|SORT_STRING|SORT_NATURAL|SORT_FLAG_CASE]], ...])
       Sort multiple arrays at once similar to how ORDER BY clause works in SQL */
    PHP_FUNCTION(array_multisort)
    {
    	zval***			args;
    	zval***			arrays;
    	Bucket***		indirect;
    	Bucket*			p;
    	HashTable*		hash;
    	int				argc;
    	int				array_size;
    	int				num_arrays = 0;
    	int				parse_state[MULTISORT_LAST];   /* 0 - flag not allowed 1 - flag allowed */
    	int				sort_order = PHP_SORT_ASC;
    	int				sort_type  = PHP_SORT_REGULAR;
    	int				i, k;
    
    	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "+", &args, &argc) == FAILURE) {
    		return;
    	}
    
    	/* Allocate space for storing pointers to input arrays and sort flags. */
    	arrays = (zval ***)ecalloc(argc, sizeof(zval **));
    	for (i = 0; i < MULTISORT_LAST; i++) {
    		parse_state[i] = 0;
    		ARRAYG(multisort_flags)[i] = (int *)ecalloc(argc, sizeof(int));
    	}
    
    	/* Here we go through the input arguments and parse them. Each one can
    	 * be either an array or a sort flag which follows an array. If not
    	 * specified, the sort flags defaults to PHP_SORT_ASC and PHP_SORT_REGULAR
    	 * accordingly. There can't be two sort flags of the same type after an
    	 * array, and the very first argument has to be an array. */
    	for (i = 0; i < argc; i++) {
    		if (Z_TYPE_PP(args[i]) == IS_ARRAY) {
    			/* We see the next array, so we update the sort flags of
    			 * the previous array and reset the sort flags. */
    			if (i > 0) {
    				ARRAYG(multisort_flags)[MULTISORT_ORDER][num_arrays - 1] = sort_order;
    				ARRAYG(multisort_flags)[MULTISORT_TYPE][num_arrays - 1] = sort_type;
    				sort_order = PHP_SORT_ASC;
    				sort_type = PHP_SORT_REGULAR;
    			}
    			arrays[num_arrays++] = args[i];
    
    			/* Next one may be an array or a list of sort flags. */
    			for (k = 0; k < MULTISORT_LAST; k++) {
    				parse_state[k] = 1;
    			}
    		} else if (Z_TYPE_PP(args[i]) == IS_LONG) {
    			switch (Z_LVAL_PP(args[i]) & ~PHP_SORT_FLAG_CASE) {
    				case PHP_SORT_ASC:
    				case PHP_SORT_DESC:
    					/* flag allowed here */
    					if (parse_state[MULTISORT_ORDER] == 1) {
    						/* Save the flag and make sure then next arg is not the current flag. */
    						sort_order = Z_LVAL_PP(args[i]) == PHP_SORT_DESC ? -1 : 1;
    						parse_state[MULTISORT_ORDER] = 0;
    					} else {
    						php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument #%d is expected to be an array or sorting flag that has not already been specified", i + 1);
    						MULTISORT_ABORT;
    					}
    					break;
    
    				case PHP_SORT_REGULAR:
    				case PHP_SORT_NUMERIC:
    				case PHP_SORT_STRING:
    				case PHP_SORT_NATURAL:
    #if HAVE_STRCOLL
    				case PHP_SORT_LOCALE_STRING:
    #endif
    					/* flag allowed here */
    					if (parse_state[MULTISORT_TYPE] == 1) {
    						/* Save the flag and make sure then next arg is not the current flag. */
    						sort_type = Z_LVAL_PP(args[i]);
    						parse_state[MULTISORT_TYPE] = 0;
    					} else {
    						php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument #%d is expected to be an array or sorting flag that has not already been specified", i + 1);
    						MULTISORT_ABORT;
    					}
    					break;
    
    				default:
    					php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument #%d is an unknown sort flag", i + 1);
    					MULTISORT_ABORT;
    					break;
    
    			}
    		} else {
    			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument #%d is expected to be an array or a sort flag", i + 1);
    			MULTISORT_ABORT;
    		}
    	}
    	/* Take care of the last array sort flags. */
    	ARRAYG(multisort_flags)[MULTISORT_ORDER][num_arrays - 1] = sort_order;
    	ARRAYG(multisort_flags)[MULTISORT_TYPE][num_arrays - 1] = sort_type;
    
    	/* Make sure the arrays are of the same size. */
    	array_size = zend_hash_num_elements(Z_ARRVAL_PP(arrays[0]));
    	for (i = 0; i < num_arrays; i++) {
    		if (zend_hash_num_elements(Z_ARRVAL_PP(arrays[i])) != array_size) {
    			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Array sizes are inconsistent");
    			MULTISORT_ABORT;
    		}
    	}
    
    	/* If all arrays are empty we don't need to do anything. */
    	if (array_size < 1) {
    		for (k = 0; k < MULTISORT_LAST; k++) {
    			efree(ARRAYG(multisort_flags)[k]);
    		}
    		efree(arrays);
    		efree(args);
    		RETURN_TRUE;
    	}
    
    	/* Create the indirection array. This array is of size MxN, where
    	 * M is the number of entries in each input array and N is the number
    	 * of the input arrays + 1. The last column is NULL to indicate the end
    	 * of the row. */
    	indirect = (Bucket ***)safe_emalloc(array_size, sizeof(Bucket **), 0);
    	for (i = 0; i < array_size; i++) {
    		indirect[i] = (Bucket **)safe_emalloc((num_arrays + 1), sizeof(Bucket *), 0);
    	}
    	for (i = 0; i < num_arrays; i++) {
    		k = 0;
    		for (p = Z_ARRVAL_PP(arrays[i])->pListHead; p; p = p->pListNext, k++) {
    			indirect[k][i] = p;
    		}
    	}
    	for (k = 0; k < array_size; k++) {
    		indirect[k][num_arrays] = NULL;
    	}
    
    	/* Do the actual sort magic - bada-bim, bada-boom. */
    	zend_qsort(indirect, array_size, sizeof(Bucket **), php_multisort_compare TSRMLS_CC);
    
    	/* Restructure the arrays based on sorted indirect - this is mostly taken from zend_hash_sort() function. */
    	HANDLE_BLOCK_INTERRUPTIONS();
    	for (i = 0; i < num_arrays; i++) {
    		hash = Z_ARRVAL_PP(arrays[i]);
    		hash->pListHead = indirect[0][i];;
    		hash->pListTail = NULL;
    		hash->pInternalPointer = hash->pListHead;
    
    		for (k = 0; k < array_size; k++) {
    			if (hash->pListTail) {
    				hash->pListTail->pListNext = indirect[k][i];
    			}
    			indirect[k][i]->pListLast = hash->pListTail;
    			indirect[k][i]->pListNext = NULL;
    			hash->pListTail = indirect[k][i];
    		}
    
    		p = hash->pListHead;
    		k = 0;
    		while (p != NULL) {
    			if (p->nKeyLength == 0)
    				p->h = k++;
    			p = p->pListNext;
    		}
    		hash->nNextFreeElement = array_size;
    		zend_hash_rehash(hash);
    	}
    	HANDLE_UNBLOCK_INTERRUPTIONS();
    
    	/* Clean up. */
    	for (i = 0; i < array_size; i++) {
    		efree(indirect[i]);
    	}
    	efree(indirect);
    	for (k = 0; k < MULTISORT_LAST; k++) {
    		efree(ARRAYG(multisort_flags)[k]);
    	}
    	efree(arrays);
    	efree(args);
    	RETURN_TRUE;
    }
    

     待续

    参考:http://php.net/manual/zh/function.array-multisort.php

  • 相关阅读:
    接口测试基础operation
    关于Fiddler常见问题之一
    接口测试用例编写规则
    Codeforces 959E. Mahmoud and Ehab and the xor-MST 思路:找规律题,时间复杂度O(log(n))
    Codeforces 930A. Peculiar apple-tree (dfs)
    51nod 2020 排序相减(暴力解法)
    《汇编语言(第三版)》pushf 和 popf 指令,以及标志寄存器在 Debug 中的表示
    DF标志和串传送指令
    《汇编语言(第三版)》cmp指令
    《汇编语言(第三版)》标志寄存器
  • 原文地址:https://www.cnblogs.com/chenpingzhao/p/4688526.html
Copyright © 2011-2022 走看看