From bf4eaca33970cfaa0391b95b9e2ffecbb1a3c7ca Mon Sep 17 00:00:00 2001 From: CismonX Date: Mon, 13 Aug 2018 19:35:34 +0800 Subject: [PATCH] add `shuffle()` and `shuffled()` --- src/collections_me.c | 1 + src/collections_methods.c | 36 ++++++++++++++++++++++++++++++++- src/php_collections_me.h | 1 + stubs/Collection.php | 11 ++++++++-- tests/041-shuffle-shuffled.phpt | 32 +++++++++++++++++++++++++++++ 5 files changed, 78 insertions(+), 3 deletions(-) create mode 100644 tests/041-shuffle-shuffled.phpt diff --git a/src/collections_me.c b/src/collections_me.c index e7f3ed0..53fa02c 100644 --- a/src/collections_me.c +++ b/src/collections_me.c @@ -198,6 +198,7 @@ const zend_function_entry collection_methods[] = { PHP_ME(Collection, reverse, NULL, ZEND_ACC_PUBLIC) PHP_ME(Collection, reversed, NULL, ZEND_ACC_PUBLIC) PHP_ME(Collection, shuffle, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Collection, shuffled, NULL, ZEND_ACC_PUBLIC) PHP_ME(Collection, set, key_value_arginfo, ZEND_ACC_PUBLIC) PHP_ME(Collection, single, predicate_arginfo, ZEND_ACC_PUBLIC) PHP_ME(Collection, slice, keys_arginfo, ZEND_ACC_PUBLIC) diff --git a/src/collections_methods.c b/src/collections_methods.c index 2f1b520..310957b 100644 --- a/src/collections_methods.c +++ b/src/collections_methods.c @@ -5,6 +5,7 @@ // #include +#include #include "php_collections.h" #include "php_collections_me.h" @@ -1479,7 +1480,40 @@ PHP_METHOD(Collection, reversed) PHP_METHOD(Collection, shuffle) { - + zend_array* current = COLLECTION_FETCH_CURRENT(); + uint32_t num_elements = zend_hash_num_elements(current); + ARRAY_NEW(shuffled, num_elements); + ZEND_HASH_FOREACH_VAL(current, zval* val) + zend_hash_next_index_insert(shuffled, val); + ZEND_HASH_FOREACH_END(); + size_t offset = 0; + Bucket* bucket = shuffled->arData; + for (; offset < num_elements - 1; ++offset) { + zend_long rand_idx = php_mt_rand_range(offset, num_elements - 1); + zend_hash_bucket_renum_swap(&bucket[offset], &bucket[rand_idx]); + } + if (GC_REFCOUNT(current) > 1) + GC_DELREF(current); + else + zend_hash_destroy(current); + COLLECTION_FETCH_CURRENT() = shuffled; +} + +PHP_METHOD(Collection, shuffled) +{ + zend_array* current = COLLECTION_FETCH_CURRENT(); + uint32_t num_elements = zend_hash_num_elements(current); + ARRAY_NEW(shuffled, num_elements); + ZEND_HASH_FOREACH_VAL(current, zval* val) + zend_hash_next_index_insert(shuffled, val); + ZEND_HASH_FOREACH_END(); + size_t offset = 0; + Bucket* bucket = shuffled->arData; + for (; offset < num_elements - 1; ++offset) { + zend_long rand_idx = php_mt_rand_range(offset, num_elements - 1); + zend_hash_bucket_renum_swap(&bucket[offset], &bucket[rand_idx]); + } + RETVAL_NEW_COLLECTION(shuffled); } PHP_METHOD(Collection, set) diff --git a/src/php_collections_me.h b/src/php_collections_me.h index 287d915..c5ec561 100644 --- a/src/php_collections_me.h +++ b/src/php_collections_me.h @@ -84,6 +84,7 @@ PHP_METHOD(Collection, retainAll); PHP_METHOD(Collection, reverse); PHP_METHOD(Collection, reversed); PHP_METHOD(Collection, shuffle); +PHP_METHOD(Collection, shuffled); PHP_METHOD(Collection, set); PHP_METHOD(Collection, single); PHP_METHOD(Collection, slice); diff --git a/stubs/Collection.php b/stubs/Collection.php index 7ccd75f..540a819 100644 --- a/stubs/Collection.php +++ b/stubs/Collection.php @@ -695,11 +695,18 @@ class Collection implements ArrayAccess, Countable function set($key, $value) {} /** - * Randomly shuffles elements in this collection. + * Randomly shuffles elements in this collection. Keys will be discarded. + * + * @return void + */ + function shuffle() {} + + /** + * Returns a shuffled copy of this collection. Keys will be discarded. * * @return Collection */ - function shuffle() {} + function shuffled() {} /** * Returns the single element matching the given predicate, or null if there is no or more than diff --git a/tests/041-shuffle-shuffled.phpt b/tests/041-shuffle-shuffled.phpt new file mode 100644 index 0000000..8c2a83b --- /dev/null +++ b/tests/041-shuffle-shuffled.phpt @@ -0,0 +1,32 @@ +--TEST-- +Test Collection::shuffle() and Collection::shuffled(). +--FILE-- + 1, 'b' => 2, 'c' => 3, 'd' => 4, 'e' => 5]; + +for ($i = 0; ; ++$i) { + $collection = Collection::init($array); + $collection->shuffle(); + if ($collection != array_values($array)) + break; + if ($i > 10) { + echo 'Collection::shuffle() failed.', PHP_EOL; + exit; + } +} +if (array_sum($array) != array_sum($collection->toArray())) + echo 'Collection::shuffle() failed.', PHP_EOL; + +for ($i = 0; ; ++$i) { + $reversed = Collection::init($array)->shuffled(); + if ($reversed != array_values($array)) + break; + if ($i > 10) { + echo 'Collection::shuffled() failed.', PHP_EOL; + exit; + } +} +if (array_sum($array) != array_sum($reversed->toArray())) + echo 'Collection::shuffled() failed.', PHP_EOL; +?> +--EXPECT--