|
Programmer's Notebook |
All computer source code presented on this page, unless it includes attribution to another author, is provided by Ed Halley under the Artistic License. Use such code freely and without any expectation of support. I would like to know if you make anything cool with the code, or need questions answered.
python/ bindings.py boards.py buzz.py cache.py cards.py constraints.py english.py getopts.py gizmos.py goals.py improv.py interpolations.py namespaces.py nihongo.py nodes.py octalplus.py patterns.py persist.py physics.py pieces.py quizzes.py recipes.py relays.py romaji.py ropen.py sheets.py strokes.py subscriptions.py svgbuild.py testing.py things.py timing.py ucsv.py useful.py uuid.py vectors.py weighted.py java/ GlobFilenameFilter.java RegexFilenameFilter.java StringBufferOutputStream.java ThreadSet.java TracingThread.java Utf8ConsoleTest.java perl/ CVQM.pm Kana.pm Typo.pm cxx/ CCache.h equalish.cpp |
# -*- python -*- ''' patterns - Data-agnostic pattern matching classes. SYNOPSIS >>> import patterns Defining a pattern: >>> class bullmarket (patterns.SpanPattern): ... def match(self, point): ... if not super(bullmarket, self).match(point): ... return False ... is_bull = compute_if_trending_up(self.points) ... return is_bull Using the pattern: >>> bull = bullmarket(15) # SpanPattern records past n points >>> for day in chart: ... if bull.match(day): ... print 'bull market detected prior to', day AUTHOR Ed Halley (ed@halley.cc) 23 January 2010 ''' #---------------------------------------------------------------------------- class Pattern (object): '''Abstract base for data pattern matching routines.''' def __init__(self): pass def match(self, point): return False class UnaryPattern (object): '''Abstract base for a transform on another single pattern.''' def __init__(self, pattern): self.pattern = pattern class NotPattern (UnaryPattern): '''An inverse of another pattern: if it returns False, we give True.''' def match(self, point): return not self.pattern.match(point) class EverPattern (UnaryPattern): '''An inverse of another pattern: if it returns False, we give True.''' def match(self, point): # we stop calling child once we're flagged; # beware this may interfere with child's data collection if getattr(self, 'flag', False): return True if self.pattern.match(point): self.flag = True return True return False class NeverPattern (UnaryPattern): '''An inverse of another pattern: if it returns False, we give True.''' # same as NotPattern(EverPattern(pattern)) def match(self, point): # we stop calling child once we're flagged; # beware this may interfere with child's data collection if getattr(self, 'flag', False): return False if self.pattern.match(point): self.flag = True return False return True class CompositePattern (object): '''Abstract base for all voting or sequencing patterns.''' def __init__(self, *patterns): if len(patterns) == 1 and isinstance(patterns[0], (list,tuple)): patterns = patterns[0] self.patterns = patterns def match(self, point): self.matches = [ x.match(point) for x in self.patterns ] # it's important to always ask match() on all child patterns # as some children may collect data over a period of time return True class AllPattern (CompositePattern): '''A unanimous voting pattern: all member patterns must vote True.''' # same as AnyPattern(NotPattern(pattern1), NotPattern(pattern2), ...) def match(self, point): super(AllPattern, self).match(point) return self.matches.count(True) == len(self.patterns) class AnyPattern (CompositePattern): '''A nomination voting pattern: any member pattern can vote True.''' def match(self, point): super(AnyPattern, self).match(point) return self.matches.count(True) > 0 class NoPattern (CompositePattern): '''A veto voting pattern: any member voting True means False.''' # same as NotPattern(AnyPattern(patterns)) def match(self, point): super(NoPattern, self).match(point) return self.matches.count(True) == 0 class VotePattern (CompositePattern): '''A counted voting pattern: enough member patterns must vote True.''' def __init__(self, enough, *patterns): super(VotePattern, self).__init__(*patterns) self.enough = enough def match(self, point): super(VotePattern, self).match(point) return self.matches.count(True) >= self.enough class SequencePattern (CompositePattern): '''A serial pattern: members must vote True to proceed, False to abort.''' def __init__(self, *patterns): super(SequencePattern, self).__init__(*patterns) self.progress = 0 def match(self, point): super(SequencePattern, self).match(point) if self.matches[self.progress]: self.progress += 1 if self.progress >= len(self.patterns): self.progress = 0 return True else: self.progress = 0 return False class SpanPattern (Pattern): '''Abstract base for patterns that use a span of recent points.''' def __init__(self, span): super(SpanPattern, self).__init__() if span < 2: raise ValueError, 'must have a span of at least two' self.span = span self.points = [] def match(self, point): if len(self.points) >= self.span: self.points = self.points[1-self.span:] self.points.append(point) return len(self.points) >= self.span #---------------------------------------------------------------------------- def __test__(): from testing import __ok__ if (False): h = Sheet('aapl.csv') print h.headers print len(h), 'points' h.sort(key='Date') print h[0] class ell(Pattern): def match(self, point): return point == 'L' class em(Pattern): def match(self, point): return point == 'M' class en(Pattern): def match(self, point): return point == 'N' l = ell() ; m = em() ; n = en() all = AllPattern(l, m, n) any = AnyPattern(l, m, n) none = NoPattern(l, m, n) notl = NotPattern(l) everl = EverPattern(l) neverl = NeverPattern(l) vote = VotePattern(2, m, n, everl) five = SpanPattern(5) print '-', tests = 'l m n all any none notl everl nev\'l vote five'.split() print ' '.join([ "%5s" % x for x in tests ]) for x in 'JKLMNOP': print x, print "%5s" % l.match(x), print "%5s" % m.match(x), print "%5s" % n.match(x), print "%5s" % all.match(x), print "%5s" % any.match(x), print "%5s" % none.match(x), print "%5s" % notl.match(x), print "%5s" % everl.match(x), print "%5s" % neverl.match(x), print "%5s" % vote.match(x), print "%5s" % five.match(x), print if __name__ == '__main__': from testing import __report__ __test__() __report__() |
|
Contact Ed Halley by email at
ed@halley.cc. Text, code, layout and artwork are Copyright © 1996-2008 Ed Halley. Copying in whole or in part, with author attribution, is expressly allowed. Any references to trademarks are illustrative and are controlled by their respective owners. |
|