Add `reduce()` and `reduceRight()`. Update signatures. Fix bugs. Update README.

This commit is contained in:
CismonX 2018-08-08 00:43:09 +08:00
parent ecfee75fbe
commit 440d3fdb63
4 changed files with 136 additions and 29 deletions

View File

@ -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

View File

@ -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(&params[0], &acc);
ZVAL_COPY_VALUE(&params[1], &bucket->val);
if (bucket->key)
ZVAL_STR(&params[2], bucket->key);
else
ZVAL_LONG(&params[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(&params[0], &acc);
ZVAL_COPY_VALUE(&params[1], &bucket->val);
if (bucket->key)
ZVAL_STR(&params[2], bucket->key);
else
ZVAL_LONG(&params[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];

View File

@ -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) {}

14
tests/039-reduce.phpt Normal file
View File

@ -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--