На практике, при программировании на PHP, довольно часто приходиться использовать функции min() и max() для поиска минимального или максимального значения элемента в массиве. Довольно редко, исходя из своей практики, среди аргументов функций. Перед тем как начать исследовать различия, думаю, что лучше для наглядности привести код вызова функций с различным набором аргументов.
Если мы попытаемся смешивать аргументы (массив с числами), то в этом случае функция пытается преобразовать массив в строку.
Для случая, если передаются строки, то происходит поиск минимального или максимального символа согласно следованию порядка в алфавите.
В общем, все понятно и ясно, все тоже самое, что и в документации. Работая с этими функциями как бы создается впечатление, что это совершенно разные функции, но на самом деле в исходниках PHP это одна и та же функция, в которой передается дополнительный флаг, подсказывающий, что в данный момент вернуть — максимальное или минимальное значение.
Приведу для наглядности исходники кода, удалив из тела функций различного рода проверки.
Функция поиска минимального значения.
Функция поиска максимального значения.
То есть, если присмотреться, то отличается всего лишь последний аргумент в функции zend_hash_minmax():
1 — возвращает максимальное значение, 0 — возвращает минимальное значение.
Перейдем в тело функции zend_hash_minmax(), также удалив код который в данный момент не требует внимания.
Таким образом, разные названия функции используют одну и туже функцию (своего рода скрытый полиморфизм). Я поначалу думал, что наверняка эти функции используют разные алгоритмы поиска минимального или максимального значения в массиве. На самом деле, как мы видим, используется обычный последовательный переход. Если использовать ранее мной описанный макрос цикла, то получаем подобный код.
Возможно, функция zend_hash_minmax() довольно долго отрабатывает при передаче “гигантского” списка элементов в массиве. Интересно, в этом случае наверное, было бы оптимальней предварительно отсортировать массив наиболее подходящим алгоритмом сортировки (в зависимости от объема элементов), а затем брать крайние значения.
php > echo min(1,2,3,4,5,6,7,-1);
-1
php > echo min([1,5,3]);
1
Если мы попытаемся смешивать аргументы (массив с числами), то в этом случае функция пытается преобразовать массив в строку.
php > echo min([-1,2],[1,5,3]);
PHP Notice: Array to string conversion in php shell code on line 1
PHP Stack trace:
PHP 1. {main}() php shell code:0
Notice: Array to string conversion in php shell code on line 1
Call Stack:
659.0962 352688 1. {main}() php shell code:0
Array
Для случая, если передаются строки, то происходит поиск минимального или максимального символа согласно следованию порядка в алфавите.
php > echo min("1","2");
1
php > echo min("a","b");
a
php > echo min("a","b", "c");
a
php > echo max("a","b", "c");
c
php > echo max("ab","ba", "cd");
cd
В общем, все понятно и ясно, все тоже самое, что и в документации. Работая с этими функциями как бы создается впечатление, что это совершенно разные функции, но на самом деле в исходниках PHP это одна и та же функция, в которой передается дополнительный флаг, подсказывающий, что в данный момент вернуть — максимальное или минимальное значение.
Приведу для наглядности исходники кода, удалив из тела функций различного рода проверки.
Файл: ext/standard/array.c
Функция поиска минимального значения.
PHP_FUNCTION(min)
{
int argc;
zval *args = NULL;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "+", &args, &argc) == FAILURE) {
return;
}
//….
if ((result = zend_hash_minmax(Z_ARRVAL(args[0]), php_array_data_compare, 0)) != NULL) {
//….
}
//….
}
Функция поиска максимального значения.
PHP_FUNCTION(min)
{
int argc;
zval *args = NULL;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "+", &args, &argc) == FAILURE) {
return;
}
//….
if ((result = zend_hash_minmax(Z_ARRVAL(args[0]), php_array_data_compare, 1)) != NULL) {
//….
}
//….
}
То есть, если присмотреться, то отличается всего лишь последний аргумент в функции zend_hash_minmax():
1 — возвращает максимальное значение, 0 — возвращает минимальное значение.
Перейдем в тело функции zend_hash_minmax(), также удалив код который в данный момент не требует внимания.
Файл: Zend/zend_hash.c
ZEND_API zval* ZEND_FASTCALL zend_hash_minmax(const HashTable *ht, compare_func_t compar, uint32_t flag)
{
//….
for (; idx < ht->nNumUsed; idx++) {
p = ht->arData + idx;
if (UNEXPECTED(Z_TYPE(p->val) == IS_UNDEF)) continue;
if (flag) {
if (compar(res, p) < 0) { /* max */
res = p;
}
} else {
if (compar(res, p) > 0) { /* min */
res = p;
}
}
}
return &res->val;
}
Таким образом, разные названия функции используют одну и туже функцию (своего рода скрытый полиморфизм). Я поначалу думал, что наверняка эти функции используют разные алгоритмы поиска минимального или максимального значения в массиве. На самом деле, как мы видим, используется обычный последовательный переход. Если использовать ранее мной описанный макрос цикла, то получаем подобный код.
PHP_FUNCTION(ms_maximal)
{
int argc = ZEND_NUM_ARGS();
double maximal = ZEND_LONG_MIN, cur_value = 0;
zval *array,
*value;
if (zend_parse_parameters(argc, "a", &array) == FAILURE) {
RETURN_DOUBLE(maximal);
}
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(array), value) {
cur_value = zval_get_double (value);
if(cur_value > maximal) {
maximal = cur_value;
}
} ZEND_HASH_FOREACH_END();
RETURN_DOUBLE(maximal);
}
Возможно, функция zend_hash_minmax() довольно долго отрабатывает при передаче “гигантского” списка элементов в массиве. Интересно, в этом случае наверное, было бы оптимальней предварительно отсортировать массив наиболее подходящим алгоритмом сортировки (в зависимости от объема элементов), а затем брать крайние значения.
Поделиться с друзьями
napa3um
Это же шутка, иронизирование над «хабром нетортом»?