diff --git a/lib/std/core/array.c3 b/lib/std/core/array.c3 index be41a527c..e8346cee0 100644 --- a/lib/std/core/array.c3 +++ b/lib/std/core/array.c3 @@ -329,6 +329,72 @@ macro bool @all(array, #predicate) } +<* + Extract a copy of all even-index elements from an input array. + + @param [&inout] allocator : "The allocator used to create the return array." + @param [in] array : "The array from which to extract all even elements." + + @require @is_valid_list(array) : "Expected a valid list" + @require $defined(array[:lengthof(array)]) : "Expected a sliceable list" +*> +macro even(Allocator allocator, array) +{ + return unlace_impl{$typeof(array[0])}(allocator, array[:lengthof(array)]); +} + + +<* + Extract a copy of all odd-index elements from an input array. + + @param [&inout] allocator : "The allocator used to create the return array." + @param [in] array : "The array from which to extract all odd elements." + + @require @is_valid_list(array) : "Expected a valid list" + @require $defined(array[:lengthof(array)]) : "Expected a sliceable list" +*> +macro odd(Allocator allocator, array) +{ + return unlace_impl{$typeof(array[0])}(allocator, lengthof(array) > 1 ? array[1..] : ($typeof(array[0])[]){}); +} + +<* + Private implementation of `even` and `odd` macros, expecting a slice and returning one as well. + This function always extracts the even elements of the input slice. + + @param [&inout] allocator : "The allocator used to create the return array." + @param [in] array : "The array from which to extract all odd elements." +*> +fn Type[] unlace_impl(Allocator allocator, Type[] array) @private +{ + usz new_len = array.len / 2 + (array.len % 2 == 0 ? 0 : 1); + if (new_len == 0) return (Type[]){}; + Type[] new_array = allocator::new_array(allocator, Type, new_len); + foreach (x, &new : new_array) *new = types::implements_copy(Type) ??? array[x * 2].copy(allocator) : array[x * 2]; + return new_array[:new_len]; +} + +<* + Unlace or partition an input list into its component parts such that `[a, b, c, d, e]` becomes + `[a, c, e]` and `[b, d]`. Returned arrays are allocated by the given allocator and are returned + via two `out` parameters, `left` and `right`. + + @param [&inout] allocator : "The allocator used to create the returned arrays." + @param [in] array : "The input array to unlace." + @param [out] left : "Stores a copy of all even-index array elements." + @param [out] right : "Stores a copy of all odd-index array elements." + + @require @is_valid_list(array) : "Expected a valid list" + @require $typeof(left) == $typeof(array[0])[]* + @require $typeof(right) == $typeof(array[0])[]* +*> +macro unlace(Allocator allocator, array, left, right) +{ + if (left) *left = even(allocator, array); + if (right) *right = odd(allocator, array); +} + + <* Zip together two separate arrays/slices into a single array of Pairs or return values. Values will be collected up to the length of the shorter array if `fill_with` is left undefined; otherwise, they diff --git a/releasenotes.md b/releasenotes.md index 06b4d134f..590dc7353 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -24,6 +24,7 @@ - Add optional line-length limitations to `io::readline` and `io::readline_to_stream`. #2879 - Add Xorshiro128++. - Add single-byte code page support (DOS/OEM, Windows/ANSI, and ISO/IEC 8859). +- Add `array::even`, `array::odd`, and `array::unlace` macros. #2892 ### Fixes - Add error message if directory with output file name already exists diff --git a/test/unit/stdlib/core/array.c3 b/test/unit/stdlib/core/array.c3 index 2ec4f408b..40e77bbc7 100644 --- a/test/unit/stdlib/core/array.c3 +++ b/test/unit/stdlib/core/array.c3 @@ -401,3 +401,28 @@ fn void zip_into_linked_list() => @pool() test::eq(l.len(), 4); foreach (i, c : l.array_view()) test::@check(c == expected[i], "Mismatch on index %d: %s (actual) != %s (expected)", i, c, expected[i]); } + +fn void unlace() +{ + int[] list = { 1, 2, 3, 4, 5 }; + int[] outl, outr; + array::unlace(tmem, list, &outl, &outr); + test::@check(outl.len == 3 && outl == (int[]){ 1, 3, 5 }); + test::@check(outr.len == 2 && outr == (int[]){ 2, 4 }); + + String list2 = "abcdef"; + char[] outl2, outr2; + array::unlace(tmem, list2, &outl2, &outr2); + test::@check(outl2.len == 3 && outl2 == "ace"); + test::@check(outr2.len == 3 && outr2 == "bdf"); +} + +fn void unlace_list() +{ + List{bool} list; + list.push_all({ false, false, true, false, false, false }); + bool[] outl, outr; + array::unlace(tmem, list.array_view(), &outl, &outr); + test::@check(outl.len == 3 && outl == (bool[]){ false, true, false }); + test::@check(outr.len == 3 && outr == (bool[]){ false, false, false }); +}