Skip to main content

patterns/
seq.rs

1use crate::arith::{raise_by, Floor};
2use patterns_macros::because;
3
4pub fn map<T, U>(items: &[T], each: fn(&T) -> U) -> Vec<U> {
5    let mut out = Vec::new();
6    for item in items {
7        out.push(each(item));
8    }
9    out
10}
11
12pub fn keep<T: Clone>(items: &[T], when: fn(&T) -> bool) -> Vec<T> {
13    let mut out = Vec::new();
14    for item in items {
15        if when(item) {
16            out.push(item.clone());
17        }
18    }
19    out
20}
21
22pub fn fold<T, A>(items: &[T], start: A, step: fn(A, &T) -> A) -> A {
23    let mut acc = start;
24    for item in items {
25        acc = step(acc, item);
26    }
27    acc
28}
29
30pub fn count<T>(items: &[T], when: fn(&T) -> bool) -> usize {
31    let mut total = 0usize;
32    for item in items {
33        if when(item) {
34            total += 1;
35        }
36    }
37    total
38}
39
40pub fn any<T>(items: &[T], when: fn(&T) -> bool) -> bool {
41    for item in items {
42        if when(item) {
43            return true;
44        }
45    }
46    false
47}
48
49pub fn all<T>(items: &[T], when: fn(&T) -> bool) -> bool {
50    for item in items {
51        if !when(item) {
52            return false;
53        }
54    }
55    true
56}
57
58pub fn first<T: Clone>(items: &[T], when: fn(&T) -> bool) -> Option<T> {
59    for item in items {
60        if when(item) {
61            return Some(item.clone());
62        }
63    }
64    None
65}
66
67pub trait Keyed {
68    type Key: PartialEq;
69
70    fn key(&self) -> Self::Key;
71}
72
73pub trait Advance {
74    type By: Copy;
75
76    fn advanced(self, by: Self::By) -> Self;
77}
78
79pub trait Shift<M> {
80    type By: Copy;
81
82    fn shifted(self, by: Self::By) -> Self;
83}
84
85
86pub fn is_empty<T>(items: &[T]) -> bool {
87    items.is_empty()
88}
89
90pub fn size<T>(items: &[T]) -> usize {
91    items.len()
92}
93
94pub fn is_full<T>(items: &[T], capacity: usize) -> bool {
95    items.len() >= capacity
96}
97
98pub fn holds<T: Keyed>(items: &[T], key: T::Key) -> bool {
99    for item in items {
100        if item.key() == key {
101            return true;
102        }
103    }
104    false
105}
106
107pub fn found<T: Keyed + Clone>(items: &[T], key: T::Key) -> Option<T> {
108    for item in items {
109        if item.key() == key {
110            return Some(item.clone());
111        }
112    }
113    None
114}
115
116pub fn with<T: Clone>(items: &[T], item: T) -> Vec<T> {
117    let mut out = items.to_vec();
118    out.push(item);
119    out
120}
121
122pub fn without<T: Keyed + Clone>(items: &[T], key: T::Key) -> Vec<T> {
123    let mut out = Vec::new();
124    for item in items {
125        if item.key() != key {
126            out.push(item.clone());
127        }
128    }
129    out
130}
131
132pub fn advance_all<T: Advance + Clone>(items: &[T], by: T::By) -> Vec<T> {
133    let mut out = Vec::new();
134    for item in items {
135        out.push(item.clone().advanced(by));
136    }
137    out
138}
139
140pub fn shift_all<M, T: Shift<M> + Clone>(items: &[T], by: T::By) -> Vec<T> {
141    let mut out = Vec::new();
142    for item in items {
143        out.push(item.clone().shifted(by));
144    }
145    out
146}
147
148pub trait Ranked {
149    type Rank: PartialOrd;
150
151    fn rank(&self) -> Self::Rank;
152}
153
154pub fn count_above<T: Ranked>(items: &[T], bound: T::Rank) -> usize {
155    let mut seen = 0usize;
156    for item in items {
157        if item.rank() > bound {
158            seen += 1;
159        }
160    }
161    seen
162}
163
164pub fn keep_above<T: Ranked + Clone>(items: &[T], bound: T::Rank) -> Vec<T> {
165    let mut out = Vec::new();
166    for item in items {
167        if item.rank() > bound {
168            out.push(item.clone());
169        }
170    }
171    out
172}
173
174pub fn rank_of<T: Ranked>(value: &T) -> T::Rank {
175    value.rank()
176}
177
178pub trait Facet<F> {
179    type Value: PartialEq;
180
181    fn facet(&self) -> Self::Value;
182}
183
184pub fn count_facet<F, T: Facet<F>>(items: &[T], value: T::Value) -> usize {
185    let mut seen = 0usize;
186    for item in items {
187        if item.facet() == value {
188            seen += 1;
189        }
190    }
191    seen
192}
193
194pub fn any_facet<F, T: Facet<F>>(items: &[T], value: T::Value) -> bool {
195    for item in items {
196        if item.facet() == value {
197            return true;
198        }
199    }
200    false
201}
202
203pub fn first_facet<F, T: Facet<F> + Clone>(items: &[T], value: T::Value) -> Option<T> {
204    for item in items {
205        if item.facet() == value {
206            return Some(item.clone());
207        }
208    }
209    None
210}
211because!(map, "the replacement for a for loop that builds a list, taking a fn pointer rather than a closure so nothing can capture");
212because!(keep, "filtering by a predicate, separate from map because a filter changes the length and a map does not");
213because!(fold, "the general accumulation, kept because map and keep cannot express a running total");
214because!(count, "folding to a count, named separately because counting is common enough that spelling it as a fold obscures the intent");
215because!(any, "existence over a predicate, which short circuits and so is not count compared against zero");
216because!(all, "universality over a predicate, the mirror of any and separate because the empty case answers differently");
217because!(first, "the first element satisfying a predicate, returning an Option so the empty case is a value rather than a panic");
218because!(is_empty, "emptiness as a call, so a length comparison never appears in spec or app");
219because!(size, "length as a call, so a length comparison never appears in spec or app");
220because!(is_full, "a collection held against its capacity, so a capacity is a usize in exactly one place and a fullness test is a call rather than a comparison");
221because!(holds, "membership by key, so a caller asks whether the collection already has this thing without writing the comparison");
222because!(found, "retrieval by key, separate from holds because the caller that wants the item should not have to search twice");
223because!(with, "adding an element and returning the new collection, so a binding never mutates");
224because!(without, "removing by key and returning the new collection, the mirror of with");
225because!(advance_all, "advancing every element by its own rule, so a caller never loops to apply a step to a collection");
226because!(count_above, "counting elements ranked over a bound, which is count composed with a comparison the caller would otherwise write as a closure");
227because!(keep_above, "the filtering form of count_above, kept separate because a caller usually wants either the number or the survivors and not both");
228because!(rank_of, "the rank a single element projects to, exposed so a caller can order by the same projection the collection functions use");
229because!(count_facet, "counting elements whose facet matches, the equality form of count_above and the route for a predicate over a named field");
230because!(any_facet, "existence over a facet, short circuiting, so a caller asks whether any element matches without counting them all");
231because!(first_facet, "the first element whose facet matches, returning an Option so the empty case is a value rather than a panic");
232because!(Shift, "the marker parameterised form of Advance, because coherence allows one Advance impl per type and a value that needs two different mutations cannot have them");
233because!(shift_all, "advancing every element by one named mutation, so a type with several ways to move keeps them apart instead of merging them into one enum");
234
235pub fn count_below<T: Ranked>(items: &[T], bound: T::Rank) -> usize {
236    let mut seen = 0usize;
237    for item in items {
238        if item.rank() < bound {
239            seen += 1;
240        }
241    }
242    seen
243}
244because!(count_below, "counting elements ranked under a bound, the mirror of count_above and separate because a caller asking how many are short of a limit should not have to invert the question");
245
246pub fn count_from<T: Ranked>(items: &[T], bound: T::Rank) -> usize {
247    let mut seen = 0usize;
248    for item in items {
249        if item.rank() >= bound {
250            seen += 1;
251        }
252    }
253    seen
254}
255because!(count_from, "counting elements ranked at or over a bound, separate from count_above because the boundary case is exactly what a limit turns on and inverting a strict comparison is a common error");
256
257pub trait Valued<M> {
258    type Value: Copy;
259
260    fn value(&self) -> Self::Value;
261}
262because!(Valued, "a lookup from a vocabulary variant to the figure it stands for, kept apart from Ranked because a cap, a rate or an allowance is read and never ordered, and the marker lets one variant carry several such figures");
263
264pub fn value_of<M, T: Valued<M>>(item: &T) -> T::Value {
265    item.value()
266}
267because!(value_of, "the figure a variant stands for, as a call, so the table a vocabulary carries is read from spec and app without a method call");
268
269pub fn keep_facet<F, T: Facet<F> + Clone>(items: &[T], value: T::Value) -> Vec<T> {
270    let mut out = Vec::new();
271    for item in items {
272        if item.facet() == value {
273            out.push(item.clone());
274        }
275    }
276    out
277}
278because!(keep_facet, "the elements whose facet matches, the equality form of keep_above and the route for keeping every match on a named field without a closure");
279
280pub fn without_facet<F, T: Facet<F> + Clone>(items: &[T], value: T::Value) -> Vec<T> {
281    let mut out = Vec::new();
282    for item in items {
283        if item.facet() != value {
284            out.push(item.clone());
285        }
286    }
287    out
288}
289because!(without_facet, "removing every element whose facet matches, the mirror of keep_facet, and separate from without because a facet can be a tuple over several fields where a key is one identity");
290
291pub fn last<T: Clone>(items: &[T], when: fn(&T) -> bool) -> Option<T> {
292    let mut out = None;
293    for item in items {
294        if when(item) {
295            out = Some(item.clone());
296        }
297    }
298    out
299}
300because!(last, "the final element satisfying a predicate, separate from first because a log is read from its end as often as its start and reversing the list to ask copies it");
301
302pub fn total<M, T: Valued<M>>(items: &[T], start: T::Value) -> T::Value
303where
304    T::Value: Floor,
305{
306    let mut sum = start;
307    for item in items {
308        sum = raise_by(sum, item.value());
309    }
310    sum
311}
312because!(total, "the sum of every value a marker reads from the elements, seeded by the caller and saturating, so a total of money or days stops at the type ceiling rather than wrapping and the pattern never conjures a zero; it reads Valued and not Ranked because a rank orders and a value is added");
313
314pub fn found_ref<T: Keyed>(items: &[T], key: T::Key) -> Option<&T> {
315    items.iter().find(|item| item.key() == key)
316}
317because!(found_ref, "retrieval by key that borrows the element, beside found because a query over a large state should not copy the element it reads while a reducer that keeps the element wants its own copy");
318
319pub fn first_ref<T>(items: &[T], when: fn(&T) -> bool) -> Option<&T> {
320    items.iter().find(|item| when(item))
321}
322because!(first_ref, "the first element satisfying a predicate, borrowed, beside first because a caller that only reads the element should not pay for a clone and a type that is not Clone still needs the search");
323
324pub fn keep_ref<T>(items: &[T], when: fn(&T) -> bool) -> Vec<&T> {
325    let mut out = Vec::new();
326    for item in items {
327        if when(item) {
328            out.push(item);
329        }
330    }
331    out
332}
333because!(keep_ref, "filtering that borrows the survivors, beside keep because a query that only counts or reads what passes should not copy the elements it passes over");
334
335pub fn first_facet_ref<F, T: Facet<F>>(items: &[T], value: T::Value) -> Option<&T> {
336    items.iter().find(|item| item.facet() == value)
337}
338because!(first_facet_ref, "the first element whose facet matches, borrowed, beside first_facet because a binding that reads one field of the match should not clone the whole element to get it");
339
340pub fn at<T: Clone>(items: &[T], index: usize) -> Option<T> {
341    items.get(index).cloned()
342}
343because!(at, "the item in a position if the position is there, so a binding reaches into a sequence without the square brackets it may not write and without the chance of ending the program on a position that is not");
344
345pub fn firsts<A: Clone, B>(pairs: &[(A, B)]) -> Vec<A> {
346    pairs.iter().map(|(a, _)| a.clone()).collect()
347}
348because!(firsts, "the left of every pair, the twin of seconds and separate from map because a pair has no facet to name, so what is wanted is said by which side is taken rather than by a function that reaches in");
349
350pub fn seconds<A, B: Clone>(pairs: &[(A, B)]) -> Vec<B> {
351    pairs.iter().map(|(_, b)| b.clone()).collect()
352}
353because!(seconds, "the right of every pair, the twin of firsts, and separate from map because a pair carries no facet a function could name; the two sides are kept apart because a side taken by its number reads as nothing at the call");
354
355pub fn pushed<T>(items: Vec<T>, item: T) -> Vec<T> {
356    let mut out = items;
357    out.push(item);
358    out
359}
360because!(pushed, "a list with one more element, taking the list by value so a reducer that owns its state appends without copying what it already holds, where with copies for a caller that only borrows");
361
362pub fn without_trailing_facet<F, T: Facet<F> + Clone>(items: &[T]) -> Vec<T> {
363    let last = match items.last() {
364        Some(item) => item.facet(),
365        None => return vec![],
366    };
367    let mut out = Vec::with_capacity(items.len());
368    for item in items {
369        if item.facet() != last {
370            out.push(item.clone());
371        }
372    }
373    out
374}
375because!(without_trailing_facet, "a list with every element of its last group removed, the group being whatever the projection says, so a capped fetch keeps only groups it is sure it saw whole");
376
377pub fn listed<T: Clone>(items: &[T]) -> Vec<T> {
378    items.to_vec()
379}
380because!(listed, "a borrowed run of elements as an owned list, for a reducer that must hand a copy into an event it builds and may not call a method to do it");