Next: , Previous: , Up: Top   [Contents][Index]

4 Patterns

Roan provides a simple pattern language for matching rows. This is useful, among other things, for counting rows considered particularly musical or unmusical.

A pattern string describes the bells in a row, with several kinds of wildcards and other constructs matching multiple bells. Bells’ names match themselves, so, for example, "13572468" matches queens on eight. A question mark matches any bell, and an asterisk matches runs of zero or more bells. Thus "*7468", at major, matches all twenty-four 7468s, and "?5?6?7?8" matches all twenty-four major rows that have the 5-6-7-8 in the positions they are in in tittums. Alternatives can be separated by the pipe character, ‘|’. Thus "13572468|12753468" matches either queens or Whittingtons. Concatentation of characters binds more tightly than alternation, but parentheses can be used to group subexpressions. Thus "*(4|5|6)(4|5|6)78" at major matches all 144 combination rollups. When matched against two major rows "?*12345678*?" matches wraps of rounds, but not either row being rounds.

Two further notations are possible. In each case it does not extend what can be expressed, it merely makes more compact something that can be expressed with the symbols already described. The first is a bell class, which consits of one or more bell names within square brackets, and indicates any one of those bells. Thus an alternative way to match the 144 combination rollups at major is "*[456][456]78".

A more compact notation is also available for describing runs of consecutive bells. Two bell symbols separated by a hyphen represent the run of bells from one to the other. Thus "*5-T" matches all rows ending 567890ET. If such a run description is followed by a solidus, ‘/’, and a one or two digit integer, it matches all runs of the length of that integer that are subsequences of the given run. Thus "*2-8/4" is equivalent to "*(2345|3456|4567|5678)". If instead of a solidus a percent sign, ’%’, is used it matches subsequences of both the run and its reverse. Thus "1-6%4*" matches all little bell runs off the front of length four selected from the bells 1 through 6, and is equivalent to the pattern "(1234|4321|2345|5432|3456|6543)*". There is some possible ambiguity with this notation, in that the second digit of an integer following a solidus or percent sign could be interpreted as a digit or a bell symbol. In these cases it is always interpreted as a digit, but the other use can be specified by using parentheses or a space.

Spaces, but no other whitespace, can be included in patterns. However no spaces may be included within bell classes or run descriptions. Thus " 123 [456] 7-T/3 * " is equivalent to "123[456]7-T/3*", but both "123[ 4 5 6 ]7-T/3*" and "123[456]7-T / 3*" are illegal, and will cause an error to be signaled.

In addition to strings, patterns may be represented by parse trees, which are simple list structures made up of keywords and bells (that is, small, non-negative integers). Strings are generally more convenient for reading and writing patterns by humans, but parse trees can be more convenient for programmatically generated patterns. The function pattern-parse converts the string representation of a pattern to such a tree structure. Sequences of elements are represented by lists starting with :sequence; alternatives by lists starting with :or; bell classes by lists of the included bells preceded by :class; runs by a list of the form (:run start end length bi), where start is the starting bell, end the ending bell, length the length of the run, and bi is a generalized boolean saying whether or not the runs are bidirectional; bells are represented by themselves; and ‘?’ and ‘*’ by :one and :any, respectively. The elements of the :sequence and :or lists may also be lists themselves, representating subexpressions. For example, the string "(?[234]*|*4-9%4?)*T" is equivalent to the tree

 (:sequence (:or (:sequence :one (:class 1 2 3) :any)
                 (:sequence :any (:run 3 8 4 t) :one))
            :any
            11)
Function: row-match-p pattern row &optional following-row

