├── .gitignore ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── TODO.md ├── bench.md ├── bench └── range_bench.zig ├── build.zig └── src ├── chain.zig ├── core ├── iterator.zig ├── monad.zig └── size-hint.zig ├── enumerate.zig ├── filter-map.zig ├── filter.zig ├── fp.zig ├── fuse.zig ├── map-while.zig ├── map.zig ├── range.zig ├── reverse.zig ├── skip-while.zig ├── slice.zig ├── step.zig ├── take-while.zig ├── take.zig ├── test_all.zig └── utils.zig /.gitignore: -------------------------------------------------------------------------------- 1 | **/*.o 2 | **/bench-* 3 | **/zig-cache 4 | 5 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright 2022 tizee 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2022 tizee 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # zig-fp 2 | 3 | Functional programmimng style patterns in Zig lang. 4 | 5 | ``` 6 | zig version 7 | 0.10.0-dev.3340+c6f5832bb 8 | ``` 9 | 10 | ## Features 11 | 12 | - Iterators 13 | - Monad 14 | 15 | ## Usage 16 | 17 | ```zig 18 | const std = @import("std"); 19 | const it = @import("zig-fp"); 20 | 21 | fn isEven(val: u32) bool { 22 | return val % 2 == 0; 23 | } 24 | 25 | fn toChar(val: u32) u8 { 26 | if(val % 4 == 0) { 27 | return '0'; 28 | }else{ 29 | return '1'; 30 | } 31 | } 32 | 33 | fn print(val: u8) void{ 34 | std.debug.print("{}\n", .{val}); 35 | } 36 | 37 | 38 | it.range(u32,0,100,1) 39 | .filter(isEven) 40 | .map(toChar) 41 | .for_each(print); 42 | ``` 43 | 44 | > Currently Zig does not support closure but we can achieve similar functionality with `comptime` and `type` to build the context for reusing the same iterator interface. 45 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | ## TODO 2 | 3 | - [x] Iterator type 4 | - [x] General iterator 5 | - [x] all 6 | - [x] any 7 | - [x] chain 8 | - [x] fuse 9 | - [x] into_array 10 | - [x] last 11 | - [ ] map_while 12 | - [x] nextN 13 | - [x] skipN 14 | - [x] skip_while 15 | - [x] step 16 | - [x] sum 17 | - [x] take 18 | - [x] take_while 19 | - [ ] unzip 20 | - [ ] zip 21 | - [x] count 22 | - [x] filter_map 23 | - [x] find 24 | - [x] fold 25 | - [x] for_each 26 | - [x] map 27 | - [x] next 28 | - [x] peek 29 | - [x] peekAhead 30 | - [x] reduce 31 | - [x] size_hint 32 | - [x] skip 33 | - [x] Double-ended iterator 34 | - [x] all 35 | - [x] any 36 | - [x] chain 37 | - [x] fuse 38 | - [x] into_array 39 | - [x] last 40 | - [ ] map_while 41 | - [x] nextBackN 42 | - [x] nextN 43 | - [x] skipBackN 44 | - [x] skipN 45 | - [x] skip_while 46 | - [x] step 47 | - [x] sum 48 | - [x] take 49 | - [x] take_while 50 | - [ ] unzip 51 | - [ ] zip 52 | - [x] count 53 | - [x] filter_map 54 | - [x] find 55 | - [x] fold 56 | - [x] for_each 57 | - [x] map 58 | - [x] next 59 | - [x] nextBack 60 | - [x] peek 61 | - [x] peekAhead 62 | - [x] peekBackward 63 | - [x] reduce 64 | - [x] reverse 65 | - [x] size_hint 66 | - [x] skip 67 | - [x] ChainIterator (Wrapper) 68 | - [x] FuseIterator (Wrapper) 69 | - [ ] MapWhileIterator (Wrapper) 70 | - [x] SkipWhileIterator (Wrapper) 71 | - [x] StepIterator (Wrapper) 72 | - [x] TakeIterator (Wrapper) 73 | - [x] TakeWhileIterator (Wrapper) 74 | - [x] EnumerateIterator (Wrapper) 75 | - [x] FilterIterator (Wrapper) 76 | - [x] FilterMapIterator (Wrapper) 77 | - [x] MapIterator (Wrapper) 78 | - [x] RangeIterator (Double-ended iterator) 79 | - [x] ReverseIterator (Wrapper) 80 | - [x] SliceIterator (Double-ended iterator) 81 | - [ ] Monad 82 | - [ ] Closure 83 | -------------------------------------------------------------------------------- /bench.md: -------------------------------------------------------------------------------- 1 | ## Range 2 | 3 | - see `bench/range_bench.zig` 4 | 5 | Following are results for `range` and `RangeContext`, the time cost is about a factor of 2 compared to the `while` loop code. 6 | 7 | ``` 8 | times: 40 9 | while : 1000ns base 10 | context: 4000ns ~3.997002997002997e+00 times 11 | range : 1000ns ~1.0e+00 times 12 | 13 | 14 | 15 | times: 400 16 | while : 2000ns base 17 | context: 3000ns ~1.4997501249375311e+00 times 18 | range : 3000ns ~1.4997501249375311e+00 times 19 | 20 | 21 | 22 | times: 4000 23 | while : 14000ns base 24 | context: 32000ns ~2.28562245553889e+00 times 25 | range : 31000ns ~2.2141989857867297e+00 times 26 | 27 | 28 | 29 | times: 40000 30 | while : 144000ns base 31 | context: 311000ns ~2.1597141686516066e+00 times 32 | range : 310000ns ~2.1527697724321357e+00 times 33 | 34 | 35 | 36 | times: 400000 37 | while : 1437000ns base 38 | context: 3262000ns ~2.2700060751523488e+00 times 39 | range : 3098000ns ~2.1558795018235895e+00 times 40 | 41 | 42 | 43 | times: 4000000 44 | while : 14478000ns base 45 | context: 31345000ns ~2.165008898673235e+00 times 46 | range : 31183000ns ~2.1538195086462557e+00 times 47 | ``` 48 | -------------------------------------------------------------------------------- /bench/range_bench.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const debug = std.debug; 3 | const RangeContext = @import("../src/range.zig").RangeContext; 4 | const range = @import("../src/range.zig").range; 5 | const Range64 = RangeContext(u64); 6 | 7 | const MAX: u64 = 5_000_000; 8 | 9 | fn whileLoop(times: u64) void { 10 | var i: u64 = 0; 11 | while (i < times) : (i += 1) { 12 | asm volatile ("" ::: "memory"); 13 | } 14 | } 15 | 16 | fn rangeLoop(times: u64) void { 17 | var iter = range(u64, 0, times, 1); 18 | 19 | while (iter.next()) |_| { 20 | asm volatile ("" ::: "memory"); 21 | } 22 | } 23 | 24 | fn rangeContextLoop(times: u64) void { 25 | var iter = Range64.init(0, times, 1); 26 | 27 | while (iter.nextFn()) |_| { 28 | asm volatile ("" ::: "memory"); 29 | } 30 | } 31 | 32 | fn bench() void { 33 | var i: u64 = 40; 34 | while (i < MAX) : (i *= 10) { 35 | var start = std.time.nanoTimestamp(); 36 | whileLoop(i); 37 | var end = std.time.nanoTimestamp(); 38 | const whileTime = end - start; 39 | 40 | start = std.time.nanoTimestamp(); 41 | rangeLoop(i); 42 | end = std.time.nanoTimestamp(); 43 | const contextTime = end - start; 44 | 45 | start = std.time.nanoTimestamp(); 46 | rangeLoop(i); 47 | end = std.time.nanoTimestamp(); 48 | const rangeTime = end - start; 49 | 50 | debug.print( 51 | \\ 52 | \\ 53 | \\ times: {} 54 | \\ while : {}ns base 55 | \\ context: {}ns ~{} times 56 | \\ range : {}ns ~{} times 57 | \\ 58 | \\ 59 | , .{ 60 | i, 61 | whileTime, 62 | contextTime, 63 | @intToFloat(f64, contextTime + 1) / @intToFloat(f64, whileTime + 1), 64 | rangeTime, 65 | @intToFloat(f64, rangeTime + 1) / @intToFloat(f64, whileTime + 1), 66 | }); 67 | } 68 | } 69 | 70 | pub fn main() anyerror!void { 71 | bench(); 72 | } 73 | -------------------------------------------------------------------------------- /build.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | pub fn build(b: *std.build.Builder) void { 4 | // Standard release options allow the person running `zig build` to select 5 | // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. 6 | const mode = b.standardReleaseOptions(); 7 | 8 | const cwd = b.build_root; 9 | 10 | const lib = b.addStaticLibrary("zig-fp", "src/fp.zig"); 11 | lib.setBuildMode(mode); 12 | lib.install(); 13 | 14 | const main_tests = b.addTest("src/test_all.zig"); 15 | main_tests.setBuildMode(mode); 16 | 17 | const test_step = b.step("test", "Run library tests"); 18 | test_step.dependOn(&main_tests.step); 19 | 20 | const target = b.standardTargetOptions(.{}); 21 | const bench_range = b.step("bench-range", "Bench range iterator"); 22 | const bench_range_exe = b.addExecutable("bench-range", "bench/range_bench.zig"); 23 | bench_range_exe.addPackagePath("../src/range.zig", "src/range.zig"); 24 | 25 | bench_range_exe.setTarget(target); 26 | bench_range_exe.setBuildMode(mode); 27 | const range_bench_output_dir = std.fs.path.join(b.allocator, &[_][]const u8{ cwd, "bench" }) catch unreachable; 28 | bench_range_exe.setOutputDir(range_bench_output_dir); 29 | bench_range_exe.single_threaded = true; 30 | bench_range_exe.install(); 31 | const run_cmd = bench_range_exe.run(); 32 | bench_range.dependOn(&run_cmd.step); 33 | } 34 | -------------------------------------------------------------------------------- /src/chain.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const IIterator = @import("core/iterator.zig").IIterator; 3 | const SizeHint = @import("core/size-hint.zig").SizeHint; 4 | 5 | const SliceIter = @import("slice.zig"); 6 | const GetPtrChildType = @import("utils.zig").GetPtrChildType; 7 | const IterAssert = @import("utils.zig"); 8 | 9 | pub fn DoubleEndedChainContext(comptime ContextA: type, comptime ContextB: type) type { 10 | comptime { 11 | if (@TypeOf(ContextA.ItemType) != @TypeOf(ContextB.ItemType)) { 12 | @compileError("Chained Iterators should have the same item type"); 13 | } 14 | IterAssert.assertDoubleEndedIteratorContext(ContextA); 15 | IterAssert.assertDoubleEndedIteratorContext(ContextB); 16 | } 17 | return struct { 18 | const Self = @This(); 19 | pub const ItemType = ContextA.ItemType; 20 | 21 | index: usize = 0, 22 | contextA: ContextA = undefined, 23 | contextB: ContextB = undefined, 24 | 25 | pub fn init(a: ContextA, b: ContextB) Self { 26 | return Self{ .contextA = a, .contextB = b }; 27 | } 28 | 29 | /// return 0 if the context does not support 30 | /// size_hint 31 | pub fn sizeHintFn(self: *Self) SizeHint { 32 | return SizeHint.join(self.contextA.sizeHintFn(), self.contextB.sizeHintFn()); 33 | } 34 | 35 | /// Look at the nth item without advancing 36 | pub fn peekAheadFn(self: *Self, n: usize) ?ItemType { 37 | if (self.contextA.peekAheadFn(n)) |value| { 38 | return value; 39 | } else { 40 | if (self.contextA.sizeHintFn().len()) |len| { 41 | return self.contextB.peekAheadFn(n - len); 42 | } else { 43 | var i: usize = 0; 44 | while (self.contextA.peekAheadFn(i + 1)) |_| : (i += 1) {} 45 | return self.contextB.peekAheadFn(n - i); 46 | } 47 | } 48 | } 49 | 50 | pub fn peekBackwardFn(self: *Self, n: usize) bool { 51 | if (self.contextA.peekBackwardFn(n)) |value| { 52 | return value; 53 | } else { 54 | if (self.contextA.sizeHintFn().len()) |len| { 55 | return self.contextB.peekBackwardFn(n - len); 56 | } else { 57 | var i: usize = 0; 58 | while (self.contextA.peekBackwardFn(i + 1)) |_| : (i += 1) {} 59 | return self.contextB.peekBackwardFn(n - i); 60 | } 61 | } 62 | } 63 | 64 | pub fn nextFn(self: *Self) ?ItemType { 65 | if (self.contextA.nextFn()) |value| { 66 | return value; 67 | } 68 | return self.contextB.nextFn(); 69 | } 70 | 71 | pub fn nextBackFn(self: *Self) ?ItemType { 72 | if (self.contextA.nextBackFn()) |value| { 73 | return value; 74 | } 75 | return self.contextB.nextBackFn(); 76 | } 77 | 78 | pub fn skipFn(self: *Self) bool { 79 | if (self.contextA.skipFn()) { 80 | return true; 81 | } 82 | return self.contextB.skipFn(); 83 | } 84 | 85 | pub fn skipBackFn(self: *Self) bool { 86 | if (self.contextA.skipBackFn()) { 87 | return true; 88 | } 89 | return self.contextB.skipBackFn(); 90 | } 91 | }; 92 | } 93 | 94 | pub fn ChainContext(comptime ContextA: type, comptime ContextB: type) type { 95 | comptime { 96 | if (@TypeOf(ContextA.ItemType) != @TypeOf(ContextB.ItemType)) { 97 | @compileError("Chained Iterators should have the same item type"); 98 | } 99 | IterAssert.assertIteratorContext(ContextA); 100 | IterAssert.assertIteratorContext(ContextB); 101 | } 102 | return struct { 103 | const Self = @This(); 104 | pub const ItemType = ContextA.ItemType; 105 | 106 | index: usize = 0, 107 | contextA: ContextA = undefined, 108 | contextB: ContextB = undefined, 109 | 110 | pub fn init(a: ContextA, b: ContextB) Self { 111 | return Self{ .contextA = a, .contextB = b }; 112 | } 113 | 114 | /// return 0 if the context does not support 115 | /// size_hint 116 | pub fn sizeHintFn(self: *Self) SizeHint { 117 | return SizeHint.join(self.contextA.sizeHintFn(), self.contextB.sizeHintFn()); 118 | } 119 | 120 | /// Look at the nth item without advancing 121 | pub fn peekAheadFn(self: *Self, n: usize) ?ItemType { 122 | if (self.contextA.peekAheadFn(n)) |value| { 123 | return value; 124 | } else { 125 | if (self.contextA.sizeHintFn().len()) |len| { 126 | return self.contextB.peekAheadFn(n - len); 127 | } else { 128 | var i: usize = 0; 129 | while (self.contextA.peekAheadFn(i + 1)) |_| : (i += 1) {} 130 | return self.contextB.peekAheadFn(n - i); 131 | } 132 | } 133 | } 134 | 135 | pub fn nextFn(self: *Self) ?ItemType { 136 | if (self.contextA.nextFn()) |value| { 137 | return value; 138 | } 139 | return self.contextB.nextFn(); 140 | } 141 | 142 | pub fn skipFn(self: *Self) bool { 143 | if (self.contextA.skipFn()) { 144 | return true; 145 | } 146 | return self.contextB.skipFn(); 147 | } 148 | }; 149 | } 150 | 151 | /// A Chain Iterator struct 152 | /// It's actually a wrapper over an iterator 153 | pub fn ChainIterator(comptime ContextA: type, comptime ContextB: type) type { 154 | comptime { 155 | if (IterAssert.isDoubleEndedIteratorContext(ContextA) and IterAssert.isDoubleEndedIteratorContext(ContextB)) { 156 | const ChainContextType = DoubleEndedChainContext(ContextA, ContextB); 157 | return IIterator(ChainContextType); 158 | } else { 159 | const ChainContextType = ChainContext(ContextA, ContextB); 160 | return IIterator(ChainContextType); 161 | } 162 | } 163 | } 164 | 165 | pub fn chain(a: anytype, b: anytype) ChainIterator(SliceIter.SliceContext(GetPtrChildType(@TypeOf(a))), SliceIter.SliceContext(GetPtrChildType(@TypeOf(b)))) { 166 | return SliceIter.slice(a).chain(SliceIter.slice(b)); 167 | } 168 | 169 | const testing = std.testing; 170 | 171 | test "test ChainIterator" { 172 | const hello: []const u8 = "hello,"; 173 | const world: []const u8 = "world"; 174 | var a = SliceIter.slice(hello); 175 | var b = SliceIter.slice(world); 176 | 177 | const truth: []const u8 = "hello,world"; 178 | 179 | const Chain = ChainIterator(@TypeOf(a).IterContext, @TypeOf(b).IterContext); 180 | var chainIter = Chain.initWithTwoContext(a.context, b.context); 181 | 182 | try IterAssert.testIterator(chainIter, truth, truth.len, SizeHint{ 183 | .low = 11, 184 | .high = 11, 185 | }); 186 | } 187 | 188 | test "test chain" { 189 | const hello: []const u8 = "hello,"; 190 | const world: []const u8 = "world"; 191 | 192 | const truth: []const u8 = "hello,world"; 193 | 194 | var chainIter = chain(hello, world); 195 | 196 | try IterAssert.testIterator(chainIter, truth, truth.len, SizeHint{ 197 | .low = 11, 198 | .high = 11, 199 | }); 200 | } 201 | -------------------------------------------------------------------------------- /src/core/iterator.zig: -------------------------------------------------------------------------------- 1 | const EnumerateIterator = @import("../enumerate.zig").EnumerateIterator; 2 | const FilterIterator = @import("../filter.zig").FilterIterator; 3 | const FilterMapIterator = @import("../filter-map.zig").FilterMapIterator; 4 | const IterAssert = @import("../utils.zig"); 5 | const MapIterator = @import("../map.zig").MapIterator; 6 | const RangeIterator = @import("../range.zig").RangeIterator; 7 | const ReverseIterator = @import("../reverse.zig").ReverseIterator; 8 | const ChainIterator = @import("../chain.zig").ChainIterator; 9 | const StepIterator = @import("../step.zig").StepIterator; 10 | const TakeIterator = @import("../take.zig").TakeIterator; 11 | const TakeWhileIterator = @import("../take-while.zig").TakeWhileIterator; 12 | const SkipWhileIterator = @import("../skip-while.zig").SkipWhileIterator; 13 | const FuseIterator = @import("../fuse.zig").FuseIterator; 14 | 15 | const std = @import("std"); 16 | const Allocator = std.mem.Allocator; 17 | const ArrayList = std.ArrayList; 18 | 19 | const slice = @import("../slice.zig").slice; 20 | const SizeHint = @import("size-hint.zig").SizeHint; 21 | 22 | /// Create a double-ended iterator if its context support 23 | /// related methods like nextBackFn etc. Otherwise, it will 24 | /// create a normal iterator type. 25 | /// This is designed to be a low-level interface. That's to 26 | /// say, you should define your iterator context to use this 27 | /// interface. 28 | /// 29 | /// Here are some reasons there are multiple constructors. 30 | /// 31 | /// If you're familiar with OOP, this sounds like decorator 32 | /// pattern. We could reuse the same context since most of 33 | /// iterators are functional programming abstractions that 34 | /// manipulate their beneath level of iterator's output. 35 | /// 36 | /// 1. Basic iterators 37 | /// 38 | /// Basic iterators are iterators over types or numbers, 39 | /// which includes the SliceIterator and RangeIterator. 40 | /// 41 | /// 2. Iterator with functions 42 | /// 43 | /// When we want to modify the iterator's output on the fly, 44 | /// i.e. with a map iterator, we could use the initWithFunc 45 | /// to pass in the function type. 46 | /// 47 | /// MapIterator, FilterIterator, FilterMapIterator are all 48 | /// initialized with this function. 49 | /// 50 | /// 3. Iterators without functions 51 | /// 52 | /// EnumerateIterator and ReverseIterator are created with 53 | /// initWithInnerContext. 54 | /// 55 | /// With these 3 constructors, we could write chained function 56 | /// calls. 57 | /// 58 | /// const iter = range(u32,1,100).map(func1) // u32 -> Type1 59 | /// .map(func2) // Type1 -> Type2 60 | /// .map(func3) // Type2 -> Type3 61 | /// .map(func4) // Typee -> Type4 62 | /// 63 | /// Note that this behavior relies on the Zig lang compiler. 64 | /// It would also generate many intermediate types. 65 | /// Although the Zig lang uses duck typing, which means 66 | /// we could reuse existing types, the cost should not be 67 | /// underestimated. 68 | /// 69 | /// More specifically, above code would generate: 70 | /// 71 | /// T1 RangeContext(u32) 72 | /// T2 IIterator(T1) -> range(u32,1,100) 73 | /// T3 DoubleEndedMapContext(T2) 74 | /// T4 IIterator(T3,@TypeOf(func1)) -> map(func1) 75 | /// T5 DoubleEndedMapContext(T3) 76 | /// T6 IIterator(T5,@TypeOf(func2)) -> map(func2) 77 | /// T7 DoubleEndedMapContext(T5) 78 | /// T8 IIterator(T7,@TypeOf(func3)) -> map(func3) 79 | /// T9 DoubleEndedMapContext(T7) 80 | /// T10 IIterator(T9,@TypeOf(func4)) -> map(func4) 81 | /// 82 | /// 83 | pub fn IIterator( 84 | comptime Context: type, 85 | ) type { 86 | if (IterAssert.isDoubleEndedIteratorContext(Context)) { 87 | return struct { 88 | const Self = @This(); 89 | pub const IterContext = Context; 90 | pub const ItemType = Context.ItemType; 91 | 92 | // A flag marks whether the context has been moved out 93 | // This is ugly but it can be used to check whether the context has been moved out 94 | // in the runtime. 95 | moved: bool = false, 96 | context: Context, 97 | 98 | fn set_moved(self: *Self) void { 99 | if (self.moved) { 100 | @panic("use moved context"); 101 | } else { 102 | self.moved = true; 103 | } 104 | } 105 | 106 | /// return 0 if the context does not support 107 | /// size_hint 108 | pub fn size_hint(self: *Self) SizeHint { 109 | if (@hasDecl(IterContext, "sizeHintFn")) { 110 | return self.context.sizeHintFn(); 111 | } else { 112 | return .{}; 113 | } 114 | } 115 | 116 | pub fn initWithContext(context: IterContext) Self { 117 | return Self{ .context = context }; 118 | } 119 | 120 | // for wrappers without Fn like Reverse 121 | pub fn initWithInnerContext(context: anytype) Self { 122 | return Self{ .context = Context.init(context) }; 123 | } 124 | 125 | // for wrappers with Fn like Map,Filter,FilterMap 126 | pub fn initWithFunc(context: anytype, f: anytype) Self { 127 | return Self{ .context = Context.init(context, f) }; 128 | } 129 | 130 | // for wrappers with combined iterators like Chain 131 | pub fn initWithTwoContext(a: anytype, b: anytype) Self { 132 | return Self{ .context = Context.init(a, b) }; 133 | } 134 | 135 | // for wrappers with a state like Step 136 | pub fn initWithAState(context: anytype, init_state: anytype) Self { 137 | return Self{ .context = Context.init(context, init_state) }; 138 | } 139 | 140 | /// Look at the next item without advancing 141 | pub fn peek(self: *Self) ?ItemType { 142 | return self.context.peekAheadFn(0); 143 | } 144 | 145 | /// Look at the nth item without advancing 146 | pub fn peekAhead(self: *Self, n: usize) ?ItemType { 147 | return self.context.peekAheadFn(n); 148 | } 149 | 150 | /// Look at the nth item without advancing 151 | pub fn peekBackward(self: *Self, n: usize) ?ItemType { 152 | return self.context.peekBackwardFn(n); 153 | } 154 | 155 | // get the next n-th element (n==0, return null) 156 | pub fn nextN(self: *Self, n: usize) ?ItemType { 157 | if (n == 0) return null; 158 | var i: usize = 0; 159 | while (i < n - 1) : (i += 1) { 160 | if (!self.context.skipFn()) { 161 | return null; 162 | } 163 | } 164 | return self.context.nextFn(); 165 | } 166 | 167 | // Advances the iterator by n items 168 | pub fn skipN(self: *Self, n: usize) bool { 169 | var i: usize = 0; 170 | while (i < n) : (i += 1) { 171 | if (!self.context.skipFn()) { 172 | return false; 173 | } 174 | } 175 | return true; 176 | } 177 | 178 | // Advances the iterator backward by n items 179 | pub fn skipBackN(self: *Self, n: usize) bool { 180 | var i: usize = 0; 181 | while (i < n) : (i += 1) { 182 | if (!self.context.skipBackFn()) { 183 | return false; 184 | } 185 | } 186 | return true; 187 | } 188 | 189 | pub fn nextBackN(self: *Self, n: usize) ?ItemType { 190 | if (n == 0) return null; 191 | var i: usize = 0; 192 | while (i < n - 1) : (i += 1) { 193 | if (!self.context.skipBackFn()) { 194 | return null; 195 | } 196 | } 197 | return self.context.nextBackFn(); 198 | } 199 | 200 | /// count the length 201 | pub fn count(self: *Self) ?usize { 202 | return self.size_hint().len(); 203 | } 204 | 205 | /// Advances the iterator and return the value 206 | pub fn next(self: *Self) ?ItemType { 207 | return self.context.nextFn(); 208 | } 209 | 210 | pub fn nextBack(self: *Self) ?ItemType { 211 | return self.context.nextBackFn(); 212 | } 213 | 214 | /// Advances the iterator, return false if failed 215 | pub fn skip(self: *Self) bool { 216 | return self.context.skipFn(); 217 | } 218 | 219 | /// Advances the iterator backward, return false if failed 220 | pub fn skipBack(self: *Self) bool { 221 | return self.context.skipBackFn(); 222 | } 223 | 224 | // this iterator should be exhausted after enumerate() 225 | pub fn enumerate(self: *Self) EnumerateIterator(Self.IterContext) { 226 | return EnumerateIterator(Self.IterContext).initWithInnerContext(self.context); 227 | } 228 | 229 | /// transform ItemType to NewType and return a new Iterator 230 | pub fn map(self: *Self, f: anytype) MapIterator(Self.IterContext, @TypeOf(f)) { 231 | self.set_moved(); 232 | return MapIterator(Self.IterContext, @TypeOf(f)).initWithFunc(self.context, f); 233 | } 234 | 235 | // this iterator should be exhausted after filter() 236 | pub fn filter(self: *Self, f: anytype) FilterIterator(Self.IterContext, @TypeOf(f)) { 237 | self.set_moved(); 238 | return FilterIterator(Self.IterContext, @TypeOf(f)).initWithFunc(self.context, f); 239 | } 240 | 241 | // this iterator should be exhausted after reverse() 242 | pub fn reverse(self: *Self) ReverseIterator(Self.IterContext) { 243 | self.set_moved(); 244 | self.context.reverseFn(); 245 | return ReverseIterator(Self.IterContext).initWithInnerContext(self.context); 246 | } 247 | 248 | // this iterator should be exhausted after filter_map() 249 | pub fn filter_map(self: *Self, f: anytype) FilterMapIterator(Self.IterContext, @TypeOf(f)) { 250 | self.set_moved(); 251 | return FilterMapIterator(Self.IterContext, @TypeOf(f)).initWithFunc(self.context, f); 252 | } 253 | 254 | // chain two iterators together by move out the ownership of their IterContext 255 | pub fn chain(self: *Self, iter: anytype) ChainIterator(Self.IterContext, @TypeOf(iter).IterContext) { 256 | self.set_moved(); 257 | comptime { 258 | if (@hasDecl(@TypeOf(iter), "IterContext")) { 259 | IterAssert.assertIteratorContext(@TypeOf(iter).IterContext); 260 | } else { 261 | @compileError("Iterator must define its IterContext type"); 262 | } 263 | } 264 | return ChainIterator(Self.IterContext, @TypeOf(iter).IterContext).initWithTwoContext(self.context, iter.context); 265 | } 266 | 267 | // Consumes the iterator 268 | pub fn step(self: *Self, init_state: usize) StepIterator(Self.IterContext) { 269 | self.set_moved(); 270 | return StepIterator(Self.IterContext).initWithAState(self.context, init_state); 271 | } 272 | 273 | // Consumes the iterator 274 | pub fn take(self: *Self, init_state: usize) TakeIterator(Self.IterContext) { 275 | self.set_moved(); 276 | return TakeIterator(Self.IterContext).initWithAState(self.context, init_state); 277 | } 278 | 279 | // Consumes the iterator 280 | pub fn take_while(self: *Self, f: anytype) TakeWhileIterator(Self.IterContext, @TypeOf(f)) { 281 | self.set_moved(); 282 | return TakeWhileIterator(Self.IterContext, @TypeOf(f)).initWithFunc(self.context, f); 283 | } 284 | 285 | // Consumes the iterator 286 | pub fn skip_while(self: *Self, f: anytype) SkipWhileIterator(Self.IterContext, @TypeOf(f)) { 287 | self.set_moved(); 288 | return SkipWhileIterator(Self.IterContext, @TypeOf(f)).initWithFunc(self.context, f); 289 | } 290 | 291 | // Consumes the iterator 292 | pub fn fuse(self: *Self) FuseIterator(Self.IterContext) { 293 | self.set_moved(); 294 | return FuseIterator(Self.IterContext).initWithInnerContext(self.context); 295 | } 296 | 297 | /// Consumes the iterator and apply the f for each item 298 | pub fn for_each(self: *Self, f: fn (item: ItemType) void) void { 299 | while (self.context.nextFn()) |value| { 300 | f(value); 301 | } 302 | } 303 | 304 | pub fn reduce(self: *Self, f: fn (accum: ItemType, cur: ItemType) ItemType) ?ItemType { 305 | var res = self.peek(); 306 | _ = self.next(); 307 | while (self.context.nextFn()) |value| { 308 | res = f(res.?, value); 309 | } 310 | return res; 311 | } 312 | 313 | // Consumes the iterator 314 | pub fn fold(self: *Self, comptime NewType: type, init: NewType, f: fn (NewType, ItemType) NewType) NewType { 315 | var res = init; 316 | while (self.context.nextFn()) |value| { 317 | res = f(res, value); 318 | } 319 | return res; 320 | } 321 | 322 | // Consumes the iterator 323 | pub fn find(self: *Self, f: fn (ItemType) bool) ?ItemType { 324 | while (self.context.nextFn()) |value| { 325 | if (f(value)) { 326 | return value; 327 | } 328 | } 329 | return null; 330 | } 331 | 332 | // Consumes the iterator 333 | pub fn all(self: *Self, f: fn (ItemType) bool) bool { 334 | while (self.context.nextFn()) |value| { 335 | if (!f(value)) { 336 | return false; 337 | } 338 | } 339 | return true; 340 | } 341 | 342 | // Consumes the iterator 343 | pub fn any(self: *Self, f: fn (ItemType) bool) bool { 344 | while (self.context.nextFn()) |value| { 345 | if (f(value)) { 346 | return true; 347 | } 348 | } 349 | return false; 350 | } 351 | 352 | // Consumes the iterator 353 | pub fn sum(self: *Self) ?ItemType { 354 | if (IterAssert.isNumber(Self.ItemType)) { 355 | var val: ItemType = 0; 356 | while (self.context.nextFn()) |value| { 357 | val += value; 358 | } 359 | return val; 360 | } else { 361 | return null; 362 | } 363 | } 364 | 365 | // Consumes the iterator 366 | pub fn last(self: *Self) ?ItemType { 367 | while (self.context.peekAheadFn(1)) |_| { 368 | _ = self.context.skipFn(); 369 | } 370 | return self.context.nextFn(); 371 | } 372 | 373 | pub fn into_array(self: *Self, list: *ArrayList(Self.ItemType)) !*ArrayList(Self.ItemType) { 374 | while (self.context.nextFn()) |value| { 375 | try list.*.append(value); 376 | } 377 | return list; 378 | } 379 | }; 380 | } else { 381 | return struct { 382 | const Self = @This(); 383 | pub const IterContext = Context; 384 | pub const ItemType = Context.ItemType; 385 | 386 | // A flag marks whether the context has been moved out 387 | // This is ugly but it can be used to check whether the context has been moved out 388 | // in the runtime. 389 | moved: bool = false, 390 | context: Context, 391 | 392 | fn set_moved(self: *Self) void { 393 | if (self.moved) { 394 | @panic("use moved context"); 395 | } else { 396 | self.moved = true; 397 | } 398 | } 399 | 400 | /// return 0 if the context does not support 401 | /// size_hint 402 | pub fn size_hint(self: *Self) SizeHint { 403 | if (@hasDecl(IterContext, "sizeHintFn")) { 404 | return self.context.sizeHintFn(); 405 | } else { 406 | return .{}; 407 | } 408 | } 409 | 410 | pub fn initWithContext(context: IterContext) Self { 411 | return Self{ .context = context }; 412 | } 413 | 414 | // for wrappers without Fn like Reverse 415 | pub fn initWithInnerContext(context: anytype) Self { 416 | return Self{ .context = Context.init(context) }; 417 | } 418 | 419 | // for wrappers with Fn like Map,Filter,FilterMap 420 | pub fn initWithFunc(context: anytype, f: anytype) Self { 421 | return Self{ .context = Context.init(context, f) }; 422 | } 423 | 424 | // for wrappers with combined iterators like Chain 425 | pub fn initWithTwoContext(a: anytype, b: anytype) Self { 426 | return Self{ .context = Context.init(a, b) }; 427 | } 428 | 429 | // for wrappers with a state like Step 430 | pub fn initWithAState(context: anytype, init_state: anytype) Self { 431 | return Self{ .context = Context.init(context, init_state) }; 432 | } 433 | 434 | /// Look at the next item without advancing 435 | pub fn peek(self: *Self) ?ItemType { 436 | return self.context.peekAheadFn(0); 437 | } 438 | 439 | /// Look at the nth item without advancing 440 | pub fn peekAhead(self: *Self, n: usize) ?ItemType { 441 | return self.context.peekAheadFn(n); 442 | } 443 | 444 | // get the next n-th element (n==0, return null) 445 | pub fn nextN(self: *Self, n: usize) ?ItemType { 446 | if (n == 0) return null; 447 | var i: usize = 0; 448 | while (i < n - 1) : (i += 1) { 449 | if (!self.context.skipFn()) { 450 | return null; 451 | } 452 | } 453 | return self.context.nextFn(); 454 | } 455 | 456 | // Advances the iterator by n items 457 | pub fn skipN(self: *Self, n: usize) bool { 458 | var i: usize = 0; 459 | while (i < n) : (i += 1) { 460 | if (!self.context.skipFn()) { 461 | return false; 462 | } 463 | } 464 | return true; 465 | } 466 | 467 | /// count the length 468 | pub fn count(self: *Self) ?usize { 469 | return self.size_hint().len(); 470 | } 471 | 472 | /// Advances the iterator and return the value 473 | pub fn next(self: *Self) ?ItemType { 474 | return self.context.nextFn(); 475 | } 476 | 477 | /// Advances the iterator, return false if failed 478 | pub fn skip(self: *Self) bool { 479 | return self.context.skipFn(); 480 | } 481 | 482 | // this iterator should be exhausted after enumerate() 483 | pub fn enumerate(self: *Self) EnumerateIterator(Self.IterContext) { 484 | return EnumerateIterator(Self.IterContext).initWithInnerContext(self.context); 485 | } 486 | 487 | /// transform ItemType to NewType and return a new Iterator 488 | pub fn map(self: *Self, f: anytype) MapIterator(Self.IterContext, @TypeOf(f)) { 489 | self.set_moved(); 490 | return MapIterator(Self.IterContext, @TypeOf(f)).initWithFunc(self.context, f); 491 | } 492 | 493 | // this iterator should be exhausted after filter() 494 | pub fn filter(self: *Self, f: anytype) FilterIterator(Self.IterContext, @TypeOf(f)) { 495 | self.set_moved(); 496 | return FilterIterator(Self.IterContext, @TypeOf(f)).initWithFunc(self.context, f); 497 | } 498 | 499 | // this iterator should be exhausted after filter_map() 500 | pub fn filter_map(self: *Self, f: anytype) FilterMapIterator(Self.IterContext, @TypeOf(f)) { 501 | self.set_moved(); 502 | return FilterMapIterator(Self.IterContext, @TypeOf(f)).initWithFunc(self.context, f); 503 | } 504 | 505 | // chain two iterators together by move out the ownership of their IterContext 506 | pub fn chain(self: *Self, iter: anytype) ChainIterator(Self.IterContext, @TypeOf(iter).IterContext) { 507 | self.set_moved(); 508 | comptime { 509 | if (@hasDecl(@TypeOf(iter), "IterContext")) { 510 | IterAssert.assertIteratorContext(@TypeOf(iter).IterContext); 511 | } else { 512 | @compileError("Iterator must define its IterContext type"); 513 | } 514 | } 515 | return ChainIterator(Self.IterContext, @TypeOf(iter).IterContext).initWithTwoContext(self.context, iter.context); 516 | } 517 | 518 | // Consumes the iterator 519 | pub fn step(self: *Self, init_state: usize) StepIterator(Self.IterContext) { 520 | self.set_moved(); 521 | return StepIterator(Self.IterContext).initWithAState(self.context, init_state); 522 | } 523 | 524 | // Consumes the iterator 525 | pub fn take(self: *Self, init_state: usize) TakeIterator(Self.IterContext) { 526 | self.set_moved(); 527 | return TakeIterator(Self.IterContext).initWithAState(self.context, init_state); 528 | } 529 | 530 | // Consumes the iterator 531 | pub fn take_while(self: *Self, f: anytype) TakeWhileIterator(Self.IterContext, @TypeOf(f)) { 532 | self.set_moved(); 533 | return TakeWhileIterator(Self.IterContext, @TypeOf(f)).initWithFunc(self.context, f); 534 | } 535 | 536 | // Consumes the iterator 537 | pub fn skip_while(self: *Self, f: anytype) SkipWhileIterator(Self.IterContext, @TypeOf(f)) { 538 | self.set_moved(); 539 | return SkipWhileIterator(Self.IterContext, @TypeOf(f)).initWithFunc(self.context, f); 540 | } 541 | 542 | // Consumes the iterator 543 | pub fn fuse(self: *Self) FuseIterator(Self.IterContext) { 544 | self.set_moved(); 545 | return FuseIterator(Self.IterContext).initWithInnerContext(self.context); 546 | } 547 | 548 | /// Consumes the iterator and apply the f for each item 549 | pub fn for_each(self: *Self, f: fn (item: ItemType) void) void { 550 | while (self.context.nextFn()) |value| { 551 | f(value); 552 | } 553 | } 554 | 555 | pub fn reduce(self: *Self, f: fn (accum: ItemType, cur: ItemType) ItemType) ?ItemType { 556 | var res = self.peek(); 557 | _ = self.next(); 558 | while (self.context.nextFn()) |value| { 559 | res = f(res.?, value); 560 | } 561 | return res; 562 | } 563 | 564 | // Consumes the iterator 565 | pub fn fold(self: *Self, comptime NewType: type, init: NewType, f: fn (NewType, ItemType) NewType) NewType { 566 | var res = init; 567 | while (self.context.nextFn()) |value| { 568 | res = f(res, value); 569 | } 570 | return res; 571 | } 572 | 573 | // Consumes the iterator 574 | pub fn find(self: *Self, f: fn (ItemType) bool) ?ItemType { 575 | while (self.context.nextFn()) |value| { 576 | if (f(value)) { 577 | return value; 578 | } 579 | } 580 | return null; 581 | } 582 | 583 | // Consumes the iterator 584 | pub fn all(self: *Self, f: fn (ItemType) bool) bool { 585 | while (self.context.nextFn()) |value| { 586 | if (!f(value)) { 587 | return false; 588 | } 589 | } 590 | return true; 591 | } 592 | 593 | // Consumes the iterator 594 | pub fn any(self: *Self, f: fn (ItemType) bool) bool { 595 | while (self.context.nextFn()) |value| { 596 | if (f(value)) { 597 | return true; 598 | } 599 | } 600 | return false; 601 | } 602 | 603 | // Consumes the iterator 604 | pub fn sum(self: *Self) ?ItemType { 605 | if (IterAssert.isNumber(Self.ItemType)) { 606 | var val: ItemType = 0; 607 | while (self.context.nextFn()) |value| { 608 | val += value; 609 | } 610 | return val; 611 | } else { 612 | return null; 613 | } 614 | } 615 | 616 | // Consumes the iterator 617 | pub fn last(self: *Self) ?ItemType { 618 | while (self.context.peekAheadFn(1)) |_| { 619 | _ = self.context.skipFn(); 620 | } 621 | return self.context.nextFn(); 622 | } 623 | 624 | pub fn into_array(self: *Self, list: *ArrayList(Self.ItemType)) !*ArrayList(Self.ItemType) { 625 | while (self.context.nextFn()) |value| { 626 | try list.*.append(value); 627 | } 628 | return list; 629 | } 630 | }; 631 | } 632 | } 633 | 634 | const debug = std.debug; 635 | const testing = std.testing; 636 | 637 | test "test filter" { 638 | const ints: []const u32 = &[_]u32{ 1, 2, 3, 4 }; 639 | var iter = slice(ints); 640 | 641 | const S = struct { 642 | pub fn large(cur: u32) bool { 643 | return cur > 2; 644 | } 645 | }.large; 646 | 647 | var truth: []const u32 = &[_]u32{ 3, 4 }; 648 | var filter_iter = iter.filter(S); 649 | var i: usize = 0; 650 | while (filter_iter.next()) |value| { 651 | debug.print("filter result {}\n", .{value}); 652 | try testing.expectEqual(truth[i], value); 653 | i += 1; 654 | } 655 | } 656 | 657 | test "test enumerate" { 658 | const str: []const u8 = "abcd"; 659 | var iter = slice(str); 660 | var enum_iter = iter.enumerate(); 661 | 662 | var i: usize = 0; 663 | while (enum_iter.next()) |value| { 664 | debug.print("{}\n", .{value}); 665 | try testing.expectEqual(value.index, i); 666 | i += 1; 667 | } 668 | } 669 | 670 | test "test peekAhead peekBackward" { 671 | const str: []const u8 = "abcd"; 672 | var iter = slice(str); 673 | 674 | var i: usize = 0; 675 | while (i < str.len) : (i += 1) { 676 | var res = iter.peekAhead(i); 677 | try testing.expectEqual(@as(?u8, str[i]), res); 678 | } 679 | try testing.expect(iter.peekAhead(i) == null); 680 | i = 0; 681 | while (i > 0) : (i -= 1) { 682 | var res = iter.peekBackward(i); 683 | try testing.expectEqual(@as(?u8, str[str.len - i - 1]), res); 684 | } 685 | try testing.expect(iter.peekBackward(i) == null); 686 | } 687 | 688 | test "test skipBack" { 689 | const str: []const u8 = "abcd"; 690 | var iter = slice(str); 691 | 692 | var i: usize = 0; 693 | while (iter.next()) |value| : (i += 1) { 694 | try testing.expectEqual(@as(?u8, str[i]), value); 695 | } 696 | try testing.expect(i == str.len); 697 | try testing.expect(iter.next() == null); 698 | i = 0; 699 | while (iter.nextBack()) |value| : (i += 1) { 700 | try testing.expectEqual(@as(?u8, str[str.len - i - 1]), value); 701 | } 702 | try testing.expect(i == str.len); 703 | try testing.expect(iter.nextBack() == null); 704 | i = 0; 705 | while (iter.next()) |value| : (i += 1) { 706 | try testing.expectEqual(@as(?u8, str[i]), value); 707 | } 708 | } 709 | 710 | test "test reverse" { 711 | const str: []const u8 = "abcd"; 712 | var iter = slice(str); 713 | var reversed_iter = iter.reverse(); 714 | 715 | const truth: []const u8 = "dcba"; 716 | var i: usize = 0; 717 | while (reversed_iter.next()) |value| { 718 | try testing.expectEqual(truth[i], value); 719 | i += 1; 720 | } 721 | } 722 | 723 | test "test reduce" { 724 | const ints: []const u32 = &[_]u32{ 1, 2, 3, 4 }; 725 | var iter = slice(ints); 726 | 727 | const Fn = struct { 728 | pub fn sum(accum: u32, cur: u32) u32 { 729 | return accum + cur; 730 | } 731 | }.sum; 732 | 733 | var res = iter.reduce(Fn); 734 | debug.print("{}\n", .{res.?}); 735 | try testing.expectEqual(@as(?u32, 10), res); 736 | } 737 | 738 | test "test for each" { 739 | const str: []const u8 = "abcd"; 740 | var iter = slice(str); 741 | 742 | const Fn = struct { 743 | pub fn print(cur: u8) void { 744 | debug.print("{}\n", .{cur}); 745 | } 746 | }.print; 747 | 748 | iter.for_each(Fn); 749 | } 750 | 751 | test "test fold" { 752 | const str: []const u8 = "abcd"; 753 | var iter = slice(str); 754 | 755 | const Fn = struct { 756 | pub fn sum(accum: u32, cur: u8) u32 { 757 | return accum + @as(u32, (cur - 'a')); 758 | } 759 | }.sum; 760 | 761 | var res = iter.fold(u32, 0, Fn); 762 | debug.print("{}\n", .{res}); 763 | try testing.expectEqual(@as(u32, 6), res); 764 | } 765 | 766 | test "test find" { 767 | const str: []const u8 = "abcd"; 768 | var iter = slice(str); 769 | 770 | const Fn = struct { 771 | pub fn findD(value: u8) bool { 772 | return value == 'd'; 773 | } 774 | }.findD; 775 | 776 | var res = iter.find(Fn); 777 | debug.print("{}\n", .{res.?}); 778 | try testing.expectEqual(@as(?u8, 'd'), res); 779 | try testing.expect(iter.next() == null); 780 | } 781 | 782 | test "test count" { 783 | const str: []const u8 = "abcd"; 784 | var iter = slice(str); 785 | 786 | var len: usize = str.len; 787 | 788 | while (iter.next()) |_| : (len -= 1) { 789 | try testing.expectEqual(@as(?usize, len - 1), iter.count()); 790 | } 791 | 792 | try testing.expect(iter.next() == null); 793 | } 794 | 795 | test "test all" { 796 | const str: []const u8 = "abcd"; 797 | var iter = slice(str); 798 | 799 | var len: usize = str.len; 800 | 801 | while (iter.next()) |_| : (len -= 1) { 802 | try testing.expectEqual(@as(?usize, len - 1), iter.count()); 803 | } 804 | 805 | try testing.expect(iter.next() == null); 806 | } 807 | 808 | test "test any" { 809 | const str: []const u8 = "abcd"; 810 | var iter = slice(str); 811 | 812 | var len: usize = str.len; 813 | 814 | while (iter.next()) |_| : (len -= 1) { 815 | try testing.expectEqual(@as(?usize, len - 1), iter.count()); 816 | } 817 | 818 | try testing.expect(iter.next() == null); 819 | } 820 | 821 | test "test last" { 822 | const str: []const u8 = "abcd"; 823 | var iter = slice(str); 824 | 825 | try testing.expectEqual(@as(?u8, 'd'), iter.last()); 826 | } 827 | 828 | test "test sum" { 829 | const str: []const u32 = &[_]u32{ 1, 2, 3 }; 830 | var iter = slice(str); 831 | try testing.expectEqual(@as(u32, 6), iter.sum().?); 832 | } 833 | 834 | test "test into_array" { 835 | const str: []const u8 = "abcd"; 836 | const allocator = testing.allocator; 837 | var iter = slice(str); 838 | var list = &ArrayList(u8).init(allocator); 839 | defer list.*.deinit(); 840 | 841 | var values = try iter.into_array(list); 842 | var i: usize = 0; 843 | for (values.*.items) |value| { 844 | try testing.expectEqual(@as(u8, str[i]), value); 845 | i += 1; 846 | } 847 | } 848 | 849 | test "test nextN and nextBackN" { 850 | const str: []const u8 = "abcd"; 851 | var iter = slice(str); 852 | var iter2 = slice(str); 853 | 854 | while (iter.next()) |value| { 855 | try testing.expectEqual(value, iter2.nextN(1).?); 856 | } 857 | try testing.expect(iter.next() == null); 858 | try testing.expect(iter2.next() == null); 859 | 860 | while (iter.nextBack()) |value| { 861 | try testing.expectEqual(value, iter2.nextBackN(1).?); 862 | } 863 | try testing.expect(iter.nextBack() == null); 864 | try testing.expect(iter2.nextBackN(1) == null); 865 | } 866 | 867 | test "test skipN and skipBackN " { 868 | const str: []const u8 = "abcd"; 869 | var iter = slice(str); 870 | var iter2 = slice(str); 871 | 872 | _ = iter.skipN(1); 873 | try IterAssert.testIterator(iter, str[1..], 3, .{ 874 | .low = 3, 875 | .high = 3, 876 | }); 877 | 878 | _ = iter2.skipN(2); 879 | try IterAssert.testIterator(iter2, str[2..], 2, .{ 880 | .low = 2, 881 | .high = 2, 882 | }); 883 | } 884 | -------------------------------------------------------------------------------- /src/core/monad.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | pub fn Monad(comptime T: type, comptime F: anytype) type { 4 | if (comptime std.meta.trait.hasFunctions(F(T), .{ "Monad_pure", "Monad_bind" })) { 5 | return F(T); 6 | } else { 7 | @compileError("given type is not monad instance"); 8 | } 9 | } 10 | 11 | pub fn MonadicOption(comptime T: type) type { 12 | return struct { 13 | const Self = @This(); 14 | some: bool, 15 | value: T, 16 | pub fn Monad_pure(t: T) Self { 17 | return Self{ .some = true, .value = t }; 18 | } 19 | pub fn Monad_bind(self: Self, f: anytype) Self { 20 | if (self.some) { 21 | return f.invoke(.{self.value}); 22 | } else { 23 | return self; 24 | } 25 | } 26 | }; 27 | } 28 | 29 | // thanks MasterQ32/closure.zig 30 | pub fn Closure(comptime function: anytype) type { 31 | const F = @TypeOf(function); 32 | const A0 = @typeInfo(F).Fn.args[0].arg_type.?; 33 | 34 | return struct { 35 | const Self = @This(); 36 | 37 | pub const is_mutable = (@typeInfo(A0) == .Pointer); 38 | pub const State = if (is_mutable) 39 | std.meta.Child(A0) 40 | else 41 | A0; 42 | pub const Result = (@typeInfo(F).Fn.return_type.?); 43 | 44 | state: State, 45 | 46 | pub fn init(state: State) Self { 47 | return Self{ .state = state }; 48 | } 49 | 50 | pub fn invoke(self: if (is_mutable) *Self else Self, args: anytype) Result { 51 | return @call(.{}, function, .{if (is_mutable) &self.state else self.state} ++ args); 52 | } 53 | }; 54 | } 55 | 56 | const testing = std.testing; 57 | 58 | test "simple monad" { 59 | const AddM = struct { 60 | pub fn add_m(comptime T: type, comptime F: anytype, a1: Monad(T, F), a2: Monad(T, F)) Monad(T, F) { 61 | const closure1 = Closure(struct { 62 | fn f(state: struct { a2: Monad(T, F) }, a: T) Monad(T, F) { 63 | const closure2 = Closure(struct { 64 | fn g(state1: struct { a: T }, b: T) Monad(T, F) { 65 | return Monad(T, F).Monad_pure(state1.a + b); 66 | } 67 | }.g).init( 68 | .{ .a = a }, 69 | ); 70 | return Monad(T, F).Monad_bind(state.a2, closure2); 71 | } 72 | }.f).init( 73 | .{ .a2 = a2 }, 74 | ); 75 | return Monad(T, F).Monad_bind(a1, closure1); 76 | } 77 | }.add_m; 78 | const res = AddM(i32, MonadicOption, MonadicOption(i32).Monad_pure(1), MonadicOption(i32).Monad_pure(2)); 79 | try testing.expectEqual(@as(i32, 3), res.value); 80 | } 81 | -------------------------------------------------------------------------------- /src/core/size-hint.zig: -------------------------------------------------------------------------------- 1 | /// SizeHint is used for get the length of iterator 2 | pub const SizeHint = struct { 3 | low: usize = 0, 4 | high: ?usize = null, 5 | 6 | pub fn len(hint: *@This()) ?usize { 7 | if (hint.high) |value| { 8 | return value; 9 | } 10 | return null; 11 | } 12 | 13 | /// create a new SizeHint from two SizeHint 14 | pub fn join(hint1: SizeHint, hint2: SizeHint) SizeHint { 15 | if (hint1.high) |h1| { 16 | if (hint2.high) |h2| { 17 | return SizeHint{ 18 | .low = hint1.low + hint2.low, 19 | .high = h1 + h2, 20 | }; 21 | } 22 | } 23 | return SizeHint{ .low = hint1.low + hint2.low, .high = null }; 24 | } 25 | }; 26 | -------------------------------------------------------------------------------- /src/enumerate.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const IIterator = @import("core/iterator.zig").IIterator; 3 | const SizeHint = @import("core/size-hint.zig").SizeHint; 4 | 5 | const SliceIter = @import("slice.zig"); 6 | const GetPtrChildType = @import("utils.zig").GetPtrChildType; 7 | const IterAssert = @import("utils.zig"); 8 | 9 | pub fn DoubleEndedEnumerateContext(comptime Context: type) type { 10 | comptime { 11 | IterAssert.assertDoubleEndedIteratorContext(Context); 12 | } 13 | return struct { 14 | const Self = @This(); 15 | pub const InnerContextType = Context; 16 | pub const ItemType = struct { index: usize, value: Context.ItemType }; 17 | 18 | index: usize = 0, 19 | context: Context, 20 | 21 | pub fn init(context: InnerContextType) Self { 22 | return Self{ .context = context, .index = @as(usize, 0) }; 23 | } 24 | 25 | /// return 0 if the context does not support 26 | /// size_hint 27 | pub fn sizeHintFn(self: *Self) SizeHint { 28 | if (@hasDecl(InnerContextType, "sizeHintFn")) { 29 | return self.context.sizeHintFn(); 30 | } else { 31 | return .{}; 32 | } 33 | } 34 | 35 | /// Look at the nth item without advancing 36 | pub fn peekAheadFn(self: *Self, n: usize) ?ItemType { 37 | if (self.context.peekAheadFn(n)) |value| { 38 | return ItemType{ 39 | .index = self.index + n, 40 | .value = value, 41 | }; 42 | } 43 | return null; 44 | } 45 | 46 | pub fn peekBackwardFn(self: *Self, n: usize) bool { 47 | if (self.context.peekBackwardFn(n)) |value| { 48 | return ItemType{ 49 | .index = self.index - n, 50 | .value = value, 51 | }; 52 | } 53 | return null; 54 | } 55 | 56 | pub fn nextFn(self: *Self) ?ItemType { 57 | if (self.context.nextFn()) |value| { 58 | defer self.index += 1; 59 | return ItemType{ 60 | .index = self.index, 61 | .value = value, 62 | }; 63 | } 64 | return null; 65 | } 66 | 67 | pub fn nextBackFn(self: *Self) ?ItemType { 68 | if (self.context.nextBackFn()) |value| { 69 | defer self.index += 1; 70 | return ItemType{ 71 | .index = self.index, 72 | .value = value, 73 | }; 74 | } 75 | return null; 76 | } 77 | 78 | pub fn skipFn(self: *Self) bool { 79 | return self.context.skipFn(); 80 | } 81 | 82 | pub fn skipBackFn(self: *Self) bool { 83 | return self.context.skipBackFn(); 84 | } 85 | }; 86 | } 87 | 88 | pub fn EnumerateContext(comptime Context: type) type { 89 | comptime { 90 | IterAssert.assertIteratorContext(Context); 91 | } 92 | return struct { 93 | const Self = @This(); 94 | pub const InnerContextType = Context; 95 | pub const ItemType = struct { index: usize, value: Context.ItemType }; 96 | 97 | index: usize, 98 | context: Context, 99 | 100 | pub fn init(context: InnerContextType) Self { 101 | return Self{ .context = context, .index = @as(usize, 0) }; 102 | } 103 | 104 | /// return 0 if the context does not support 105 | /// size_hint 106 | pub fn size_hint(self: *Self) SizeHint { 107 | if (@hasDecl(InnerContextType, "sizeHintFn")) { 108 | return self.context.sizeHintFn(); 109 | } else { 110 | return .{}; 111 | } 112 | } 113 | 114 | /// Look at the nth item without advancing 115 | pub fn peekAheadFn(self: *Self, n: usize) ?ItemType { 116 | if (self.context.peekAheadFn(n)) |value| { 117 | return ItemType{ 118 | .index = self.index + n, 119 | .value = value, 120 | }; 121 | } 122 | return null; 123 | } 124 | 125 | pub fn nextFn(self: *Self) ?ItemType { 126 | if (self.context.nextFn()) |value| { 127 | defer self.index += 1; 128 | return ItemType{ 129 | .index = self.index, 130 | .value = value, 131 | }; 132 | } 133 | return null; 134 | } 135 | 136 | pub fn skipFn(self: *Self) bool { 137 | return self.context.skipFn(); 138 | } 139 | }; 140 | } 141 | 142 | /// A Enumerate Iterator struct 143 | /// It's actually a wrapper over an iterator 144 | pub fn EnumerateIterator(comptime Context: type) type { 145 | if (IterAssert.isDoubleEndedIteratorContext(Context)) { 146 | const EnumerateContextType = DoubleEndedEnumerateContext(Context); 147 | return IIterator(EnumerateContextType); 148 | } else { 149 | const EnumerateContextType = EnumerateContext(Context); 150 | return IIterator(EnumerateContextType); 151 | } 152 | } 153 | 154 | pub fn enumerate(s: anytype) EnumerateIterator(SliceIter.SliceContext(GetPtrChildType(@TypeOf(s)))) { 155 | return SliceIter.slice(s).enumerate(); 156 | } 157 | 158 | const debug = std.debug; 159 | const testing = std.testing; 160 | const testIterator = @import("utils.zig").testIterator; 161 | 162 | test "test enumerate" { 163 | const ints: []const u32 = &[_]u32{ 1, 2, 3, 4, 5, 6 }; 164 | 165 | var iter = enumerate(ints); 166 | for (ints) |value| { 167 | if (iter.next()) |tuple| { 168 | try testing.expectEqual(@as(usize, value - 1), tuple.index); 169 | try testing.expectEqual(@as(u32, value), tuple.value); 170 | } 171 | } 172 | try testing.expect(iter.next() == null); 173 | } 174 | -------------------------------------------------------------------------------- /src/filter-map.zig: -------------------------------------------------------------------------------- 1 | const GetPtrChildType = @import("utils.zig").GetPtrChildType; 2 | const IIterator = @import("core/iterator.zig").IIterator; 3 | const IterAssert = @import("utils.zig"); 4 | const SizeHint = @import("core/size-hint.zig").SizeHint; 5 | const SliceIter = @import("slice.zig"); 6 | 7 | pub fn DoubleEndedFilterMapContext(comptime Context: type, comptime TransformFn: type) type { 8 | comptime { 9 | IterAssert.assertDoubleEndedIteratorContext(Context); 10 | } 11 | return struct { 12 | const Self = @This(); 13 | pub const InnerContextType = Context; 14 | pub const ItemType = @typeInfo(TransformFn).Fn.return_type.?; 15 | 16 | transformFn: TransformFn, 17 | context: Context, 18 | 19 | pub fn init(context: InnerContextType, transformFn: TransformFn) Self { 20 | return Self{ 21 | .transformFn = transformFn, 22 | .context = context, 23 | }; 24 | } 25 | 26 | /// return 0 if the context does not support 27 | /// size_hint 28 | pub fn sizeHintFn(self: *Self) SizeHint { 29 | if (@hasDecl(InnerContextType, "sizeHintFn")) { 30 | return self.context.sizeHintFn(); 31 | } else { 32 | return .{}; 33 | } 34 | } 35 | 36 | /// Look at the nth item without advancing 37 | pub fn peekAheadFn(self: *Self, n: usize) ?ItemType { 38 | var i: usize = 0; 39 | var count: usize = 0; 40 | while (count < n) : (i += 1) { 41 | if (self.context.peekAheadFn(i)) |value| { 42 | if (self.transformFn(value)) |_| { 43 | count += 1; 44 | } 45 | } else { 46 | break; 47 | } 48 | } 49 | return self.context.peekAheadFn(i); 50 | } 51 | 52 | pub fn peekBackwardFn(self: *Self, n: usize) bool { 53 | var i: usize = 0; 54 | var count: usize = 0; 55 | while (count < n) : (i += 1) { 56 | if (self.context.peekBackwardFn(i)) |value| { 57 | if (self.transformFn(value)) |_| { 58 | i += 1; 59 | } 60 | } else { 61 | break; 62 | } 63 | } 64 | return self.context.peekBackwardFn(i); 65 | } 66 | 67 | pub fn nextFn(self: *Self) ?ItemType { 68 | while (self.context.nextFn()) |value| { 69 | if (self.transformFn(value)) |res| { 70 | return res; 71 | } 72 | } 73 | return null; 74 | } 75 | 76 | pub fn nextBackFn(self: *Self) ?ItemType { 77 | while (self.context.nextBackFn()) |value| { 78 | if (self.transformFn(value)) |res| { 79 | return res; 80 | } 81 | } 82 | return null; 83 | } 84 | 85 | pub fn skipFn(self: *Self) bool { 86 | while (self.context.nextFn()) |value| { 87 | if (self.transformFn(value)) |_| { 88 | return true; 89 | } 90 | } 91 | return false; 92 | } 93 | 94 | pub fn skipBackFn(self: *Self) bool { 95 | while (self.context.nextBackFn()) |value| { 96 | if (self.transformFn(value)) |_| { 97 | return true; 98 | } 99 | } 100 | return false; 101 | } 102 | }; 103 | } 104 | 105 | pub fn FilterMapContext(comptime Context: type, comptime TransformFn: type) type { 106 | comptime { 107 | IterAssert.isIteratorContext(Context); 108 | } 109 | return struct { 110 | const Self = @This(); 111 | pub const InnerContextType = Context; 112 | pub const ItemType = @typeInfo(TransformFn).Fn.return_type.?; 113 | 114 | transformFn: TransformFn = undefined, 115 | context: Context = undefined, 116 | 117 | pub fn init(context: InnerContextType, transformFn: TransformFn) Self { 118 | return Self{ 119 | .context = context, 120 | .transformFn = transformFn, 121 | }; 122 | } 123 | 124 | /// return 0 if the context does not support 125 | /// size_hint 126 | pub fn size_hint(self: *Self) SizeHint { 127 | if (@hasDecl(InnerContextType, "sizeHintFn")) { 128 | return self.context.sizeHintFn(); 129 | } else { 130 | return .{}; 131 | } 132 | } 133 | 134 | pub fn peekAheadFn(self: *Self, n: usize) ?ItemType { 135 | var i: usize = 0; 136 | var count: usize = 0; 137 | while (count < n) : (i += 1) { 138 | if (self.context.peekAheadFn(i)) |value| { 139 | if (self.transformFn(value)) |_| { 140 | count += 1; 141 | } 142 | } else { 143 | break; 144 | } 145 | } 146 | return self.context.peekAheadFn(i); 147 | } 148 | 149 | pub fn nextFn(self: *Self) ?ItemType { 150 | while (self.context.nextFn()) |value| { 151 | if (self.transformFn(value)) |res| { 152 | return res; 153 | } 154 | } 155 | return null; 156 | } 157 | 158 | pub fn skipFn(self: *Self) bool { 159 | while (self.context.nextFn()) |value| { 160 | if (self.transformFn(value)) |_| { 161 | return true; 162 | } 163 | } 164 | return false; 165 | } 166 | }; 167 | } 168 | 169 | /// A FilterMap Iterator struct 170 | /// It's actually a wrapper over an iterator 171 | pub fn FilterMapIterator(comptime Context: type, comptime TransformFn: type) type { 172 | if (IterAssert.isDoubleEndedIteratorContext(Context)) { 173 | const FilterMapContextType = DoubleEndedFilterMapContext(Context, TransformFn); 174 | return IIterator(FilterMapContextType); 175 | } else { 176 | const FilterMapContextType = FilterMapContext(Context, TransformFn); 177 | return IIterator(FilterMapContextType); 178 | } 179 | } 180 | 181 | pub fn filterMap(s: anytype, transformFn: anytype) FilterMapIterator(SliceIter.SliceContext(GetPtrChildType(@TypeOf(s))), @TypeOf(transformFn)) { 182 | return SliceIter.slice(s).filter_map(transformFn); 183 | } 184 | 185 | const std = @import("std"); 186 | const debug = std.debug; 187 | const testing = std.testing; 188 | 189 | const SliceIterator = @import("slice.zig").SliceIterator; 190 | const slice = @import("slice.zig").slice; 191 | 192 | test "test FilterMapIterator" { 193 | const transform = struct { 194 | pub fn transform(value: u8) ?bool { 195 | if (value == '1') return true; 196 | return null; 197 | } 198 | }.transform; 199 | const StrIter = SliceIterator(u8); 200 | const str = "0011"; 201 | var context = StrIter.IterContext.init(str); 202 | 203 | const Map = FilterMapIterator(StrIter.IterContext, @TypeOf(transform)); 204 | var map_context = Map.IterContext.init(context, transform); 205 | var map_iter = Map.initWithContext(map_context); 206 | 207 | const truth = [_]bool{ true, true }; 208 | var i: usize = 0; 209 | while (map_iter.next()) |value| { 210 | debug.print("{}\n", .{value.?}); 211 | try testing.expectEqual(truth[i], value.?); 212 | i += 1; 213 | } 214 | } 215 | 216 | test "test slice filter_map" { 217 | const str: []const u8 = "0011"; 218 | var slice_iter = slice(str); 219 | 220 | const transform = struct { 221 | pub fn transform(value: u8) ?bool { 222 | if (value == '1') return true; 223 | return null; 224 | } 225 | }.transform; 226 | var map_iter = slice_iter.filter_map(transform); 227 | 228 | while (map_iter.next()) |value| { 229 | debug.print("{}\n", .{value.?}); 230 | try testing.expect(value.?); 231 | } 232 | } 233 | 234 | test "test filterMap method" { 235 | const str: []const u8 = "0011"; 236 | 237 | const transform = struct { 238 | pub fn transform(value: u8) ?bool { 239 | if (value == '1') return true; 240 | return null; 241 | } 242 | }.transform; 243 | 244 | var map_iter = filterMap(str, transform); 245 | while (map_iter.next()) |value| { 246 | debug.print("{}\n", .{value.?}); 247 | try testing.expect(value.?); 248 | } 249 | } 250 | -------------------------------------------------------------------------------- /src/filter.zig: -------------------------------------------------------------------------------- 1 | const IIterator = @import("core/iterator.zig").IIterator; 2 | const SliceIter = @import("slice.zig"); 3 | const SizeHint = @import("core/size-hint.zig").SizeHint; 4 | 5 | const GetPtrChildType = @import("utils.zig").GetPtrChildType; 6 | const IterAssert = @import("utils.zig"); 7 | 8 | /// The filter iterator is designed to be lazily evaluated 9 | /// as the the map iterator 10 | /// It's just a wrapper over underlying iterator 11 | pub fn DoubleEndedFilterContext(comptime Context: type, comptime FilterFn: type) type { 12 | comptime { 13 | IterAssert.assertDoubleEndedIteratorContext(Context); 14 | } 15 | return struct { 16 | const Self = @This(); 17 | pub const InnerContextType = Context; 18 | pub const ItemType = Context.ItemType; 19 | 20 | filterFn: FilterFn, 21 | context: Context, 22 | 23 | pub fn init(context: InnerContextType, filterFn: FilterFn) Self { 24 | return Self{ 25 | .filterFn = filterFn, 26 | .context = context, 27 | }; 28 | } 29 | /// return 0 if the context does not support 30 | /// size_hint 31 | pub fn sizeHintFn(self: *Self) SizeHint { 32 | if (@hasDecl(InnerContextType, "sizeHintFn")) { 33 | return self.context.sizeHintFn(); 34 | } else { 35 | return {}; 36 | } 37 | } 38 | 39 | /// Look at the nth item without advancing 40 | pub fn peekAheadFn(self: *Self, n: usize) ?ItemType { 41 | var count: usize = 0; 42 | var i: usize = 0; 43 | while (count < n) { 44 | if (self.context.peekAheadFn(i)) |value| { 45 | if (self.filterFn(value)) { 46 | count += 1; 47 | } 48 | i += 1; 49 | } else { 50 | break; 51 | } 52 | } 53 | return self.context.peekAheadFn(i); 54 | } 55 | 56 | pub fn peekBackwardFn(self: *Self, n: usize) ?ItemType { 57 | var count: usize = 0; 58 | var i: usize = 0; 59 | while (count < n) { 60 | if (self.context.peekBackwardFn(i)) |value| { 61 | if (self.filterFn(value)) { 62 | count += 1; 63 | } 64 | i += 1; 65 | } else { 66 | break; 67 | } 68 | } 69 | return self.context.peekBackwardFn(i); 70 | } 71 | 72 | pub fn nextFn(self: *Self) ?ItemType { 73 | while (true) { 74 | if (self.context.nextFn()) |value| { 75 | if (self.filterFn(value)) { 76 | return value; 77 | } 78 | } else { 79 | break; 80 | } 81 | } 82 | return null; 83 | } 84 | 85 | pub fn nextBackFn(self: *Self) ?ItemType { 86 | var i: usize = 0; 87 | while (true) { 88 | if (self.context.nextBackFn()) |value| { 89 | if (self.filterFn(value)) { 90 | return value; 91 | } 92 | i += 1; 93 | } else { 94 | break; 95 | } 96 | } 97 | return null; 98 | } 99 | 100 | pub fn skipFn(self: *Self) bool { 101 | while (self.context.peekAheadFn(0)) |value| { 102 | if (self.filterFn(value)) { 103 | return true; 104 | } else { 105 | self.context.skipFn(); 106 | } 107 | } 108 | return false; 109 | } 110 | 111 | pub fn skipBackFn(self: *Self) bool { 112 | while (self.context.peekBackwardFn(0)) |value| { 113 | if (self.filterFn(value)) { 114 | return true; 115 | } else { 116 | self.context.skipBackFn(); 117 | } 118 | } 119 | return false; 120 | } 121 | 122 | pub fn reverseFn(self: *Self) void { 123 | self.context.reverseFn(); 124 | } 125 | }; 126 | } 127 | 128 | pub fn FilterContext(comptime Context: type, comptime FilterFn: type) type { 129 | comptime { 130 | IterAssert.assertIteratorContext(Context); 131 | } 132 | return struct { 133 | const Self = @This(); 134 | pub const InnerContextType = Context; 135 | pub const ItemType = Context.ItemType; 136 | 137 | filterFn: FilterFn = null, 138 | context: Context, 139 | 140 | pub fn init(context: InnerContextType, filterFn: FilterFn) Self { 141 | return Self{ 142 | .filterFn = filterFn, 143 | .context = context, 144 | }; 145 | } 146 | 147 | /// return 0 if the context does not support 148 | /// size_hint 149 | pub fn size_hint(self: *Self) SizeHint { 150 | if (@hasDecl(InnerContextType, "sizeHintFn")) { 151 | return self.context.sizeHintFn(); 152 | } else { 153 | return 0; 154 | } 155 | } 156 | 157 | /// Look at the nth item without advancing 158 | pub fn peekAheadFn(self: *Self, n: usize) ?ItemType { 159 | var count: usize = 0; 160 | var i: usize = 0; 161 | while (count < n) : (i += 1) { 162 | if (self.context.peekAheadFn(i)) |value| { 163 | if (self.filterFn(value)) { 164 | count += 1; 165 | } 166 | } else { 167 | break; 168 | } 169 | } 170 | return self.context.peekAheadFn(i); 171 | } 172 | 173 | pub fn nextFn(self: *Self) ?ItemType { 174 | while (self.context.nextFn()) |value| { 175 | if (self.filterFn(value)) { 176 | return value; 177 | } 178 | } 179 | return null; 180 | } 181 | 182 | pub fn skipFn(self: *Self) bool { 183 | if (self.nexFn()) |_| { 184 | return true; 185 | } 186 | return false; 187 | } 188 | }; 189 | } 190 | 191 | /// A Filter Iterator constructor 192 | /// It's actually a wrapper over an iterator 193 | pub fn FilterIterator(comptime Context: type, comptime FilterFn: type) type { 194 | if (IterAssert.isDoubleEndedIteratorContext(Context)) { 195 | const FilterContextType = DoubleEndedFilterContext(Context, FilterFn); 196 | return IIterator(FilterContextType); 197 | } else { 198 | const FilterContextType = FilterContext(Context, FilterFn); 199 | return IIterator(FilterContextType); 200 | } 201 | } 202 | 203 | pub fn filter(s: anytype, func: anytype) FilterIterator(SliceIter.SliceContext(GetPtrChildType(@TypeOf(s))), @TypeOf(func)) { 204 | return SliceIter.slice(s).filter(func); 205 | } 206 | 207 | const std = @import("std"); 208 | const debug = std.debug; 209 | const testing = std.testing; 210 | const slice = @import("slice.zig").slice; 211 | 212 | test "test filter with map" { 213 | const ints: []const u32 = &[_]u32{ 1, 2, 3, 4, 5, 6, 7, 8 }; 214 | var iter = slice(ints); 215 | 216 | const S = struct { 217 | pub fn is_even(cur: u32) bool { 218 | return cur % 2 == 0; 219 | } 220 | }.is_even; 221 | var filter_iter = iter.filter(S); 222 | 223 | const Fn = struct { 224 | pub fn toChar(old: u32) bool { 225 | if (@mod(old, 2) == 0) { 226 | return true; 227 | } else { 228 | return false; 229 | } 230 | } 231 | }.toChar; 232 | 233 | var map_iter = filter_iter.map(Fn); 234 | 235 | while (map_iter.next()) |value| { 236 | debug.print("map_iter result {}\n", .{value}); 237 | } 238 | } 239 | 240 | test "test filter method" { 241 | const ints: []const u32 = &[_]u32{ 1, 2, 3, 4, 5, 6, 7, 8 }; 242 | 243 | const S = struct { 244 | pub fn is_even(cur: u32) bool { 245 | return cur % 2 == 0; 246 | } 247 | }.is_even; 248 | var filter_iter = filter(ints, S); 249 | 250 | const Fn = struct { 251 | pub fn toChar(old: u32) bool { 252 | if (@mod(old, 2) == 0) { 253 | return true; 254 | } else { 255 | return false; 256 | } 257 | } 258 | }.toChar; 259 | 260 | var map_iter = filter_iter.map(Fn); 261 | 262 | while (map_iter.next()) |value| { 263 | debug.print("map_iter result {}\n", .{value}); 264 | } 265 | } 266 | 267 | test "test filter method with std" { 268 | var str: []const u8 = "abcd1234"; 269 | 270 | var map_iter = filter(str, std.ascii.isDigit); 271 | 272 | var truth: []const u8 = str[4..]; 273 | var i: usize = 0; 274 | while (map_iter.next()) |value| { 275 | debug.print("{}\n", .{value}); 276 | try testing.expectEqual(truth[i], value); 277 | i += 1; 278 | } 279 | try testing.expectEqual(@as(usize, 4), i); 280 | } 281 | -------------------------------------------------------------------------------- /src/fp.zig: -------------------------------------------------------------------------------- 1 | /// Iterators 2 | pub const Chain = @import("chain.zig").ChainIterator; 3 | pub const EnumerateIterator = @import("enumerate.zig").EnumerateIterator; 4 | pub const FilterIterator = @import("filter.zig").FilterIterator; 5 | pub const FilterMapIterator = @import("filter-map.zig").FilterMapIterator; 6 | pub const IIterator = @import("core/iterator.zig").IIterator; 7 | pub const MapIterator = @import("map.zig").MapIterator; 8 | pub const RangeIterator = @import("range.zig").RangeIterator; 9 | pub const ReverseIterator = @import("reverse.zig").ReverseIterator; 10 | pub const SliceIterator = @import("slice.zig").SliceIterator; 11 | pub const StepIterator = @import("step.zig").StepIterator; 12 | pub const TakeIterator = @import("take.zig").TakeIterator; 13 | pub const TakeWhileIterator = @import("take-while.zig").TakeWhileIterator; 14 | pub const SkipWhileIterator = @import("skip-while.zig").SkipWhileIterator; 15 | pub const FuseIterator = @import("fuse.zig").FuseIterator; 16 | 17 | pub const chain = @import("chain.zig").chain; 18 | pub const enumerate = @import("enumerate.zig").enumerate; 19 | pub const filter = @import("filter.zig").filter; 20 | pub const filterMap = @import("filter-map.zig").filterMap; 21 | pub const map = @import("map.zig").map; 22 | pub const range = @import("range.zig").range; 23 | pub const reverse = @import("reverse.zig").reverse; 24 | pub const slice = @import("slice.zig").slice; 25 | pub const step = @import("step.zig").step; 26 | pub const take = @import("take.zig").take; 27 | pub const takeWhile = @import("take-while.zig").takeWhile; 28 | pub const skipWhile = @import("skip-while.zig").skipWhile; 29 | 30 | /// Monad 31 | pub const Monad = @import("core/monad.zig").Monad; 32 | pub const MonadicOption = @import("core/monad.zig").MonadicOption; 33 | pub const Closure = @import("core/monad.zig").Closure; 34 | -------------------------------------------------------------------------------- /src/fuse.zig: -------------------------------------------------------------------------------- 1 | const IIterator = @import("core/iterator.zig").IIterator; 2 | const SliceIter = @import("slice.zig"); 3 | const SizeHint = @import("core/size-hint.zig").SizeHint; 4 | 5 | const GetPtrChildType = @import("utils.zig").GetPtrChildType; 6 | const IterAssert = @import("utils.zig"); 7 | 8 | /// The Fuse iterator is designed to be lazily evaluated 9 | /// as the the map iterator 10 | /// It's just a wrapper over underlying iterator 11 | pub fn DoubleEndedFuseContext(comptime Context: type) type { 12 | comptime { 13 | IterAssert.assertDoubleEndedIteratorContext(Context); 14 | } 15 | return struct { 16 | const Self = @This(); 17 | pub const InnerContextType = Context; 18 | pub const ItemType = Context.ItemType; 19 | 20 | context: Context, 21 | fused: bool = false, 22 | 23 | pub fn init(context: InnerContextType) Self { 24 | return Self{ 25 | .context = context, 26 | .fused = false, 27 | }; 28 | } 29 | /// return 0 if the context does not support 30 | /// size_hint 31 | pub fn sizeHintFn(self: *Self) SizeHint { 32 | if (self.fused) { 33 | return .{}; 34 | } 35 | if (@hasDecl(InnerContextType, "sizeHintFn")) { 36 | return self.context.sizeHintFn(); 37 | } else { 38 | return .{}; 39 | } 40 | } 41 | 42 | /// Look at the nth item without advancing 43 | pub fn peekAheadFn(self: *Self, n: usize) ?ItemType { 44 | if (self.fused) { 45 | return null; 46 | } 47 | var count: usize = 0; 48 | while (count < n) { 49 | if (self.context.peekAheadFn(count)) |_| { 50 | count += 1; 51 | } else { 52 | return null; 53 | } 54 | } 55 | return self.context.peekAheadFn(n); 56 | } 57 | 58 | pub fn peekBackwardFn(self: *Self, n: usize) ?ItemType { 59 | if (self.fused) { 60 | return null; 61 | } 62 | var count: usize = 0; 63 | while (count < n) { 64 | if (self.context.peekBackwardFn(count)) |_| { 65 | count += 1; 66 | } else { 67 | return null; 68 | } 69 | } 70 | return self.context.peekBackwardFn(n); 71 | } 72 | 73 | pub fn nextFn(self: *Self) ?ItemType { 74 | if (self.fused) { 75 | return null; 76 | } 77 | if (self.context.nextFn()) |value| { 78 | return value; 79 | } 80 | self.fused = true; 81 | return null; 82 | } 83 | 84 | pub fn nextBackFn(self: *Self) ?ItemType { 85 | if (self.fused) { 86 | return null; 87 | } 88 | if (self.context.nextBackFn()) |value| { 89 | return value; 90 | } 91 | self.fused = true; 92 | return null; 93 | } 94 | 95 | pub fn skipFn(self: *Self) bool { 96 | if (self.fused) { 97 | return false; 98 | } 99 | if (self.context.skipFn()) { 100 | return true; 101 | } 102 | self.fused = true; 103 | return false; 104 | } 105 | 106 | pub fn skipBackFn(self: *Self) bool { 107 | if (self.fused) { 108 | return false; 109 | } 110 | if (self.context.skipBackFn()) { 111 | return true; 112 | } 113 | self.fused = true; 114 | return false; 115 | } 116 | 117 | pub fn reverseFn(self: *Self) void { 118 | self.fused = false; 119 | self.context.reverseFn(); 120 | } 121 | }; 122 | } 123 | 124 | pub fn FuseContext(comptime Context: type) type { 125 | comptime { 126 | IterAssert.assertIteratorContext(Context); 127 | } 128 | return struct { 129 | const Self = @This(); 130 | pub const InnerContextType = Context; 131 | pub const ItemType = Context.ItemType; 132 | 133 | context: Context, 134 | fused: bool = false, 135 | 136 | pub fn init(context: InnerContextType) Self { 137 | return Self{ 138 | .context = context, 139 | .fused = false, 140 | }; 141 | } 142 | /// return 0 if the context does not support 143 | /// size_hint 144 | pub fn sizeHintFn(self: *Self) SizeHint { 145 | if (self.fused) { 146 | return .{}; 147 | } 148 | if (@hasDecl(InnerContextType, "sizeHintFn")) { 149 | return self.context.sizeHintFn(); 150 | } else { 151 | return {}; 152 | } 153 | } 154 | 155 | /// Look at the nth item without advancing 156 | pub fn peekAheadFn(self: *Self, n: usize) ?ItemType { 157 | if (self.fused) { 158 | return null; 159 | } 160 | var count: usize = 0; 161 | while (count < n) { 162 | if (self.context.peekAheadFn(count)) |_| { 163 | count += 1; 164 | } else { 165 | return null; 166 | } 167 | } 168 | return self.context.peekAheadFn(n); 169 | } 170 | 171 | pub fn nextFn(self: *Self) ?ItemType { 172 | if (self.fused) { 173 | return null; 174 | } 175 | if (self.context.nextFn()) |value| { 176 | return value; 177 | } 178 | self.fused = true; 179 | return null; 180 | } 181 | 182 | pub fn skipFn(self: *Self) bool { 183 | if (self.fused) { 184 | return false; 185 | } 186 | if (self.context.skipFn()) { 187 | return true; 188 | } 189 | self.fused = true; 190 | return false; 191 | } 192 | }; 193 | } 194 | 195 | /// A Fuse Iterator constructor 196 | /// It's actually a wrapper over an iterator 197 | pub fn FuseIterator(comptime Context: type) type { 198 | if (IterAssert.isDoubleEndedIteratorContext(Context)) { 199 | const FuseContextType = DoubleEndedFuseContext(Context); 200 | return IIterator(FuseContextType); 201 | } else { 202 | const FuseContextType = FuseContext(Context); 203 | return IIterator(FuseContextType); 204 | } 205 | } 206 | 207 | const std = @import("std"); 208 | const debug = std.debug; 209 | const testing = std.testing; 210 | const slice = @import("slice.zig").slice; 211 | 212 | test "test slice fuse" { 213 | const ints: []const u32 = &[_]u32{ 1, 2, 3, 4 }; 214 | var iter = slice(ints); 215 | 216 | var fuse_iter = iter.fuse(); 217 | try IterAssert.testIterator(fuse_iter, ints, 4, .{ 218 | .low = 4, 219 | .high = 4, 220 | }); 221 | } 222 | -------------------------------------------------------------------------------- /src/map-while.zig: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tizee/zig-fp/4ee19bbecd2d6e2b9f38f9e4cfedfc0ea17080f6/src/map-while.zig -------------------------------------------------------------------------------- /src/map.zig: -------------------------------------------------------------------------------- 1 | const IIterator = @import("core/iterator.zig").IIterator; 2 | const SliceIter = @import("slice.zig"); 3 | const IterAssert = @import("utils.zig"); 4 | const GetPtrChildType = @import("utils.zig").GetPtrChildType; 5 | const SizeHint = @import("core/size-hint.zig").SizeHint; 6 | 7 | pub fn DoubleEndedMapContext(comptime Context: type, comptime TransformFn: type) type { 8 | comptime { 9 | IterAssert.assertDoubleEndedIteratorContext(Context); 10 | } 11 | return struct { 12 | const Self = @This(); 13 | pub const InnerContextType = Context; 14 | pub const ItemType = @typeInfo(TransformFn).Fn.return_type.?; 15 | 16 | context: Context, 17 | transfromFn: TransformFn = undefined, 18 | 19 | pub fn init(context: InnerContextType, f: TransformFn) Self { 20 | return Self{ 21 | .context = context, 22 | .transfromFn = f, 23 | }; 24 | } 25 | 26 | /// return 0 if the context does not support 27 | /// size_hint 28 | pub fn sizeHintFn(self: *Self) SizeHint { 29 | if (@hasDecl(InnerContextType, "sizeHintFn")) { 30 | return self.context.sizeHintFn(); 31 | } else { 32 | return .{}; 33 | } 34 | } 35 | 36 | /// Look at the nth item without advancing 37 | pub fn peekAheadFn(self: *Self, n: usize) ?ItemType { 38 | if (self.context.peekAheadFn(n)) |value| { 39 | return self.transfromFn(value); 40 | } 41 | return null; 42 | } 43 | 44 | pub fn peekBackwardFn(self: *Self, n: usize) bool { 45 | if (self.context.peekBackwardFn(n)) |value| { 46 | return self.transfromFn(value); 47 | } 48 | return null; 49 | } 50 | 51 | pub fn nextFn(self: *Self) ?ItemType { 52 | if (self.context.nextFn()) |value| { 53 | return self.transfromFn(value); 54 | } 55 | return null; 56 | } 57 | 58 | pub fn nextBackFn(self: *Self) ?ItemType { 59 | if (self.context.nextBackFn()) |value| { 60 | return self.transfromFn(value); 61 | } 62 | return null; 63 | } 64 | 65 | pub fn skipFn(self: *Self) bool { 66 | return self.context.skipFn(); 67 | } 68 | 69 | pub fn skipBackFn(self: *Self) bool { 70 | return self.context.skipBackFn(); 71 | } 72 | }; 73 | } 74 | 75 | pub fn MapContext(comptime Context: type, comptime TransformFn: type) type { 76 | comptime { 77 | IterAssert.isIteratorContext(Context); 78 | } 79 | return struct { 80 | const Self = @This(); 81 | pub const InnerContextType = Context; 82 | pub const ItemType = @typeInfo(TransformFn).Fn.return_type.?; 83 | 84 | context: Context, 85 | transfromFn: TransformFn = undefined, 86 | 87 | pub fn init(context: InnerContextType) Self { 88 | return Self{ 89 | .context = context, 90 | }; 91 | } 92 | 93 | /// return 0 if the context does not support 94 | /// size_hint 95 | pub fn sizeHintFn(self: *Self) SizeHint { 96 | if (@hasDecl(InnerContextType, "sizeHintFn")) { 97 | return self.context.sizeHintFn(); 98 | } else { 99 | return .{}; 100 | } 101 | } 102 | 103 | /// Look at the nth item without advancing 104 | pub fn peekAheadFn(self: *Self, n: usize) ?ItemType { 105 | if (self.context.peekAheadFn(n)) |value| { 106 | return self.transfromFn(value); 107 | } 108 | return null; 109 | } 110 | 111 | pub fn nextFn(self: *Self) ?ItemType { 112 | if (self.context.nextFn()) |value| { 113 | return self.transfromFn(value); 114 | } 115 | return null; 116 | } 117 | 118 | pub fn skipFn(self: *Self) bool { 119 | return self.context.skipFn(); 120 | } 121 | }; 122 | } 123 | 124 | /// A Map Iterator struct 125 | /// It's actually a wrapper over an iterator 126 | pub fn MapIterator(comptime Context: type, comptime TransformFn: type) type { 127 | if (IterAssert.isDoubleEndedIteratorContext(Context)) { 128 | const MapContextType = DoubleEndedMapContext(Context, TransformFn); 129 | return IIterator(MapContextType); 130 | } else { 131 | const MapContextType = MapContext(Context, TransformFn); 132 | return IIterator(MapContextType); 133 | } 134 | } 135 | 136 | pub fn map(s: anytype, transformFn: anytype) MapIterator(SliceIter.SliceContext(GetPtrChildType(@TypeOf(s))), @TypeOf(transformFn)) { 137 | return SliceIter.slice(s).map(transformFn); 138 | } 139 | 140 | const std = @import("std"); 141 | const debug = std.debug; 142 | const testing = std.testing; 143 | 144 | test "test MapIterator" { 145 | const transform = struct { 146 | pub fn transform(value: u8) bool { 147 | if (value == '1') return true; 148 | return false; 149 | } 150 | }.transform; 151 | 152 | const StrIter = SliceIter.SliceIterator(u8); 153 | 154 | const str: []const u8 = "0011"; 155 | var context = StrIter.IterContext.init(str); 156 | 157 | const Map = MapIterator(StrIter.IterContext, @TypeOf(transform)); 158 | var map_context = Map.IterContext.init(context, transform); 159 | var map_iter = Map.initWithContext(map_context); 160 | 161 | const truth = [_]bool{ false, false, true, true }; 162 | var i: usize = 0; 163 | while (map_iter.next()) |value| { 164 | debug.print("{}\n", .{value}); 165 | try testing.expectEqual(truth[i], value); 166 | i += 1; 167 | } 168 | } 169 | 170 | test "test slice map" { 171 | var str: []const u8 = "0011"; 172 | var slice_iter = SliceIter.slice(str); 173 | 174 | const transform = struct { 175 | pub fn transform(value: u8) bool { 176 | if (value == '1') return true; 177 | return false; 178 | } 179 | }.transform; 180 | 181 | const transform2 = struct { 182 | pub fn transform2(value: bool) u8 { 183 | if (value) return '1'; 184 | return '0'; 185 | } 186 | }.transform2; 187 | var map_iter = slice_iter 188 | .map(transform) 189 | .map(transform2); 190 | 191 | var i: usize = 0; 192 | while (map_iter.next()) |value| { 193 | debug.print("{}\n", .{value}); 194 | try testing.expectEqual(str[i], value); 195 | i += 1; 196 | } 197 | } 198 | 199 | test "test map method" { 200 | const str: []const u8 = "0011"; 201 | 202 | const transform = struct { 203 | pub fn transform(value: u8) bool { 204 | if (value == '1') return true; 205 | return false; 206 | } 207 | }.transform; 208 | 209 | const transform2 = struct { 210 | pub fn transform2(value: bool) u8 { 211 | if (value) return '1'; 212 | return '0'; 213 | } 214 | }.transform2; 215 | var map_iter = map(str, transform) 216 | .map(transform2); 217 | 218 | var i: usize = 0; 219 | while (map_iter.next()) |value| { 220 | debug.print("{}\n", .{value}); 221 | try testing.expectEqual(str[i], value); 222 | i += 1; 223 | } 224 | } 225 | -------------------------------------------------------------------------------- /src/range.zig: -------------------------------------------------------------------------------- 1 | const math = @import("std").math; 2 | const IIterator = @import("core/iterator.zig").IIterator; 3 | const SizeHint = @import("core/size-hint.zig").SizeHint; 4 | 5 | const IterAssert = @import("utils.zig"); 6 | /// A half open Range for integer types 7 | pub fn RangeContext(comptime T: type) type { 8 | comptime { 9 | IterAssert.assertNum(T); 10 | } 11 | 12 | return struct { 13 | const Self = @This(); 14 | 15 | pub const ItemType = T; 16 | 17 | direction: bool, 18 | // current value 19 | current: ItemType, 20 | // start value 21 | start: ItemType, 22 | // sentinal end value 23 | end: ItemType, 24 | // step size 25 | step: ItemType, 26 | 27 | pub fn sizeHintFn(self: *Self) SizeHint { 28 | const len = math.sub(ItemType, self.end, self.start) catch 0; 29 | const steps = math.divCeil(ItemType, len, self.step); 30 | 31 | return SizeHint{ 32 | .low = steps, 33 | .high = steps, 34 | }; 35 | } 36 | 37 | /// Look at the nth item without advancing 38 | pub fn peekAheadFn(self: *Self, comptime n: usize) ?ItemType { 39 | var current = self.current; 40 | var i: usize = 0; 41 | while (i < n) { 42 | current += self.step; 43 | i += 1; 44 | } 45 | if (current == self.end) return null; 46 | if (self.step < 0 and current < self.end) { 47 | return null; 48 | } else if (self.step > 0 and current > self.end) { 49 | return null; 50 | } 51 | return current; 52 | } 53 | 54 | pub fn peekBackwardFn(self: *Self, comptime n: usize) ?ItemType { 55 | var current = self.current; 56 | var i: usize = 0; 57 | // TODO should panic for unsigned data 58 | while (i < n) { 59 | current -= self.step; 60 | i += 1; 61 | } 62 | if (current == self.end) return null; 63 | if (self.step < 0 and current > self.end) { 64 | return null; 65 | } else if (self.step > 0 and current < self.start) { 66 | return null; 67 | } 68 | return current; 69 | } 70 | 71 | /// Advances the iterator by step and return the item 72 | pub fn nextFn(self: *Self) ?ItemType { 73 | if (self.step < 0 and self.current <= self.end) { 74 | return null; 75 | } 76 | if (self.step > 0 and self.current >= self.end) { 77 | return null; 78 | } 79 | defer self.current += self.step; 80 | return self.current; 81 | } 82 | 83 | pub fn nextBackFn(self: *Self) ?ItemType { 84 | var current = self.current; 85 | if (self.step < 0) { 86 | if (self.current >= self.start) { 87 | return null; 88 | } 89 | } else { 90 | if (self.current <= self.start) { 91 | return null; 92 | } 93 | } 94 | defer self.current -= self.step; 95 | return current; 96 | } 97 | 98 | /// Advances the iterator by step and return the 99 | /// state whether it succeeds 100 | pub fn skipFn(self: *Self) bool { 101 | if (self.current == self.end) return false; 102 | if (self.step < 0 and self.current < self.end) { 103 | return false; 104 | } else if (self.step > 0 and self.current > self.end) { 105 | return false; 106 | } 107 | 108 | self.current += self.step; 109 | return true; 110 | } 111 | 112 | pub fn skipBackFn(self: *Self) bool { 113 | if (self.current == self.start) return false; 114 | if (self.step < 0 and self.current >= self.start) { 115 | return false; 116 | } else if (self.step > 0 and self.current <= self.start) { 117 | return false; 118 | } 119 | 120 | self.current -= self.step; 121 | return true; 122 | } 123 | 124 | /// reverse a range means 125 | /// This would reset the state of the context 126 | /// set the current to the end 127 | pub fn reverseFn(self: *Self) void { 128 | self.direction = !self.direction; 129 | if (!self.direction) { 130 | self.current = self.end; 131 | } else { 132 | self.current = self.start; 133 | } 134 | } 135 | 136 | pub fn init(start: ItemType, end: ItemType, step: ItemType) Self { 137 | return Self{ .direction = true, .start = start, .current = start, .end = end, .step = step }; 138 | } 139 | }; 140 | } 141 | 142 | pub fn RangeIterator(comptime T: type) type { 143 | const RangeContextType = RangeContext(T); 144 | return IIterator(RangeContextType); 145 | } 146 | 147 | /// An easy-to-use API 148 | pub fn range(comptime T: type, start: T, end: T, step: T) RangeIterator(T) { 149 | const RangeType = RangeIterator(T); 150 | var context = RangeType.IterContext.init(start, end, step); 151 | return RangeType.initWithContext(context); 152 | } 153 | 154 | const std = @import("std"); 155 | const testing = std.testing; 156 | const debug = std.debug; 157 | 158 | fn testRange(comptime T: type) anyerror!void { 159 | const Range = RangeIterator(T); 160 | var iter = range(T, 1, 10, 1); 161 | 162 | var val: T = 1; 163 | while (iter.next()) |value| { 164 | try testing.expectEqual(val, value); 165 | val += 1; 166 | } 167 | try testing.expectEqual(@as(?T, null), iter.peek()); 168 | 169 | comptime { 170 | if (T == i32 or T == i8 or T == i16 or T == i64 or T == i128 or T == c_short or T == c_int or T == c_long or T == c_longlong or T == c_longdouble or T == f16 or T == f32 or T == f64 or T == f80 or T == f128) { 171 | var context1 = Range.IterContext.init(10, 1, -1); 172 | var iter1 = Range.initWithContext(context1); 173 | var val1 = 10; 174 | while (iter1.next()) |value| { 175 | try testing.expectEqual(val1, value); 176 | val1 -= 1; 177 | } 178 | } 179 | } 180 | 181 | iter = range(T, 1, 4, 1); 182 | try testing.expectEqual(@as(?T, 1), iter.next()); 183 | if (iter.skip()) { 184 | if (iter.next()) |value| { 185 | try testing.expectEqual(@as(T, 3), value); 186 | } 187 | if (iter.next()) |value| { 188 | try testing.expectEqual(@as(T, 4), value); 189 | } 190 | try testing.expectEqual(@as(?T, null), iter.next()); 191 | } else { 192 | unreachable; 193 | } 194 | } 195 | 196 | test "Test Range for primitives" { 197 | try testRange(u8); 198 | try testRange(i8); 199 | try testRange(u16); 200 | try testRange(i16); 201 | try testRange(u32); 202 | try testRange(i32); 203 | try testRange(u64); 204 | try testRange(i64); 205 | try testRange(u128); 206 | try testRange(i128); 207 | try testRange(usize); 208 | try testRange(isize); 209 | try testRange(c_ushort); 210 | try testRange(c_short); 211 | try testRange(c_ulong); 212 | try testRange(c_long); 213 | try testRange(c_ulonglong); 214 | try testRange(c_longlong); 215 | try testRange(c_longdouble); 216 | try testRange(f16); 217 | try testRange(f32); 218 | try testRange(f64); 219 | try testRange(f80); 220 | try testRange(f128); 221 | } 222 | -------------------------------------------------------------------------------- /src/reverse.zig: -------------------------------------------------------------------------------- 1 | const IIterator = @import("core/iterator.zig").IIterator; 2 | const SliceIter = @import("slice.zig"); 3 | const SizeHint = @import("core/size-hint.zig").SizeHint; 4 | 5 | const GetPtrChildType = @import("utils.zig").GetPtrChildType; 6 | const IterAssert = @import("utils.zig"); 7 | 8 | // ReverseIterator's context should be a wrapper over its inner context 9 | pub fn ReverseContext(comptime Context: type) type { 10 | comptime { 11 | IterAssert.assertDoubleEndedIteratorContext(Context); 12 | } 13 | 14 | return struct { 15 | const Self = @This(); 16 | pub const InnerContextType = Context; 17 | pub const ItemType = Context.ItemType; 18 | 19 | context: Context, 20 | 21 | pub fn init(context: Context) Self { 22 | return Self{ 23 | .context = context, 24 | }; 25 | } 26 | 27 | /// return 0 if the context does not support 28 | /// size_hint 29 | pub fn sizeHintFn(self: *Self) SizeHint { 30 | if (@hasDecl(InnerContextType, "sizeHintFn")) { 31 | return self.context.sizeHintFn(); 32 | } else { 33 | return .{}; 34 | } 35 | } 36 | 37 | /// Look at the nth item without advancing 38 | pub fn peekAheadFn(self: *Self, n: usize) ?ItemType { 39 | return self.context.peekAheadFn(n); 40 | } 41 | 42 | pub fn peekBackwardFn(self: *Self, n: usize) bool { 43 | return self.context.peekBackwardFn(n); 44 | } 45 | 46 | pub fn nextFn(self: *Self) ?ItemType { 47 | return self.context.nextBackFn(); 48 | } 49 | 50 | pub fn nextBackFn(self: *Self) ?ItemType { 51 | return self.context.nextFn(); 52 | } 53 | 54 | pub fn skipFn(self: *Self) bool { 55 | return self.context.skipFn(); 56 | } 57 | 58 | pub fn skipBackFn(self: *Self) bool { 59 | return self.context.skipBackFn(); 60 | } 61 | 62 | pub fn reverseFn(self: *Self) void { 63 | self.context.reverseFn(); 64 | } 65 | }; 66 | } 67 | 68 | // An Reverse Iterator for a slice 69 | // Iter should be a double-ended iterator 70 | pub fn ReverseIterator(comptime Context: type) type { 71 | const ReverseContextType = ReverseContext(Context); 72 | return IIterator(ReverseContextType); 73 | } 74 | 75 | pub fn reverse(s: anytype) ReverseIterator(SliceIter.SliceContext(GetPtrChildType(@TypeOf(s)))) { 76 | return SliceIter.slice(s).reverse(); 77 | } 78 | 79 | const std = @import("std"); 80 | const testing = std.testing; 81 | const debug = std.debug; 82 | 83 | test "Test Reversed for Int" { 84 | const ints: []const u32 = &[_]u32{ 1, 2, 3, 4 }; 85 | 86 | var iter = SliceIter.slice(ints); 87 | 88 | var reversed_iter = iter.reverse(); 89 | 90 | var i: usize = ints.len; 91 | while (reversed_iter.next()) |value| { 92 | i -= 1; 93 | debug.print("{} {}\n", .{ ints[i], value }); 94 | try testing.expectEqual(ints[i], value); 95 | } 96 | } 97 | 98 | test "Test Reversed for string" { 99 | const Point = struct { 100 | x: i32, 101 | y: i32, 102 | }; 103 | 104 | const points: []const Point = &[_]Point{ 105 | .{ 106 | .x = 1, 107 | .y = 1, 108 | }, 109 | .{ 110 | .x = 2, 111 | .y = 2, 112 | }, 113 | .{ 114 | .x = 3, 115 | .y = 3, 116 | }, 117 | .{ 118 | .x = 4, 119 | .y = 4, 120 | }, 121 | }; 122 | var iter = SliceIter.slice(points); 123 | 124 | var reversed_iter = iter.reverse(); 125 | 126 | var i: usize = points.len; 127 | while (reversed_iter.next()) |value| { 128 | i -= 1; 129 | debug.print("{}\n", .{value}); 130 | try testing.expectEqual(points[i], value); 131 | } 132 | } 133 | 134 | test "test reverse" { 135 | const ints: []const u32 = &[_]u32{ 1, 2, 3, 4 }; 136 | 137 | var reversed_iter = reverse(ints); 138 | 139 | var i: usize = ints.len; 140 | while (reversed_iter.next()) |value| { 141 | i -= 1; 142 | debug.print("{} {}\n", .{ ints[i], value }); 143 | try testing.expectEqual(ints[i], value); 144 | } 145 | try testing.expectEqual(@as(usize, 0), i); 146 | try testing.expectEqual(@as(?u32, null), reversed_iter.next()); 147 | } 148 | 149 | test "test reverse reverse" { 150 | const ints: []const u32 = &[_]u32{ 1, 2, 3, 4 }; 151 | 152 | var reversed_iter = reverse(ints) 153 | .reverse() 154 | .reverse() 155 | .reverse(); 156 | 157 | var i: usize = 0; 158 | while (reversed_iter.next()) |value| : (i += 1) { 159 | debug.print("{} {}\n", .{ ints[i], value }); 160 | try testing.expectEqual(ints[i], value); 161 | } 162 | try testing.expectEqual(@as(usize, 4), i); 163 | try testing.expectEqual(@as(?u32, null), reversed_iter.next()); 164 | } 165 | -------------------------------------------------------------------------------- /src/skip-while.zig: -------------------------------------------------------------------------------- 1 | const IIterator = @import("core/iterator.zig").IIterator; 2 | const SliceIter = @import("slice.zig"); 3 | const SizeHint = @import("core/size-hint.zig").SizeHint; 4 | 5 | const GetPtrChildType = @import("utils.zig").GetPtrChildType; 6 | const IterAssert = @import("utils.zig"); 7 | 8 | /// The SkipWhile iterator is designed to be lazily evaluated 9 | /// as the the map iterator 10 | /// It's just a wrapper over underlying iterator 11 | pub fn DoubleEndedSkipWhileContext(comptime Context: type, comptime FilterFn: type) type { 12 | comptime { 13 | IterAssert.assertDoubleEndedIteratorContext(Context); 14 | } 15 | return struct { 16 | const Self = @This(); 17 | pub const InnerContextType = Context; 18 | pub const ItemType = Context.ItemType; 19 | 20 | filterFn: FilterFn, 21 | fused: bool = false, 22 | context: Context, 23 | 24 | pub fn init(context: InnerContextType, filterFn: FilterFn) Self { 25 | return Self{ 26 | .filterFn = filterFn, 27 | .context = context, 28 | .fused = false, 29 | }; 30 | } 31 | /// return 0 if the context does not support 32 | /// size_hint 33 | pub fn sizeHintFn(self: *Self) SizeHint { 34 | _ = self; 35 | return .{}; 36 | } 37 | 38 | /// Look at the nth item without advancing 39 | pub fn peekAheadFn(self: *Self, n: usize) ?ItemType { 40 | var count: usize = 0; 41 | var i: usize = 0; 42 | while (count < n) { 43 | if (self.context.peekAheadFn(i)) |value| { 44 | if (self.filterFn(value)) { 45 | return null; 46 | } else { 47 | count += 1; 48 | } 49 | i += 1; 50 | } else { 51 | return null; 52 | } 53 | } 54 | return self.context.peekAheadFn(i); 55 | } 56 | 57 | pub fn peekBackwardFn(self: *Self, n: usize) ?ItemType { 58 | var count: usize = 0; 59 | var i: usize = 0; 60 | while (count < n) { 61 | if (self.context.peekBackwardFn(i)) |value| { 62 | if (self.filterFn(value)) { 63 | return null; 64 | } else { 65 | count += 1; 66 | } 67 | i += 1; 68 | } else { 69 | return null; 70 | } 71 | } 72 | return self.context.peekBackwardFn(i); 73 | } 74 | 75 | pub fn nextFn(self: *Self) ?ItemType { 76 | if (self.fused) { 77 | return self.context.nextFn(); 78 | } 79 | while (self.context.nextFn()) |value| { 80 | if (self.filterFn(value)) { 81 | continue; 82 | } else { 83 | self.fused = true; 84 | return value; 85 | } 86 | } 87 | return null; 88 | } 89 | 90 | pub fn nextBackFn(self: *Self) ?ItemType { 91 | if (self.fused) { 92 | return self.context.nextBackFn(); 93 | } 94 | while (self.context.nextBackFn()) |value| { 95 | if (self.filterFn(value)) { 96 | continue; 97 | } else { 98 | self.fused = true; 99 | return value; 100 | } 101 | } 102 | return null; 103 | } 104 | 105 | pub fn skipFn(self: *Self) bool { 106 | if (self.fused) { 107 | return self.context.skipFn(); 108 | } 109 | while (self.context.skipFn()) |value| { 110 | if (self.filterFn(value)) { 111 | continue; 112 | } else { 113 | self.fused = true; 114 | return true; 115 | } 116 | } 117 | return false; 118 | } 119 | 120 | pub fn skipBackFn(self: *Self) bool { 121 | if (self.fused) { 122 | return self.context.skipBackFn(); 123 | } 124 | while (self.context.skipBackFn()) |value| { 125 | if (self.filterFn(value)) { 126 | continue; 127 | } else { 128 | self.fused = true; 129 | return true; 130 | } 131 | } 132 | return false; 133 | } 134 | 135 | pub fn reverseFn(self: *Self) void { 136 | self.fused = false; 137 | self.context.reverseFn(); 138 | } 139 | }; 140 | } 141 | 142 | pub fn SkipWhileContext(comptime Context: type, comptime FilterFn: type) type { 143 | comptime { 144 | IterAssert.assertIteratorContext(Context); 145 | } 146 | return struct { 147 | const Self = @This(); 148 | pub const InnerContextType = Context; 149 | pub const ItemType = Context.ItemType; 150 | 151 | filterFn: FilterFn, 152 | fused: bool = false, 153 | context: Context, 154 | 155 | pub fn init(context: InnerContextType, filterFn: FilterFn) Self { 156 | return Self{ 157 | .filterFn = filterFn, 158 | .context = context, 159 | .fused = false, 160 | }; 161 | } 162 | /// return 0 if the context does not support 163 | /// size_hint 164 | pub fn sizeHintFn(self: *Self) SizeHint { 165 | _ = self; 166 | return .{}; 167 | } 168 | 169 | /// Look at the nth item without advancing 170 | pub fn peekAheadFn(self: *Self, n: usize) ?ItemType { 171 | var count: usize = 0; 172 | var i: usize = 0; 173 | while (count < n) { 174 | if (self.context.peekAheadFn(i)) |value| { 175 | if (self.filterFn(value)) { 176 | return null; 177 | } else { 178 | count += 1; 179 | } 180 | i += 1; 181 | } else { 182 | return null; 183 | } 184 | } 185 | return self.context.peekAheadFn(i); 186 | } 187 | 188 | pub fn nextFn(self: *Self) ?ItemType { 189 | if (self.fused) { 190 | return self.context.nextFn(); 191 | } 192 | while (self.context.nextFn()) |value| { 193 | if (self.filterFn(value)) { 194 | continue; 195 | } else { 196 | self.fused = true; 197 | return value; 198 | } 199 | } 200 | return null; 201 | } 202 | 203 | pub fn skipFn(self: *Self) bool { 204 | if (self.fused) { 205 | return self.context.skipFn(); 206 | } 207 | while (self.context.skipFn()) |value| { 208 | if (self.filterFn(value)) { 209 | continue; 210 | } else { 211 | self.fused = true; 212 | return true; 213 | } 214 | } 215 | return false; 216 | } 217 | }; 218 | } 219 | 220 | /// A SkipWhile Iterator constructor 221 | /// It's actually a wrapper over an iterator 222 | pub fn SkipWhileIterator(comptime Context: type, comptime FilterFn: type) type { 223 | if (IterAssert.isDoubleEndedIteratorContext(Context)) { 224 | const SkipWhileContextType = DoubleEndedSkipWhileContext(Context, FilterFn); 225 | return IIterator(SkipWhileContextType); 226 | } else { 227 | const SkipWhileContextType = SkipWhileContext(Context, FilterFn); 228 | return IIterator(SkipWhileContextType); 229 | } 230 | } 231 | 232 | pub fn skipWhile(s: anytype, func: anytype) SkipWhileIterator(SliceIter.SliceContext(GetPtrChildType(@TypeOf(s))), @TypeOf(func)) { 233 | return SliceIter.slice(s).skip_while(func); 234 | } 235 | 236 | const std = @import("std"); 237 | const debug = std.debug; 238 | const testing = std.testing; 239 | const slice = @import("slice.zig").slice; 240 | 241 | test "test SkipWhile method" { 242 | const ints: []const u32 = &[_]u32{ 2, 4, 6, 8, 1, 3, 5, 7 }; 243 | const truths: []const u32 = &[_]u32{ 1, 3, 5, 7 }; 244 | 245 | const S = struct { 246 | pub fn is_even(cur: u32) bool { 247 | return cur % 2 == 0; 248 | } 249 | }.is_even; 250 | var skip_while_iter = skipWhile(ints, S); 251 | try IterAssert.testIterator(skip_while_iter, truths, 4, .{}); 252 | } 253 | 254 | test "test SkipWhile method with std" { 255 | var str: []const u8 = "1234abcd"; 256 | var truths: []const u8 = "abcd"; 257 | 258 | var skip_while_iter = skipWhile(str, std.ascii.isDigit); 259 | 260 | try IterAssert.testIterator(skip_while_iter, truths, 4, .{}); 261 | } 262 | -------------------------------------------------------------------------------- /src/slice.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const math = std.math; 3 | const IIterator = @import("core/iterator.zig").IIterator; 4 | const SizeHint = @import("core/size-hint.zig").SizeHint; 5 | 6 | const GetPtrChildType = @import("utils.zig").GetPtrChildType; 7 | const assertSlice = @import("utils.zig").assertSlice; 8 | 9 | /// A thin wrapper over a slice 10 | /// It's actually a double-ended iterator context 11 | pub fn SliceContext(comptime T: type) type { 12 | return struct { 13 | const Self = @This(); 14 | pub const ItemType = T; 15 | direction: bool, 16 | 17 | current: usize, 18 | len: usize, 19 | 20 | data: []const ItemType, 21 | 22 | pub fn sizeHintFn(self: Self) SizeHint { 23 | if (self.direction) { 24 | const len = math.sub(usize, self.len, self.current) catch 0; 25 | return SizeHint{ 26 | .low = len, 27 | .high = len, 28 | }; 29 | } else { 30 | return SizeHint{ 31 | .low = self.current, 32 | .high = self.current, 33 | }; 34 | } 35 | } 36 | 37 | /// Advances the iterator by n*step and return the item 38 | pub fn peekAheadFn(self: *Self, n: usize) ?ItemType { 39 | var current = self.current + n; 40 | if (current < self.len) { 41 | return self.data[current]; 42 | } 43 | return null; 44 | } 45 | 46 | pub fn peekBackwardFn(self: *Self, n: usize) ?ItemType { 47 | if (self.current > n and (self.current - n) < self.len) { 48 | return self.data[self.current - n]; 49 | } 50 | return null; 51 | } 52 | 53 | pub fn nextFn(self: *Self) ?ItemType { 54 | if (self.current == self.len) return null; 55 | defer self.current += 1; 56 | return self.data[self.current]; 57 | } 58 | 59 | pub fn nextBackFn(self: *Self) ?ItemType { 60 | if (self.current == 0) return null; 61 | self.current -= 1; 62 | return self.data[self.current]; 63 | } 64 | 65 | pub fn skipBackFn(self: *Self) bool { 66 | if (self.current == 0) { 67 | return false; 68 | } else { 69 | self.current -= 1; 70 | } 71 | return true; 72 | } 73 | 74 | pub fn skipFn(self: *Self) bool { 75 | if (self.current >= self.len) { 76 | // reach last 77 | return false; 78 | } else { 79 | self.current += 1; 80 | } 81 | return true; 82 | } 83 | 84 | /// This would reset the state of the context 85 | /// If the iterator has been exhausted then it's 86 | /// a no-op 87 | pub fn reverseFn(self: *Self) void { 88 | self.direction = !self.direction; 89 | if (self.direction) { 90 | self.current = 0; 91 | } else { 92 | self.current = self.len; 93 | } 94 | } 95 | 96 | pub fn init(data: anytype) Self { 97 | return Self{ .direction = true, .len = data.len, .current = @as(usize, 0), .data = data }; 98 | } 99 | }; 100 | } 101 | 102 | pub fn SliceIterator(comptime T: type) type { 103 | const SliceContextType = SliceContext(T); 104 | return IIterator(SliceContextType); 105 | } 106 | 107 | /// var iter = slice(u8,"abcd"); 108 | pub fn slice(s: anytype) SliceIterator(GetPtrChildType(@TypeOf(s))) { 109 | comptime { 110 | assertSlice(@TypeOf(s)); 111 | } 112 | const SliceIteratorType = SliceIterator(GetPtrChildType(@TypeOf(s))); 113 | var context = SliceIteratorType.IterContext.init(s); 114 | return SliceIteratorType.initWithContext(context); 115 | } 116 | 117 | const testing = std.testing; 118 | const testIterator = @import("utils.zig").testIterator; 119 | const debug = std.debug; 120 | 121 | test "test SliceIterator" { 122 | const StrIter = SliceIterator(u8); 123 | const str = "abcd"; 124 | var context = StrIter.IterContext.init(str); 125 | var iter = StrIter.initWithContext(context); 126 | 127 | var i: usize = 0; 128 | while (iter.next()) |value| { 129 | try testing.expectEqual(str[i], value); 130 | i += 1; 131 | debug.print("{}\n", .{value}); 132 | } 133 | context = StrIter.IterContext.init(str); 134 | iter = StrIter.initWithContext(context); 135 | 136 | try testing.expectEqual(@as(?u8, str[0]), iter.peek()); 137 | _ = iter.next(); 138 | _ = iter.next(); 139 | try testing.expectEqual(@as(?u8, str[2]), iter.peek()); 140 | } 141 | 142 | test "test slice" { 143 | const str: []const u8 = "abcd"; 144 | var iter = slice(str); 145 | 146 | var i: usize = 0; 147 | while (iter.next()) |value| { 148 | try testing.expectEqual(str[i], value); 149 | i += 1; 150 | debug.print("{}\n", .{value}); 151 | } 152 | } 153 | 154 | test "test slice size_hint" { 155 | const str: []const u8 = "abcd"; 156 | var iter = slice(str); 157 | try testIterator(iter, str, 4, SizeHint{ 158 | .low = 4, 159 | .high = 4, 160 | }); 161 | } 162 | 163 | test "test slice ducking types" { 164 | const StrIter = SliceIterator(u8); 165 | const str: []const u8 = "abcd"; 166 | 167 | var context = StrIter.IterContext.init(str); 168 | var iter = StrIter.initWithContext(context); 169 | 170 | var iter2 = slice(str); 171 | 172 | try testing.expect(@TypeOf(iter2) == @TypeOf(iter)); 173 | } 174 | -------------------------------------------------------------------------------- /src/step.zig: -------------------------------------------------------------------------------- 1 | const math = @import("std").math; 2 | const IIterator = @import("core/iterator.zig").IIterator; 3 | const SliceIter = @import("slice.zig"); 4 | const SizeHint = @import("core/size-hint.zig").SizeHint; 5 | 6 | const GetPtrChildType = @import("utils.zig").GetPtrChildType; 7 | const IterAssert = @import("utils.zig"); 8 | 9 | /// The Step iterator is designed to be lazily evaluated 10 | /// as the the map iterator 11 | /// It's just a wrapper over underlying iterator 12 | pub fn DoubleEndedStepContext(comptime Context: type) type { 13 | comptime { 14 | IterAssert.assertDoubleEndedIteratorContext(Context); 15 | } 16 | return struct { 17 | const Self = @This(); 18 | pub const InnerContextType = Context; 19 | pub const ItemType = Context.ItemType; 20 | 21 | context: Context = undefined, 22 | start: bool = false, 23 | step: usize = 1, 24 | 25 | pub fn init(context: InnerContextType, init_state: usize) Self { 26 | if (init_state == 0) { 27 | @panic("StepIterator should use a positive and non-zero step"); 28 | } 29 | return Self{ 30 | .context = context, 31 | .step = init_state, 32 | .start = false, 33 | }; 34 | } 35 | /// return 0 if the context does not support 36 | /// size_hint 37 | pub fn sizeHintFn(self: *Self) SizeHint { 38 | if (@hasDecl(InnerContextType, "sizeHintFn")) { 39 | if (self.context.sizeHintFn().len()) |value| { 40 | const steps = math.divCeil(usize, value, self.step) catch 0; 41 | return SizeHint{ 42 | .low = steps, 43 | .high = steps, 44 | }; 45 | } 46 | return .{}; 47 | } else { 48 | return .{}; 49 | } 50 | } 51 | 52 | /// Look at the nth item without advancing 53 | /// index = n * step 54 | pub fn peekAheadFn(self: *Self, n: usize) ?ItemType { 55 | return self.context.peekAheadFn(n * self.step); 56 | } 57 | 58 | pub fn peekBackwardFn(self: *Self, n: usize) ?ItemType { 59 | return self.context.peekBackwardFn(n * self.step); 60 | } 61 | 62 | /// set a counter for the step 63 | /// always start from the first item 64 | pub fn nextFn(self: *Self) ?ItemType { 65 | if (!self.start) { 66 | self.start = true; 67 | return self.context.nextFn(); 68 | } 69 | var i: usize = 0; 70 | while (i < self.step - 1) : (i += 1) { 71 | if (!self.context.skipFn()) { 72 | return null; 73 | } 74 | } 75 | return self.context.nextFn(); 76 | } 77 | 78 | pub fn nextBackFn(self: *Self) ?ItemType { 79 | if (!self.start) { 80 | self.start = true; 81 | return self.context.nextBackFn(); 82 | } 83 | var i: usize = 0; 84 | while (i < self.step - 1) : (i += 1) { 85 | if (!self.context.skipBackFn()) { 86 | return null; 87 | } 88 | } 89 | return self.context.nextBackFn(); 90 | } 91 | 92 | pub fn skipFn(self: *Self) bool { 93 | if (!self.start) { 94 | self.start = true; 95 | return self.context.skipFn(); 96 | } 97 | var i: usize = 0; 98 | while (i < self.step - 1) : (i += 1) { 99 | if (!self.context.skipFn()) { 100 | return null; 101 | } 102 | } 103 | return self.context.skipFn(); 104 | } 105 | 106 | pub fn skipBackFn(self: *Self) bool { 107 | if (!self.start) { 108 | self.start = true; 109 | return self.context.skipBackFn(); 110 | } 111 | var i: usize = 0; 112 | while (i < self.step - 1) : (i += 1) { 113 | if (!self.context.skipBackFn()) { 114 | return null; 115 | } 116 | } 117 | return self.context.skipBackFn(); 118 | } 119 | 120 | pub fn reverseFn(self: *Self) void { 121 | self.context.reverseFn(); 122 | } 123 | }; 124 | } 125 | 126 | pub fn StepContext(comptime Context: type) type { 127 | comptime { 128 | IterAssert.assertIteratorContext(Context); 129 | } 130 | return struct { 131 | const Self = @This(); 132 | pub const InnerContextType = Context; 133 | pub const ItemType = Context.ItemType; 134 | 135 | context: Context = undefined, 136 | start: bool = false, 137 | step: usize = 1, 138 | 139 | pub fn init(context: InnerContextType, init_state: usize) Self { 140 | if (init_state == 0) { 141 | @panic("StepIterator should use a positive and non-zero step"); 142 | } 143 | return Self{ 144 | .context = context, 145 | .step = init_state, 146 | .start = false, 147 | }; 148 | } 149 | /// return 0 if the context does not support 150 | /// size_hint 151 | pub fn sizeHintFn(self: *Self) SizeHint { 152 | if (@hasDecl(InnerContextType, "sizeHintFn")) { 153 | if (self.context.sizeHintFn().len()) |value| { 154 | const steps = math.divCeil(usize, value, self.step); 155 | return .{ 156 | .low = steps, 157 | .high = steps, 158 | }; 159 | } 160 | } else { 161 | return {}; 162 | } 163 | } 164 | 165 | /// Look at the nth item without advancing 166 | /// index = n * step 167 | pub fn peekAheadFn(self: *Self, n: usize) ?ItemType { 168 | return self.context.peekAheadFn(n * self.step); 169 | } 170 | 171 | /// set a counter for the step 172 | /// always start from the first item 173 | pub fn nextFn(self: *Self) ?ItemType { 174 | if (!self.start) { 175 | self.start = true; 176 | return self.context.nextFn(); 177 | } 178 | var i: usize = 0; 179 | while (i < self.step - 1) : (i += 1) { 180 | if (!self.context.skipFn()) { 181 | return null; 182 | } 183 | } 184 | return self.context.nextFn(); 185 | } 186 | 187 | pub fn nextBackFn(self: *Self) ?ItemType { 188 | if (!self.start) { 189 | self.start = true; 190 | return self.context.nextBackFn(); 191 | } 192 | var i: usize = 0; 193 | while (i < self.step - 1) : (i += 1) { 194 | if (!self.context.skipBackFn()) { 195 | return null; 196 | } 197 | } 198 | return self.context.nextBackFn(); 199 | } 200 | 201 | pub fn skipFn(self: *Self) bool { 202 | if (!self.start) { 203 | self.start = true; 204 | return self.context.skipFn(); 205 | } 206 | var i: usize = 0; 207 | while (i < self.step - 1) : (i += 1) { 208 | if (!self.context.skipFn()) { 209 | return null; 210 | } 211 | } 212 | return self.context.skipFn(); 213 | } 214 | 215 | pub fn skipBackFn(self: *Self) bool { 216 | if (!self.start) { 217 | self.start = true; 218 | return self.context.skipBackFn(); 219 | } 220 | var i: usize = 0; 221 | while (i < self.step - 1) : (i += 1) { 222 | if (!self.context.skipBackFn()) { 223 | return null; 224 | } 225 | } 226 | return self.context.skipBackFn(); 227 | } 228 | }; 229 | } 230 | 231 | /// A Step Iterator constructor 232 | /// It's actually a wrapper over an iterator 233 | pub fn StepIterator(comptime Context: type) type { 234 | if (IterAssert.isDoubleEndedIteratorContext(Context)) { 235 | const StepContextType = DoubleEndedStepContext(Context); 236 | return IIterator(StepContextType); 237 | } else { 238 | const StepContextType = StepContext(Context); 239 | return IIterator(StepContextType); 240 | } 241 | } 242 | 243 | pub fn step(s: anytype, state: usize) StepIterator(SliceIter.SliceContext(GetPtrChildType(@TypeOf(s)))) { 244 | return SliceIter.slice(s).step(state); 245 | } 246 | 247 | const std = @import("std"); 248 | const debug = std.debug; 249 | const testing = std.testing; 250 | const slice = @import("slice.zig").slice; 251 | 252 | test "test step method" { 253 | const ints: []const u32 = &[_]u32{ 1, 2, 3, 4, 5, 6, 7, 8 }; 254 | const truth: []const u32 = &[_]u32{ 1, 3, 5, 7 }; 255 | 256 | var step_iter = step(ints, 2); 257 | try IterAssert.testIterator(step_iter, truth, 4, .{ 258 | .low = 4, 259 | .high = 4, 260 | }); 261 | } 262 | 263 | test "test slice step" { 264 | const ints: []const u32 = &[_]u32{ 1, 2, 3, 4, 5, 6, 7, 8 }; 265 | const truth: []const u32 = &[_]u32{ 1, 3, 5, 7 }; 266 | 267 | var step_iter = SliceIter.slice(ints).step(2); 268 | 269 | try IterAssert.testIterator(step_iter, truth, 4, .{ 270 | .low = 4, 271 | .high = 4, 272 | }); 273 | } 274 | -------------------------------------------------------------------------------- /src/take-while.zig: -------------------------------------------------------------------------------- 1 | const IIterator = @import("core/iterator.zig").IIterator; 2 | const SliceIter = @import("slice.zig"); 3 | const SizeHint = @import("core/size-hint.zig").SizeHint; 4 | 5 | const GetPtrChildType = @import("utils.zig").GetPtrChildType; 6 | const IterAssert = @import("utils.zig"); 7 | 8 | /// The TakeWhile iterator is designed to be lazily evaluated 9 | /// as the the map iterator 10 | /// It's just a wrapper over underlying iterator 11 | pub fn DoubleEndedTakeWhileContext(comptime Context: type, comptime FilterFn: type) type { 12 | comptime { 13 | IterAssert.assertDoubleEndedIteratorContext(Context); 14 | } 15 | return struct { 16 | const Self = @This(); 17 | pub const InnerContextType = Context; 18 | pub const ItemType = Context.ItemType; 19 | 20 | filterFn: FilterFn, 21 | fused: bool = false, 22 | context: Context, 23 | 24 | pub fn init(context: InnerContextType, filterFn: FilterFn) Self { 25 | return Self{ 26 | .filterFn = filterFn, 27 | .context = context, 28 | .fused = false, 29 | }; 30 | } 31 | /// return 0 if the context does not support 32 | /// size_hint 33 | pub fn sizeHintFn(self: *Self) SizeHint { 34 | _ = self; 35 | return .{}; 36 | } 37 | 38 | /// Look at the nth item without advancing 39 | pub fn peekAheadFn(self: *Self, n: usize) ?ItemType { 40 | var count: usize = 0; 41 | var i: usize = 0; 42 | while (count < n) { 43 | if (self.context.peekAheadFn(i)) |value| { 44 | if (self.filterFn(value)) { 45 | count += 1; 46 | } else { 47 | return null; 48 | } 49 | i += 1; 50 | } else { 51 | return null; 52 | } 53 | } 54 | return self.context.peekAheadFn(i); 55 | } 56 | 57 | pub fn peekBackwardFn(self: *Self, n: usize) ?ItemType { 58 | var count: usize = 0; 59 | var i: usize = 0; 60 | while (count < n) { 61 | if (self.context.peekBackwardFn(i)) |value| { 62 | if (self.filterFn(value)) { 63 | count += 1; 64 | } else { 65 | return null; 66 | } 67 | i += 1; 68 | } else { 69 | return null; 70 | } 71 | } 72 | return self.context.peekBackwardFn(i); 73 | } 74 | 75 | pub fn nextFn(self: *Self) ?ItemType { 76 | if (self.context.nextFn()) |value| { 77 | if (self.filterFn(value)) { 78 | return value; 79 | } else { 80 | self.fused = true; 81 | } 82 | } 83 | return null; 84 | } 85 | 86 | pub fn nextBackFn(self: *Self) ?ItemType { 87 | if (self.context.nextBackFn()) |value| { 88 | if (self.filterFn(value)) { 89 | return value; 90 | } else { 91 | self.fused = true; 92 | } 93 | } 94 | return null; 95 | } 96 | 97 | pub fn skipFn(self: *Self) bool { 98 | if (self.context.skipFn()) |value| { 99 | if (self.filterFn(value)) { 100 | return true; 101 | } else { 102 | self.fused = true; 103 | } 104 | } 105 | return false; 106 | } 107 | 108 | pub fn skipBackFn(self: *Self) bool { 109 | if (self.context.skipBackFn()) |value| { 110 | if (self.filterFn(value)) { 111 | return true; 112 | } else { 113 | self.fused = true; 114 | } 115 | } 116 | return false; 117 | } 118 | 119 | pub fn reverseFn(self: *Self) void { 120 | self.fused = false; 121 | self.context.reverseFn(); 122 | } 123 | }; 124 | } 125 | 126 | pub fn TakeWhileContext(comptime Context: type, comptime FilterFn: type) type { 127 | comptime { 128 | IterAssert.assertIteratorContext(Context); 129 | } 130 | return struct { 131 | const Self = @This(); 132 | pub const InnerContextType = Context; 133 | pub const ItemType = Context.ItemType; 134 | 135 | filterFn: FilterFn, 136 | fused: bool = false, 137 | context: Context, 138 | 139 | pub fn init(context: InnerContextType, filterFn: FilterFn) Self { 140 | return Self{ 141 | .filterFn = filterFn, 142 | .context = context, 143 | .fused = false, 144 | }; 145 | } 146 | /// return 0 if the context does not support 147 | /// size_hint 148 | pub fn sizeHintFn(self: *Self) SizeHint { 149 | _ = self; 150 | return {}; 151 | } 152 | 153 | /// Look at the nth item without advancing 154 | pub fn peekAheadFn(self: *Self, n: usize) ?ItemType { 155 | var count: usize = 0; 156 | var i: usize = 0; 157 | while (count < n) { 158 | if (self.context.peekAheadFn(i)) |value| { 159 | if (self.filterFn(value)) { 160 | count += 1; 161 | } else { 162 | return null; 163 | } 164 | i += 1; 165 | } else { 166 | return null; 167 | } 168 | } 169 | return self.context.peekAheadFn(i); 170 | } 171 | 172 | pub fn nextFn(self: *Self) ?ItemType { 173 | if (self.context.nextFn()) |value| { 174 | if (self.filterFn(value)) { 175 | return value; 176 | } else { 177 | self.fused = true; 178 | } 179 | } 180 | return null; 181 | } 182 | 183 | pub fn skipFn(self: *Self) bool { 184 | if (self.context.skipFn()) |value| { 185 | if (self.filterFn(value)) { 186 | return true; 187 | } else { 188 | self.fused = true; 189 | } 190 | } 191 | return false; 192 | } 193 | 194 | pub fn reverseFn(self: *Self) void { 195 | self.fused = false; 196 | self.context.reverseFn(); 197 | } 198 | }; 199 | } 200 | 201 | /// A TakeWhile Iterator constructor 202 | /// It's actually a wrapper over an iterator 203 | pub fn TakeWhileIterator(comptime Context: type, comptime FilterFn: type) type { 204 | if (IterAssert.isDoubleEndedIteratorContext(Context)) { 205 | const TakeWhileContextType = DoubleEndedTakeWhileContext(Context, FilterFn); 206 | return IIterator(TakeWhileContextType); 207 | } else { 208 | const TakeWhileContextType = TakeWhileContext(Context, FilterFn); 209 | return IIterator(TakeWhileContextType); 210 | } 211 | } 212 | 213 | pub fn takeWhile(s: anytype, func: anytype) TakeWhileIterator(SliceIter.SliceContext(GetPtrChildType(@TypeOf(s))), @TypeOf(func)) { 214 | return SliceIter.slice(s).take_while(func); 215 | } 216 | 217 | const std = @import("std"); 218 | const debug = std.debug; 219 | const testing = std.testing; 220 | const slice = @import("slice.zig").slice; 221 | 222 | test "test takeWhile method" { 223 | const ints: []const u32 = &[_]u32{ 2, 4, 6, 8, 9 }; 224 | const truths: []const u32 = &[_]u32{ 2, 4, 6, 8 }; 225 | 226 | const S = struct { 227 | pub fn is_even(cur: u32) bool { 228 | return cur % 2 == 0; 229 | } 230 | }.is_even; 231 | var take_while_iter = takeWhile(ints, S); 232 | try IterAssert.testIterator(take_while_iter, truths, 4, .{}); 233 | } 234 | 235 | test "test takeWhile method with std" { 236 | var str: []const u8 = "1234abcd"; 237 | var truths: []const u8 = "1234"; 238 | 239 | var take_while_iter = takeWhile(str, std.ascii.isDigit); 240 | 241 | try IterAssert.testIterator(take_while_iter, truths, 4, .{}); 242 | } 243 | -------------------------------------------------------------------------------- /src/take.zig: -------------------------------------------------------------------------------- 1 | const math = @import("std").math; 2 | const IIterator = @import("core/iterator.zig").IIterator; 3 | const SliceIter = @import("slice.zig"); 4 | const SizeHint = @import("core/size-hint.zig").SizeHint; 5 | 6 | const GetPtrChildType = @import("utils.zig").GetPtrChildType; 7 | const IterAssert = @import("utils.zig"); 8 | 9 | /// The Take iterator is designed to be lazily evaluated 10 | /// as the the map iterator 11 | /// It's just a wrapper over underlying iterator 12 | pub fn DoubleEndedTakeContext(comptime Context: type) type { 13 | comptime { 14 | IterAssert.assertDoubleEndedIteratorContext(Context); 15 | } 16 | return struct { 17 | const Self = @This(); 18 | pub const InnerContextType = Context; 19 | pub const ItemType = Context.ItemType; 20 | 21 | context: Context = undefined, 22 | total: usize = 1, 23 | number: usize = 1, 24 | 25 | pub fn init(context: InnerContextType, init_state: usize) Self { 26 | if (init_state == 0) { 27 | @panic("TakeIterator should use a positive and non-zero number"); 28 | } 29 | return Self{ 30 | .context = context, 31 | .number = init_state, 32 | .total = init_state, 33 | }; 34 | } 35 | /// return 0 if the context does not support 36 | /// size_hint 37 | pub fn sizeHintFn(self: *Self) SizeHint { 38 | if (@hasDecl(InnerContextType, "sizeHintFn")) { 39 | if (self.context.sizeHintFn().len()) |value| { 40 | var num = blk: { 41 | if (value > self.number) { 42 | break :blk self.number; 43 | } else { 44 | break :blk value; 45 | } 46 | }; 47 | return SizeHint{ 48 | .low = num, 49 | .high = num, 50 | }; 51 | } 52 | return .{}; 53 | } else { 54 | return .{}; 55 | } 56 | } 57 | 58 | /// Look at the nth item without advancing 59 | pub fn peekAheadFn(self: *Self, n: usize) ?ItemType { 60 | if (self.number == 0) { 61 | return null; 62 | } 63 | if (self.number < n) { 64 | return null; 65 | } 66 | return self.context.peekAheadFn(n); 67 | } 68 | 69 | pub fn peekBackwardFn(self: *Self, n: usize) ?ItemType { 70 | if (self.number == 0) { 71 | return null; 72 | } 73 | if (self.number < n) { 74 | return null; 75 | } 76 | return self.context.peekBackwardFn(n); 77 | } 78 | 79 | pub fn nextFn(self: *Self) ?ItemType { 80 | if (self.number > 0) { 81 | self.number -= 1; 82 | return self.context.nextFn(); 83 | } 84 | return null; 85 | } 86 | 87 | pub fn nextBackFn(self: *Self) ?ItemType { 88 | if (self.number > 0) { 89 | self.number -= 1; 90 | return self.context.nextBackFn(); 91 | } 92 | return null; 93 | } 94 | 95 | pub fn skipFn(self: *Self) bool { 96 | if (self.number > 0) { 97 | self.number -= 1; 98 | return self.context.skipFn(); 99 | } 100 | return false; 101 | } 102 | 103 | pub fn skipBackFn(self: *Self) bool { 104 | if (self.number > 0) { 105 | self.number -= 1; 106 | return self.context.skipBackFn(); 107 | } 108 | return false; 109 | } 110 | 111 | // reset number to initial state 112 | pub fn reverseFn(self: *Self) void { 113 | self.number = self.total; 114 | self.context.reverseFn(); 115 | } 116 | }; 117 | } 118 | 119 | pub fn TakeContext(comptime Context: type) type { 120 | comptime { 121 | IterAssert.assertIteratorContext(Context); 122 | } 123 | return struct { 124 | const Self = @This(); 125 | pub const InnerContextType = Context; 126 | pub const ItemType = Context.ItemType; 127 | 128 | context: Context = undefined, 129 | total: usize = 1, 130 | number: usize = 1, 131 | 132 | pub fn init(context: InnerContextType, init_state: usize) Self { 133 | if (init_state == 0) { 134 | @panic("TakeIterator should use a positive and non-zero number"); 135 | } 136 | return Self{ 137 | .context = context, 138 | .number = init_state, 139 | .total = init_state, 140 | }; 141 | } 142 | /// return 0 if the context does not support 143 | /// size_hint 144 | pub fn sizeHintFn(self: *Self) SizeHint { 145 | if (@hasDecl(InnerContextType, "sizeHintFn")) { 146 | if (self.context.sizeHintFn().len()) |value| { 147 | var num = blk: { 148 | if (value > self.number) { 149 | break :blk self.number; 150 | } else { 151 | break :blk value; 152 | } 153 | }; 154 | return SizeHint{ 155 | .low = num, 156 | .high = num, 157 | }; 158 | } 159 | return .{}; 160 | } else { 161 | return .{}; 162 | } 163 | } 164 | 165 | /// Look at the nth item without advancing 166 | pub fn peekAheadFn(self: *Self, n: usize) ?ItemType { 167 | if (self.number == 0) { 168 | return null; 169 | } 170 | if (self.number < n) { 171 | return null; 172 | } 173 | return self.context.peekAheadFn(n); 174 | } 175 | 176 | pub fn nextFn(self: *Self) ?ItemType { 177 | if (self.number > 0) { 178 | self.number -= 1; 179 | return self.context.nextFn(); 180 | } 181 | return null; 182 | } 183 | 184 | pub fn skipFn(self: *Self) bool { 185 | if (self.number > 0) { 186 | self.number -= 1; 187 | return self.context.skipFn(); 188 | } 189 | return false; 190 | } 191 | }; 192 | } 193 | 194 | /// A Take Iterator constructor 195 | /// It's actually a wrapper over an iterator 196 | pub fn TakeIterator(comptime Context: type) type { 197 | if (IterAssert.isDoubleEndedIteratorContext(Context)) { 198 | const TakeContextType = DoubleEndedTakeContext(Context); 199 | return IIterator(TakeContextType); 200 | } else { 201 | const TakeContextType = TakeContext(Context); 202 | return IIterator(TakeContextType); 203 | } 204 | } 205 | 206 | pub fn take(s: anytype, state: usize) TakeIterator(SliceIter.SliceContext(GetPtrChildType(@TypeOf(s)))) { 207 | return SliceIter.slice(s).take(state); 208 | } 209 | 210 | const std = @import("std"); 211 | const debug = std.debug; 212 | const testing = std.testing; 213 | const slice = @import("slice.zig").slice; 214 | 215 | test "test take method" { 216 | const ints: []const u32 = &[_]u32{ 1, 2, 3, 4, 5, 6, 7, 8 }; 217 | const truth: []const u32 = &[_]u32{ 1, 2, 3, 4 }; 218 | 219 | var take_iter = take(ints, 4); 220 | try IterAssert.testIterator(take_iter, truth, 4, .{ 221 | .low = 4, 222 | .high = 4, 223 | }); 224 | } 225 | 226 | test "test slice Take" { 227 | const ints: []const u32 = &[_]u32{ 1, 2, 3, 4, 5, 6, 7, 8 }; 228 | const truth: []const u32 = &[_]u32{ 1, 2, 3, 4 }; 229 | 230 | var take_iter = SliceIter.slice(ints).take(4); 231 | 232 | try IterAssert.testIterator(take_iter, truth, 4, .{ 233 | .low = 4, 234 | .high = 4, 235 | }); 236 | } 237 | -------------------------------------------------------------------------------- /src/test_all.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const testing = std.testing; 3 | 4 | test "test all" { 5 | testing.refAllDecls(@import("fp.zig")); 6 | _ = @import("chain.zig"); 7 | _ = @import("core/iterator.zig"); 8 | _ = @import("enumerate.zig"); 9 | _ = @import("filter.zig"); 10 | _ = @import("filter-map.zig"); 11 | _ = @import("map.zig"); 12 | _ = @import("range.zig"); 13 | _ = @import("reverse.zig"); 14 | _ = @import("slice.zig"); 15 | _ = @import("step.zig"); 16 | _ = @import("take.zig"); 17 | _ = @import("take-while.zig"); 18 | _ = @import("fuse.zig"); 19 | } 20 | -------------------------------------------------------------------------------- /src/utils.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const meta = std.meta; 3 | const testing = std.testing; 4 | const SizeHint = @import("core/size-hint.zig").SizeHint; 5 | 6 | pub fn isSlice(comptime Ptr: type) bool { 7 | const info = @typeInfo(Ptr); 8 | return info == .Pointer and info.Pointer.size == .Slice; 9 | } 10 | 11 | pub fn assertSlice(comptime Ptr: type) void { 12 | if (!isSlice(Ptr)) { 13 | @compileError("require a Slice type"); 14 | } 15 | } 16 | 17 | pub fn GetPtrChildType(comptime Ptr: type) type { 18 | const info = @typeInfo(Ptr); 19 | if (info == .Array) { 20 | return info.Array.child; 21 | } else if (info == .Pointer and info.Pointer.size == .Slice) { 22 | return info.Pointer.child; 23 | } 24 | return Ptr; 25 | } 26 | 27 | pub fn isFloat(comptime T: type) bool { 28 | return @typeInfo(T) == .Float; 29 | } 30 | 31 | pub fn isInteger(comptime T: type) bool { 32 | return @typeInfo(T) == .Int; 33 | } 34 | 35 | pub fn isNumber(comptime T: type) bool { 36 | return isFloat(T) or isInteger(T); 37 | } 38 | 39 | pub fn assertNum(comptime T: type) void { 40 | if (!isNumber(T)) { 41 | @compileError("require a Number type"); 42 | } 43 | } 44 | 45 | pub fn isIteratorContext(comptime Context: type) bool { 46 | const has_nextFn = @hasDecl(Context, "nextFn"); 47 | const has_peekAheadFn = @hasDecl(Context, "peekAheadFn"); 48 | const has_skipFn = @hasDecl(Context, "skipFn"); 49 | return has_nextFn and has_peekAheadFn and has_skipFn; 50 | } 51 | 52 | pub fn assertIteratorContext(comptime Context: type) void { 53 | if (!isIteratorContext(Context)) { 54 | @compileError("Context is not a IteratorContext"); 55 | } 56 | } 57 | 58 | pub fn isDoubleEndedIteratorContext(comptime Context: type) bool { 59 | assertIteratorContext(Context); 60 | const has_nextBackwardFn = @hasDecl(Context, "nextBackFn"); 61 | const has_skipBackFn = @hasDecl(Context, "skipBackFn"); 62 | const has_peekBackwardFn = @hasDecl(Context, "peekBackwardFn"); 63 | return has_nextBackwardFn and has_skipBackFn and has_peekBackwardFn; 64 | } 65 | 66 | pub fn assertDoubleEndedIteratorContext(comptime Context: type) void { 67 | if (!isDoubleEndedIteratorContext(Context)) { 68 | @compileError("Context is not a DoubleEndedMapContext"); 69 | } 70 | } 71 | 72 | pub fn testIterator(it: anytype, expected: anytype, len: usize, hint: SizeHint) !void { 73 | var iter = it; 74 | try testing.expectEqual(hint, iter.size_hint()); 75 | 76 | var i: usize = 0; 77 | for (expected) |item| { 78 | try testing.expectEqual(item, iter.next().?); 79 | i += 1; 80 | } 81 | try testing.expect(iter.next() == null); 82 | try testing.expectEqual(len, i); 83 | } 84 | --------------------------------------------------------------------------------