From 3dc6fcb12f33e6ea7790623268e3f5b57a32e300 Mon Sep 17 00:00:00 2001 From: Stuart Cook Date: Sat, 29 Sep 2018 22:06:58 +1000 Subject: [PATCH] Explain how Length.run_step works --- .../internal/conjecture/shrinking/length.py | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/hypothesis-python/src/hypothesis/internal/conjecture/shrinking/length.py b/hypothesis-python/src/hypothesis/internal/conjecture/shrinking/length.py index 42328c8a4d..8308831175 100644 --- a/hypothesis-python/src/hypothesis/internal/conjecture/shrinking/length.py +++ b/hypothesis-python/src/hypothesis/internal/conjecture/shrinking/length.py @@ -53,13 +53,45 @@ def left_is_better(self, left, right): return len(left) < len(right) def run_step(self): + # Try to delete as many elements from the sequence as possible, in + # (roughly) one pass, from right to left. + # + # Starting from the end of the sequence, delete as many consecutive + # elements as possible. When we reach an element that we can't delete + # this way, skip over it, and repeat the process for the remaining + # elements to its left. + # + # This diagram shows the layout of the non-deletable elements that + # we've previously skipped, the remaining elements that might be + # deletable, and the (k) elements we are currently trying to delete. + # The two slices show how we create a new sequence that omits those + # (k) elements. + # <=================| + # remaining skipped + # /^^^^^^^^^^^^^^^^^^^^^^^^^\ /^^^^^^^^^^^^^^^^^\ + # +---+---+---+---+---+---+---+---+---+---+---+---+ + # | | | | | | | | | | | | | + # +---+---+---+---+---+---+---+---+---+---+---+---+ + # \_________________/ \#####/ \_________________/ + # [:remaining-k] k [remaining:] + # <=====| + + # Number of elements at the end of the sequence that we were + # unable to delete. skipped = 0 + # When all elements have been deleted or skipped, the pass is complete. while skipped < len(self.current): + # Number of elements that might still be deletable. remaining = len(self.current) - skipped start = self.current + # Delete as many of the remaining elements as possible, from right + # to left. Always retain the skipped elements at the end. find_integer( lambda k: k <= remaining and self.consider( start[:remaining - k] + start[remaining:] ) ) + # Skip over the element we couldn't delete, and continue. + # (If we deleted everything, this will put j > len(self.current), + # which is harmless because the loop is about to end anyway.) skipped += 1