Repository navigation
Conversation
Add default implementations of ZipperMoving::to_prev_step and ZipperIteration::to_prev_val (plus their _observed forms), which visit positions / values in the exact reverse of to_next_step / to_next_val. Called at the root they move to the last position / value in the trie, and they return false once they are back at the root. The new methods are forwarded through the zipper lens macro and PathTracker, covered by the shared zipper_moving_tests and zipper_iteration_tests suites, documented in the book, and benchmarked in the binary_keys, sparse_keys and superdense_keys benches. Closes Adam-Vandervorst#120
# Conflicts: # src/zipper.rs
Adding lean descriptions and harness entries for the methods
|
Thanks for the contribution @anyingiit ! I made one behavior tweak around handling of non-existent paths, and I added the new entry points to the formal model. I'm happy to merge this. Can you please confirm, by replying in this PR, that you agree to license your contribution as part of the pathmap repo, under the MIT license and any/all future licenses. Out of curiosity, who is winshyon? |
|
We should also add descend last byte and descend last path specializations in one go |
I'll add them. But I don't want to do it in this PR. How urgent? Added #169 |
|
Not very. It does make MORK's tail sink about 30x slower than its head sink while the underlying data structure doesn't have a reason for that |
Requested by winshyon
Description
Adds reverse counterparts to the existing iteration methods, as requested in #120:
ZipperMoving::to_prev_step/to_prev_step_observed— visits every existing position in the exact reverse of theto_next_steporder. If the focus has a previous sibling it moves there and descends the last child at each level down to a leaf; otherwise it ascends one byte. Called at the root it moves to the last position in the trie, and it returnsfalseonce it is back at the root, so calling it repeatedly cycles just liketo_next_step.ZipperIteration::to_prev_val/to_prev_val_observed— visits values in the exact reverse of theto_next_valorder (a value that has descendants comes right after its last descendant). It follows the same path as repeatedto_prev_stepcalls, but usesdescend_last_path_observedto reach the last leaf under a subtrie in one call, which made it ~1.8x faster than a plainto_prev_steploop onbinary_keys.Both are default implementations built on
to_prev_sibling_byte/descend_last_byte/ascend_byte, so every zipper gets them. The zipper lens macro andPathTrackerforward them to the wrapped zipper (the same asto_next_*), so a native implementation can be dropped in later. As discussed in the issue,to_prev_k_path/descend_last_k_pathare not added.Tests:
to_prev_step_test1was added tozipper_moving_tests!, andzipper_prev_iter_test1/zipper_prev_iter_test2tozipper_iteration_tests!, so they run for every zipper type that uses those macros (read, owned read, write, prefix, product, overlay, poly, path_tracker, arena_compact, ...). They check that the reverse order is exactly the forward order reversed, that thePathObserveragrees withpath(), that stepping back from the middle of the trie works, and that iteration works from a zipper rooted below the map root.Benchmarks:
binary_zipper_prev_iter/binary_zipper_prev_step_iter,sparse_zipper_prev_cursor/sparse_zipper_prev_step_iter,superdense_zipper_prev_cursor/superdense_zipper_prev_step_iter, next to the existing forward benches. Medians on my machine (shared, noisy) at the largest size:Reverse stepping is about as fast as forward stepping.
to_prev_valis slower thanto_next_val, mostly on sparse/binary tries. The forward path uses the nativedescend_first_byte/descend_until, while the reverse path goes through the defaultdescend_last_byte(child_count+descend_indexed_byte) andto_prev_sibling_byte. Nativeto_prev_*implementations (or a nativedescend_last_byte) for the read zipper could close that gap in a follow-up, and these benches are there to measure it.The book's iteration chapter now mentions the new methods.
Motivation / disclosure: I had some spare AI-assistant quota (Claude Code) and am using it to try to help projects with open good-first-issues. The change was prepared with Claude Code and verified as listed below. If it doesn't fit, isn't up to your bar, or you'd simply rather not take it — please feel free to close it, no hard feelings at all 🙂
Related issue
Closes #120
Checklist
to_prev_step_observed/to_prev_val_observedare stubbed to returnfalse(28 failed), and pass with the change.cargo build --release --all-targets(no new warnings)cargo test --release: 1058 passed, 0 failed (lib) + integration and doc tests OKcargo test --release --features arena_compact,random: 1220 passed, 0 failed (lib) + integration and doc tests OKcargo doc --no-deps: OK (the one existing warning atsrc/write_zipper.rs:201is unchanged)cargo bench --bench {binary_keys,sparse_keys,superdense_keys} -- prevCHANGELOG.mdis updated (if applicable) — n/a (the repo has no CHANGELOG)pathmap-book/src/1.02.05_zipper_iter.md/api_links.md🤖 Generated with Claude Code