Skip to content

Commit b5a4e10

Browse files
committed
adopt tuple-based leapjoin API
It turned out that the old dyn-based API didn't mesh well, so remove it. The tuple-based API also enables `filter_with` and `filter_anti` to be used on their own, though perhaps a dedicated loop would be more efficient in that case.
1 parent 1118b29 commit b5a4e10

File tree

3 files changed

+330
-141
lines changed

3 files changed

+330
-141
lines changed

src/lib.rs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ pub use crate::treefrog::{
2727
filter_anti::FilterAnti,
2828
filter_with::FilterWith,
2929
filters::{PrefixFilter, ValueFilter},
30-
Leaper, RelationLeaper,
30+
Leaper, Leapers, RelationLeaper,
3131
};
3232

3333
/// A static, ordered list of key-value pairs.
@@ -116,9 +116,9 @@ impl<Tuple: Ord> Relation<Tuple> {
116116

117117
/// Creates a `Relation` using the `leapjoin` logic;
118118
/// see [`Variable::leapjoin`]
119-
pub fn from_leapjoin<'a, SourceTuple: Ord, Val: Ord + 'a>(
119+
pub fn from_leapjoin<'leap, SourceTuple: Ord, Val: Ord + 'leap>(
120120
source: &Relation<SourceTuple>,
121-
leapers: &mut [&mut dyn Leaper<'a, SourceTuple, Val>],
121+
leapers: impl Leapers<'leap, SourceTuple, Val>,
122122
logic: impl FnMut(&SourceTuple, &Val) -> Tuple,
123123
) -> Self {
124124
treefrog::leapjoin(&source.elements, leapers, logic)
@@ -409,8 +409,9 @@ impl<Tuple: Ord> Variable<Tuple> {
409409
/// some dynamic variable `source` of source tuples (`SourceTuple`)
410410
/// with some set of values (of type `Val`).
411411
/// - You provide these values by combining `source` with a set of leapers
412-
/// `leapers`, each of which is derived from a fixed relation. You can create
413-
/// a leaper in one of two ways:
412+
/// `leapers`, each of which is derived from a fixed relation. The `leapers`
413+
/// should be either a single leaper (of suitable type) or else a tuple of leapers.
414+
/// You can create a leaper in one of two ways:
414415
/// - Extension: In this case, you have a relation of type `(K, Val)` for some
415416
/// type `K`. You provide a closure that maps from `SourceTuple` to the key
416417
/// `K`. If you use `relation.extend_with`, then any `Val` values the
@@ -423,10 +424,10 @@ impl<Tuple: Ord> Variable<Tuple> {
423424
/// - Finally, you get a callback `logic` that accepts each `(SourceTuple, Val)`
424425
/// that was successfully joined (and not filtered) and which maps to the
425426
/// type of this variable.
426-
pub fn from_leapjoin<'a, SourceTuple: Ord, Val: Ord + 'a>(
427+
pub fn from_leapjoin<'leap, SourceTuple: Ord, Val: Ord + 'leap>(
427428
&self,
428429
source: &Variable<SourceTuple>,
429-
leapers: &mut [&mut dyn Leaper<'a, SourceTuple, Val>],
430+
leapers: impl Leapers<'leap, SourceTuple, Val>,
430431
logic: impl FnMut(&SourceTuple, &Val) -> Tuple,
431432
) {
432433
self.insert(treefrog::leapjoin(&source.recent.borrow(), leapers, logic));

src/test.rs

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ fn reachable_with_leapfrog(edges: &[(u32, u32)]) -> Relation<(u32, u32)> {
6161
// reachable(N1, N3) :- edges(N1, N2), reachable(N2, N3).
6262
reachable.from_leapjoin(
6363
&reachable,
64-
&mut [&mut edges_by_successor.extend_with(|&(n2, _)| n2)],
64+
edges_by_successor.extend_with(|&(n2, _)| n2),
6565
|&(_, n3), &n1| (n1, n3),
6666
);
6767
}
@@ -125,6 +125,46 @@ proptest! {
125125
let output2 = sum_join_via_relation(&set1, &set2);
126126
assert_eq!(output1.elements, output2.elements);
127127
}
128+
129+
/// Test the behavior of `filter_anti` used on its own in a
130+
/// leapjoin -- effectively it becomes an "intersection"
131+
/// operation.
132+
#[test]
133+
fn filter_with_on_its_own((set1, set2) in (inputs(), inputs())) {
134+
let input1: Relation<(u32, u32)> = set1.iter().collect();
135+
let input2: Relation<(u32, u32)> = set2.iter().collect();
136+
let intersection1 = Relation::from_leapjoin(
137+
&input1,
138+
input2.filter_with(|&tuple| tuple),
139+
|&tuple, &()| tuple,
140+
);
141+
142+
let intersection2: Relation<(u32, u32)> = input1.elements.iter()
143+
.filter(|t| input2.elements.binary_search(&t).is_ok())
144+
.collect();
145+
146+
assert_eq!(intersection1.elements, intersection2.elements);
147+
}
148+
149+
/// Test the behavior of `filter_anti` used on its own in a
150+
/// leapjoin -- effectively it becomes a "set minus" operation.
151+
#[test]
152+
fn filter_anti_on_its_own((set1, set2) in (inputs(), inputs())) {
153+
let input1: Relation<(u32, u32)> = set1.iter().collect();
154+
let input2: Relation<(u32, u32)> = set2.iter().collect();
155+
156+
let difference1 = Relation::from_leapjoin(
157+
&input1,
158+
input2.filter_anti(|&tuple| tuple),
159+
|&tuple, &()| tuple,
160+
);
161+
162+
let difference2: Relation<(u32, u32)> = input1.elements.iter()
163+
.filter(|t| input2.elements.binary_search(&t).is_err())
164+
.collect();
165+
166+
assert_eq!(difference1.elements, difference2.elements);
167+
}
128168
}
129169

130170
/// Test that `from_leapjoin` matches against the tuples from an
@@ -144,7 +184,7 @@ fn leapjoin_from_extend() {
144184
while iteration.changed() {
145185
variable.from_leapjoin(
146186
&variable,
147-
&mut [&mut doubles.extend_with(|&(i, _)| i)],
187+
doubles.extend_with(|&(i, _)| i),
148188
|&(i, _), &j| (i, j),
149189
);
150190
}

0 commit comments

Comments
 (0)