Determines whether row, or pair of consecutive rows, row and following-row, match a pattern. If following-row is supplied it should be of the same stage as row. The pattern may be a string or a tree, and should be constructed to be appropriate for the stage of row; an error is signaled if it contains explicit matches for bells of higher stage than row. Returns a generalized boolean indicating whether or not pattern matches.

 (row-match-p "*[456][456]78" !32516478) ⇒ t
 (row-match-p "*[456][456]78" !12453678) ⇒ nil
 (row-match-p "*[456][456]78" !9012345678) ⇒ t
 (row-match-p "?*123456*?" !651234 !562143) ⇒ t
 (row-match-p "?*123456*?" !651234 !652143) ⇒ nil
 (row-match-p "?*123456*?" !123456) ⇒ nil
 (row-match-p '(:sequence :any 6 7) !65432178) ⇒ t
 (row-match-p '(:sequence :any 6 7) !23456781) ⇒ nil

Signals an error if pattern cannot be parsed as a pattern, if row is not a row, if following-row is neither a row nor nil, if pattern contains bells above the stage of row, or if following-row is a row of a different stage than row.

Care should be used when matching against two rows. In the usual use case when searching for things like wraps every row typically will be passed twice to this method, first as row and then as following-row. A naive pattern might end up matching twice, and thus double counting. For example, if at major "*12345678*" were used to search for wraps of rounds it would match whenever row or following-row were themselves rounds, possibly leading to double counting. Instead a search for wraps of rounds might be better done against something like "?*12345678*?".

Function: parse-pattern pattern &optional stage

Converts a string representation of a pattern to its parse tree, and returns it. The stage is the stage for which pattern is parsed, and defaults to *default-stage*. If pattern is a non-empty list it is presumed to be a pattern parse tree and is returned unchanged. Signals a type-error if pattern is neither a string nor a non-empty list, or if stage is not a stage. Signals a parse-error if pattern is a string but cannot be parsed as a pattern, or contains bells above those appropriate for stage.

 (parse-pattern "(?[234]*|*4-9%4?)*T" 12)
     ⇒
   (:sequence (:or (:sequence :one (:class 1 2 3) :any)
                   (:sequence :any (:run 3 8 4 t) :one))
              :any
              11)
Function: format-pattern tree &optional upper-case

Returns a string that if parsed with parse-pattern, would return the parse tree tree. Note that the generation of a suitable string from tree is not unique, and this function simply returns one of potentially many equivalent possibilities. The case of any bells represented by letters is controlled by upper-case, which defaults to the current value of *print-bells-upper-case*. Signals an error if tree is not a parse tree for a pattern.

 (format-pattern '(:sequence 0 1 2 :any 7) t) ⇒ "123*8"
Function: named-row-pattern name &optional stage covered

Returns a pattern, as a parse tree, that matches a named row at stage. The name is one of those listed below. If stage is not supplied it defaults to the current value of *default-stage*. If covered, a generalized boolean, is non-nil the row(’s) that will be matched will assume an implicit tenor. If covered is not supplied it defaults to nil for even stages and t for odd stages. If there is no such named row known that corresponds to the values of stage and covered nil is returned. Signals an error if name is not a keyword or is not a known named row name as enumerated below, or if stage is not a stage.

The supported values for name, and the stages at which they are defined, are:

:backrounds

any stage

:queens

uncovered singles and above, or covered two and above.

:kings

uncovered minimus and above, or covered singles and above; note that kings at uncovered minor or covered doubles is the same row as Whittingtons at those stages

:whittingtons

uncovered minor and above, or covered doubles and above; note that Whittingtons at uncovered minor or covered doubles is the same row as kings at those stages

:double-whittingtons

covered cinques or uncovered maximus, only

:roller-coaster

covered caters or uncovered royal, only

:near-miss

any stage

 (format-pattern (named-row-pattern :whittingtons 10 nil))
     ⇒ "1234975680"
 (format-pattern (named-row-pattern :whittingtons 9 t)
     ⇒ "123497568"
 (format-pattern (named-row-pattern :whittingtons 9 nil))
     ⇒ "123864579"
 (named-row-pattern :whittingtons 4)
     ⇒ nil
Type: pattern-parse-error

An error signaled when attempting to parse a malformed row pattern. Contains three potenitally useful slots accessible with pattern-parse-error-message, pattern-parse-error-pattern and pattern-parse-error-index.


Up: Patterns   [Contents][Index]

4.1 Counting matches

Often one would like to count how many times a variety of patterns match many different rows. To support this use Roan provides match-counters. After creating a match-counter with make-match-counter you add a variety of patterns to it, with add-pattern or add-patterns, each with a label, which will typically be a symbol or string, but can be any Lisp object. You then apply the match-counter to rows with record-matches, and query how many matches have occurred with match-counter-counts.

The order in which patterns are added to a match-counter is preserved, and is reflected in the return values of match-counter-labels, and match-counter-counts called without a second argument. Replacing an existing pattern by adding a different one with a label that is equalp to an existing one does not change the order, but deleting a pattern with remove-pattern and then re-adding it does move it to the end of the order. When a pattern has been replaced by one with an equalp label that is not eq to the original label which label is retained is undefined.

A match-counter also distinguishes matches that occur at handstroke from those that occur at backstroke. Typically you tell the match-counter which stroke the next row it is asked to match is on, and it then automatically alternates handstrokes and backstrokes for subsequent rows. For patterns that span two rows, such as wraps, the stroke is considered to be that between the rows; for example a wrap of rounds that spans a backstroke lead would be considered to be “at” backstroke.

 (let ((m (make-match-counter 8)))
   (add-patterns m '((cru "*[456][456]78")
                     (wrap "?*12345678*?" t)
                     (lb4 "1-7%4*|*1-7%4")))
   (loop for (row following)
         on (generate-rows #8!36.6.5.3x5.56.5,2)
         do (record-matches m row following))
   (values (match-counter-counts m)))
     ⇒ ((cru . 3) (wrap . 1) (lb4 . 5))
Type: match-counter

Used to collect statistics on how many rows match a variety of patterns.

Function: make-match-counter &optional stage

Returns a fresh match-counter, initially containing no patterns, that is configured to attempt to match patterns against rows of stage bells. If not supplied, stage defaults to the current value of *default-stage*. Attempts to add patterns only appropriate for a different stage or match rows of a different stage with record-matches will signal an error.

Function: add-pattern counter label pattern &optional double-row-p
Function: add-patterns counter lists

Adds one or more patterns to those matched by the match-counter count. A single pattern, pattern, is added, with label label, by add-pattern. If the generalized boolean double-row-p is true two rows (which typically should be consecutive) will be matched against pattern, and others one row; if not supplied double-row-p is nil. Multiple patterns may be added together with add-patterns: lists should be a list of lists, where the sublists are of the form (label pattern &optional double-row-p), and the patterns are added in the order given. In either case the pattern may be either a string or list structure that is a parsed pattern, such as returned by parse-pattern. If label is equalp to the label of a pattern already added to counter that pattern will be replaced, and its corresponding counts reset to zero. Either function reeturns counter. Either signals a type-error if counter is not a match-counter. Signals an error if any of the patterns are not an appropriate pattern for the stage of counter.

Function: remove-pattern counter label

Removes any pattern in method-counter count with its label equalp to label. Returns t if such a pattern was found and removed, and nil otherwise. Signals a type-error if count is not a method-counter.

Function: remove-all-patterns counter

Removes all the patterns in the method-counter counter, and returns a positive integer, the number of patterns so removed, if any, or nil if counter had no patterns. Signals a type-error if counter is not a match-counter.

Function: match-counter-pattern counter label &optional as-string upper-case

Returns two values: the first is the pattern whose label in count is equalp to label, if any, and otherwise nil; the second is a generalized boolean if and only if the first value is non-nil and the pattern is to be matched against two rows rather than just one. If the generalized boolean as-string is true the pattern is returned as a string, as by format-pattern, with the case of any bells represented by letters controled by the generalized boolean upper-case; and otherwise as a parse tree, as by parse-pattern. A string return value may not be string-equal to that added to counter, but will match the same rows. If as-string is not supplied it defaults to true; if upper-case is not supplied it defaults to the current value of *print-bells-upper-case*. Signals a type-error if counter is not a match-counter.

Function: match-counter-labels counter

Returns two lists, the labels of those patterns in count that are matched against a single row, and those that are matched against two rows. Both lists are in the order in which the corresponding patterns were first added to counter. Signals a type-error if counter is not a match-counter.

Function: match-counter-counts counter &optional label

Returns three values, the number of times the pattern with label equalp to label in counter has matched rows presented to it with record-matches since counter was reset or the relevent pattern was added to it. The first return value is the total number of matches, the second the number of matches at handstroke, and the third the number of matches at backstroke. If no label is supplied it instead returns three a-lists mapping the labels of the patterns in counter to the number of matches, again total, handstroke and backstroke. The elements of these a-lists are in the order in which the corresponding patterns were first added to counter. Returns nil if there is no pattern labeled label. Signals a type-error if counter is not a match-counter.

Function: reset-match-counter counter

Resets all the counts associated with all the patterns in counter to zero. Signals a type-error if counter is not a match-counter.

Function: match-counter-handstroke-p counter

Returns a generalized boolean indicating that the next row presented to counter will be a handstroke. Can be used with setf to tell counter whether or not it should consider the next row a handstroke or a backstroke. If not explicitly set again, either with (setf match-counter-handstroke-p), or with the handstroke-p argument to record-matches, whether or not subsequent rows will be considered handstroke or backstroke will alternate. Signals a type-error if counter is not a match-counter.

Function: record-matches counter row &optional following-row handstroke-p

Causes all the single-row patterns of counter to be matched against row, and, if a following-row is supplied and not nil, also all the double-row patterns to be matched against both rows. If the generalized boolean handstroke-p is supplied it indicates whether row is to be considered a handstroke or not, and, unless explicitly set again, either with the handstroke-p argument to record-matches by with (setf match-counter-handstroke-p), whether or not subsequent rows will be considered handroke or backstroke will alternate. That is, supplying a handtroke-p argument to record-matches is equivalent to calling (setf match-counter-handstoke-p) immediately before it. Signals a type-error if counter is not a match-counter, row is not a row, or following-row is neither a row nor nil.


Up: Patterns   [Contents][Index]