Revision | 6357c91a7658c1b18fe6d1026aa580807a4ce616 (tree) |
---|---|
Time | 2022-10-26 02:07:31 |
Author | Antoon Pardon <aej@pard...> |
Commiter | Antoon Pardon |
The comm2 iterator
@@ -1,4 +1,4 @@ | ||
1 | -# Copyright © 2017-2019 Antoon Pardon | |
1 | +# Copyright © 2017-2022 Antoon Pardon | |
2 | 2 | # |
3 | 3 | # Permission is hereby granted, free of charge, to any person obtaining a |
4 | 4 | # copy of this software and associated documentation files (the "Software"), |
@@ -153,3 +153,25 @@ | ||
153 | 153 | if self.next is _INVALID: |
154 | 154 | self.next = next(self.iterator) |
155 | 155 | return self.next |
156 | + | |
157 | +def comm2(it1: Iterable[Any], it2: Iterable[Any]) -> Iterable[tuple[Any, Any]]: | |
158 | + """ | |
159 | + This function behave a bit like the unix command comm. | |
160 | + It takes two sorted iterators and produces an iterator of tuples. | |
161 | + """ | |
162 | + top = 1,None | |
163 | + it1 = ((0, item) for item in it1) | |
164 | + it2 = ((0, item) for item in it2) | |
165 | + item1 = next(it1, top) | |
166 | + item2 = next(it2, top) | |
167 | + while (item1, item2) != (top, top): | |
168 | + if item1 < item2: | |
169 | + yield item1[1], None | |
170 | + item1 = next(it1, top) | |
171 | + elif item1 > item2: | |
172 | + yield None, item2[1] | |
173 | + item2 = next(it2, top) | |
174 | + else: | |
175 | + yield item1[1], item2[1] | |
176 | + item1 = next(it1, top) | |
177 | + item2 = next(it2, top) |
@@ -1,3 +1,3 @@ | ||
1 | -version = '0.01.12k' | |
2 | -modified = '2022-09-12 @ 13:20:16' | |
1 | +version = '0.01.13g' | |
2 | +modified = '2022-10-25 @ 19:07:31' | |
3 | 3 | installed = '*********************' |
@@ -2,13 +2,15 @@ | ||
2 | 2 | # pylint: disable=missing-function-docstring,invalid-name,missing-module-docstring |
3 | 3 | |
4 | 4 | from unittest import TestCase, main as unitmain #, skip |
5 | -from random import shuffle | |
5 | +from random import shuffle, randint | |
6 | 6 | from itertools import product |
7 | +from operator import itemgetter | |
7 | 8 | |
8 | 9 | #from typing import List |
9 | 10 | |
10 | 11 | # pylint: disable=unused-import |
11 | 12 | from AGPlib.iterators import PipeIterator, Apply, Map, concat, SubMap, Select, Discard, Peeker |
13 | +from AGPlib.iterators import comm2 | |
12 | 14 | # pylint: enable=unused-import |
13 | 15 | |
14 | 16 | SIZE_LIMIT = 101 |
@@ -46,6 +48,11 @@ | ||
46 | 48 | for sub_iterable in iterable: |
47 | 49 | yield from sub_iterable |
48 | 50 | |
51 | +def rndrange(*args): | |
52 | + for i in range(*args): | |
53 | + if randint(0, 1) == 1: | |
54 | + yield i | |
55 | + | |
49 | 56 | # pylint: disable=no-self-argument |
50 | 57 | |
51 | 58 | List = Apply(list) |
@@ -150,4 +157,37 @@ | ||
150 | 157 | peeked = peeker.peek() |
151 | 158 | s.assertEqual(index + 1, size) |
152 | 159 | |
160 | + def test_comm2_double_empty(s): | |
161 | + comm_lst = list(comm2([], [])) | |
162 | + s.assertEqual(comm_lst, []) | |
163 | + | |
164 | + def test_comm2_one_empty(s): | |
165 | + lst = list(rndrange(200)) | |
166 | + length = len(lst) | |
167 | + Nones = length * [None] | |
168 | + comm_lst1 = list(comm2(lst, [])) | |
169 | + projected0 = list(map(itemgetter(0), comm_lst1)) | |
170 | + projected1 = list(map(itemgetter(1), comm_lst1)) | |
171 | + s.assertEqual(lst, projected0) | |
172 | + s.assertEqual(Nones, projected1) | |
173 | + comm_lst2 = list(comm2([], lst)) | |
174 | + projected0 = list(map(itemgetter(0), comm_lst2)) | |
175 | + projected1 = list(map(itemgetter(1), comm_lst2)) | |
176 | + s.assertEqual(Nones, projected0) | |
177 | + s.assertEqual(lst, projected1) | |
178 | + | |
179 | + def test_comm2_double_filled(s): | |
180 | + itr1 = rndrange(1, 4096) | |
181 | + itr2 = rndrange(1, 4096) | |
182 | + prv = 0 | |
183 | + for item in comm2(itr1, itr2): | |
184 | + if item[0] is None: | |
185 | + nxt = item[1] | |
186 | + elif item[1] is None: | |
187 | + nxt = item[0] | |
188 | + else: | |
189 | + s.assertEqual(*item) | |
190 | + nxt = item[1] | |
191 | + s.assertLess(prv, nxt) | |
192 | + | |
153 | 193 | unitmain() |