Add `reduce()` and `reduceRight()`. Update signatures. Fix bugs. Update README.
This commit is contained in:
parent
ecfee75fbe
commit
440d3fdb63
|
@ -17,7 +17,7 @@ Method names and functionalities are inspired by [Kotlin.Collections](https://ko
|
|||
|
||||
### 2.1 Functionalities
|
||||
|
||||
See [stubs](stubs/) directory for signature of all classes and methods of this extension, with PHPDoc.
|
||||
See [stubs](stubs/) directory for signature of all classes and methods of this extension, with PHPDoc. They can also serve as IDE helper.
|
||||
|
||||
### 2.2 PHP-style access
|
||||
|
||||
|
|
|
@ -113,9 +113,28 @@
|
|||
/// Unused global variable.
|
||||
zval rv;
|
||||
|
||||
static zend_always_inline int bucket_compare_numeric(Bucket* op1, Bucket* op2)
|
||||
static zend_always_inline int bucket_compare_numeric(const void* op1, const void* op2)
|
||||
{
|
||||
return numeric_compare_function(&op1->val, &op2->val);
|
||||
Bucket* b1 = (Bucket*)op1;
|
||||
Bucket* b2 = (Bucket*)op2;
|
||||
return numeric_compare_function(&b1->val, &b2->val);
|
||||
}
|
||||
|
||||
static zend_always_inline int bucket_compare_string(const void* op1, const void* op2)
|
||||
{
|
||||
Bucket* b1 = (Bucket*)op1;
|
||||
Bucket* b2 = (Bucket*)op2;
|
||||
return string_compare_function(&b1->val, &b2->val);
|
||||
}
|
||||
|
||||
static zend_always_inline int bucket_compare_regular(const void* op1, const void* op2)
|
||||
{
|
||||
Bucket* b1 = (Bucket*)op1;
|
||||
Bucket* b2 = (Bucket*)op2;
|
||||
zval result;
|
||||
if (compare_function(&result, &b1->val, &b2->val) == FAILURE)
|
||||
return 0;
|
||||
return ZEND_NORMALIZE_BOOL(Z_LVAL(result));
|
||||
}
|
||||
|
||||
int count_collection(zval* obj, zend_long* count)
|
||||
|
@ -1271,12 +1290,78 @@ PHP_METHOD(Collection, putAll)
|
|||
|
||||
PHP_METHOD(Collection, reduce)
|
||||
{
|
||||
|
||||
zend_fcall_info fci;
|
||||
zend_fcall_info_cache fcc;
|
||||
ZEND_PARSE_PARAMETERS_START(1, 1)
|
||||
Z_PARAM_FUNC(fci, fcc)
|
||||
ZEND_PARSE_PARAMETERS_END();
|
||||
zend_array* current = COLLECTION_FETCH_CURRENT();
|
||||
if (zend_hash_num_elements(current) == 0) {
|
||||
ERR_BAD_SIZE();
|
||||
RETURN_NULL();
|
||||
}
|
||||
INIT_FCI(3);
|
||||
zval acc;
|
||||
Bucket* bucket = current->arData;
|
||||
Bucket* end = bucket + current->nNumUsed;
|
||||
for (; bucket < end; ++bucket) {
|
||||
if (Z_ISUNDEF(bucket->val))
|
||||
continue;
|
||||
ZVAL_COPY_VALUE(&acc, &(bucket++)->val);
|
||||
break;
|
||||
}
|
||||
for (; bucket < end; ++bucket) {
|
||||
if (Z_ISUNDEF(bucket->val))
|
||||
continue;
|
||||
ZVAL_COPY_VALUE(¶ms[0], &acc);
|
||||
ZVAL_COPY_VALUE(¶ms[1], &bucket->val);
|
||||
if (bucket->key)
|
||||
ZVAL_STR(¶ms[2], bucket->key);
|
||||
else
|
||||
ZVAL_LONG(¶ms[2], bucket->h);
|
||||
zend_call_function(&fci, &fcc);
|
||||
zval_ptr_dtor(&acc);
|
||||
ZVAL_COPY_VALUE(&acc, &retval);
|
||||
}
|
||||
RETVAL_ZVAL(&retval, 0, 0);
|
||||
}
|
||||
|
||||
PHP_METHOD(Collection, reduceRight)
|
||||
{
|
||||
|
||||
zend_fcall_info fci;
|
||||
zend_fcall_info_cache fcc;
|
||||
ZEND_PARSE_PARAMETERS_START(1, 1)
|
||||
Z_PARAM_FUNC(fci, fcc)
|
||||
ZEND_PARSE_PARAMETERS_END();
|
||||
zend_array* current = COLLECTION_FETCH_CURRENT();
|
||||
if (zend_hash_num_elements(current) == 0) {
|
||||
ERR_BAD_SIZE();
|
||||
RETURN_NULL();
|
||||
}
|
||||
INIT_FCI(3);
|
||||
zval acc;
|
||||
Bucket* start = current->arData;
|
||||
Bucket* bucket = start + current->nNumUsed - 1;
|
||||
for (; bucket >= start; --bucket) {
|
||||
if (Z_ISUNDEF(bucket->val))
|
||||
continue;
|
||||
ZVAL_COPY_VALUE(&acc, &(bucket--)->val);
|
||||
break;
|
||||
}
|
||||
for (; bucket >= start; --bucket) {
|
||||
if (Z_ISUNDEF(bucket->val))
|
||||
continue;
|
||||
ZVAL_COPY_VALUE(¶ms[0], &acc);
|
||||
ZVAL_COPY_VALUE(¶ms[1], &bucket->val);
|
||||
if (bucket->key)
|
||||
ZVAL_STR(¶ms[2], bucket->key);
|
||||
else
|
||||
ZVAL_LONG(¶ms[2], bucket->h);
|
||||
zend_call_function(&fci, &fcc);
|
||||
zval_ptr_dtor(&acc);
|
||||
ZVAL_COPY_VALUE(&acc, &retval);
|
||||
}
|
||||
RETVAL_ZVAL(&retval, 0, 0);
|
||||
}
|
||||
|
||||
PHP_METHOD(Collection, remove)
|
||||
|
@ -1365,7 +1450,7 @@ PHP_METHOD(Collection, reverse)
|
|||
else if (HT_IS_PACKED(current))
|
||||
zend_hash_next_index_insert(reversed, &bucket->val);
|
||||
else
|
||||
zend_hash_index_add(reversed, &bucket->h, &bucket->val);
|
||||
zend_hash_index_add(reversed, bucket->h, &bucket->val);
|
||||
ZEND_HASH_FOREACH_END();
|
||||
if (GC_REFCOUNT(current) > 1)
|
||||
GC_DELREF(current);
|
||||
|
@ -1385,7 +1470,7 @@ PHP_METHOD(Collection, reversed)
|
|||
else if (HT_IS_PACKED(current))
|
||||
zend_hash_next_index_insert(reversed, &bucket->val);
|
||||
else
|
||||
zend_hash_index_add(reversed, &bucket->h, &bucket->val);
|
||||
zend_hash_index_add(reversed, bucket->h, &bucket->val);
|
||||
ZEND_HASH_FOREACH_END();
|
||||
RETVAL_NEW_COLLECTION(reversed);
|
||||
}
|
||||
|
@ -1571,7 +1656,7 @@ PHP_METHOD(Collection, takeLast)
|
|||
continue;
|
||||
taken[--num_taken] = bucket;
|
||||
}
|
||||
memset(&taken[0], NULL, num_taken * sizeof(Bucket*));
|
||||
memset(&taken[0], 0, num_taken * sizeof(Bucket*));
|
||||
int i = 0;
|
||||
for (; i < n; ++i) {
|
||||
Bucket* bucket = taken[i];
|
||||
|
@ -1607,7 +1692,7 @@ PHP_METHOD(Collection, takeLastWhile)
|
|||
else
|
||||
break;
|
||||
ZEND_HASH_FOREACH_END();
|
||||
memset(&taken[0], NULL, num_elements * sizeof(Bucket*));
|
||||
memset(&taken[0], 0, num_elements * sizeof(Bucket*));
|
||||
int i = 0;
|
||||
for (; i < zend_hash_num_elements(current); ++i) {
|
||||
Bucket* bucket = taken[i];
|
||||
|
|
|
@ -624,7 +624,7 @@ class Collection implements ArrayAccess, Countable
|
|||
* Accumulates value starting with the first element and applying operation from left to right to
|
||||
* current accumulator value and each element.
|
||||
*
|
||||
* @param $operation ($acc, $value, $key) -> $new_acc
|
||||
* @param callable $operation ($acc, $value, $key) -> $new_acc
|
||||
* @return mixed
|
||||
*/
|
||||
function reduce($operation) {}
|
||||
|
@ -633,7 +633,7 @@ class Collection implements ArrayAccess, Countable
|
|||
* Accumulates value starting with the last element and applying operation from right to left to
|
||||
* each element and current accumulator value.
|
||||
*
|
||||
* @param $operation ($acc, $value, $key) -> $new_acc
|
||||
* @param callable $operation ($acc, $value, $key) -> $new_acc
|
||||
* @return mixed
|
||||
*/
|
||||
function reduceRight($operation) {}
|
||||
|
@ -712,82 +712,90 @@ class Collection implements ArrayAccess, Countable
|
|||
function slice($keys) {}
|
||||
|
||||
/**
|
||||
* Sorts the collection in-place according to the natural order of its elements.
|
||||
* Sorts the collection in-place according to the specified order of its elements.
|
||||
*
|
||||
* @param int $order[optional]
|
||||
* @return void
|
||||
*/
|
||||
function sort() {}
|
||||
function sort($order = SORT_REGULAR) {}
|
||||
|
||||
/**
|
||||
* Sorts elements in the collection in-place according to natural sort order of the value returned
|
||||
* Sorts elements in the collection in-place according to the specified order of the value returned
|
||||
* by specified selector function.
|
||||
*
|
||||
* @param callable $selector ($value, $key) -> $new_value
|
||||
* @param int $flags[optional]
|
||||
* @return void
|
||||
*/
|
||||
function sortBy($selector) {}
|
||||
function sortBy($selector, $flags = SORT_REGULAR) {}
|
||||
|
||||
/**
|
||||
* Sorts elements in the collection in-place descending according to natural sort order of the
|
||||
* Sorts elements in the collection in-place descending according to the specified order of the
|
||||
* value returned by specified selector function.
|
||||
*
|
||||
* @param callable $selector ($value, $key) -> $new_value
|
||||
* @param int $flags[optional]
|
||||
* @return void
|
||||
*/
|
||||
function sortByDescending($selector) {}
|
||||
function sortByDescending($selector, $flags = SORT_REGULAR) {}
|
||||
|
||||
/**
|
||||
* Sorts elements in the collection in-place descending according to their natural sort order.
|
||||
* Sorts elements in the collection in-place descending according to the specified order.
|
||||
*
|
||||
* @param int $flags[optional]
|
||||
* @return void
|
||||
*/
|
||||
function sortDescending() {}
|
||||
function sortDescending($flags = SORT_REGULAR) {}
|
||||
|
||||
/**
|
||||
* Sorts elements in the collection in-place according to the order specified with comparator.
|
||||
*
|
||||
* @param callable $comparator ($value_1, $value_2, $key_1, $key_2) -> int
|
||||
* @param callable $comparator (Pair($key, $value), Pair($key, $value)) -> int
|
||||
* @return void
|
||||
*/
|
||||
function sortWith($comparator) {}
|
||||
|
||||
/**
|
||||
* Returns a collection of all elements sorted according to their natural sort order.
|
||||
* Returns a collection of all elements sorted according to the specified order.
|
||||
*
|
||||
* @param int $flags[optional]
|
||||
* @return Collection
|
||||
*/
|
||||
function sorted() {}
|
||||
function sorted($flags = SORT_REGULAR) {}
|
||||
|
||||
/**
|
||||
* Returns a collection of all elements sorted according to natural sort order of the
|
||||
* Returns a collection of all elements sorted according to the specified order of the
|
||||
* value returned by specified selector function.
|
||||
*
|
||||
* @param callable $selector ($value, $key) -> $new_value
|
||||
* @param int $flags[optional]
|
||||
* @return Collection
|
||||
*/
|
||||
function sortedBy($selector) {}
|
||||
function sortedBy($selector, $flags = SORT_REGULAR) {}
|
||||
|
||||
/**
|
||||
* Returns a collection of all elements sorted descending according to natural sort order
|
||||
* Returns a collection of all elements sorted descending according to the specified order
|
||||
* of the value returned by specified selector function.
|
||||
*
|
||||
* @param callable $selector ($value, $key) -> $new_value
|
||||
* @param int $flags[optional]
|
||||
* @return Collection
|
||||
*/
|
||||
function sortedByDescending($selector) {}
|
||||
function sortedByDescending($selector, $flags = SORT_REGULAR) {}
|
||||
|
||||
/**
|
||||
* Returns a collection of all elements sorted descending according to their natural sort order.
|
||||
* Returns a collection of all elements sorted descending according to the specified order.
|
||||
*
|
||||
* @param callable $selector ($value, $key) -> $new_value
|
||||
* @param int $flags[optional]
|
||||
* @return Collection
|
||||
*/
|
||||
function sortedDescending($selector) {}
|
||||
function sortedDescending($selector, $flags = SORT_REGULAR) {}
|
||||
|
||||
/**
|
||||
* Returns a collection of all elements sorted according to the specified comparator.
|
||||
*
|
||||
* @param callable $comparator ($value_1, $value_2, $key_1, $key_2) -> int
|
||||
* @param callable $comparator (Pair($key, $value), Pair($key, $value)) -> int
|
||||
* @return Collection
|
||||
*/
|
||||
function sortedWith($comparator) {}
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
--TEST--
|
||||
Test Collection::reduce() and Collection::reduceRight().
|
||||
--FILE--
|
||||
<?php
|
||||
$collection = Collection::init(['a', 'b', 'c', 'd', 'e']);
|
||||
$reduce_cb = function ($acc, $value, $key) {
|
||||
return $acc.$value.strval($key);
|
||||
};
|
||||
if ($collection->reduce($reduce_cb) != 'ab1c2d3e4')
|
||||
echo 'Collection::reduce() failed.', PHP_EOL;
|
||||
if ($collection->reduceRight($reduce_cb) != 'ed3c2b1a0')
|
||||
echo 'Collection::reduceRight() failed.', PHP_EOL;
|
||||
?>
|
||||
--EXPECT--
|
Reference in New Issue