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.
Make donations with PayPal - it's fast, free and secure!