Course Sets

Specifying courses, and in particular sets of courses, is an important part of specifying plans. Specifying a single course, like ^CS_135 is just the tip of the iceberg.

As a running example, consider the Math requirement that students take one of CS115, CS135, or CS145. This could, of course, be specified as

all of {
  1 of { 
    ^CS_115 
    ^CS_135 
    ^CS_145 
  }
  # More requirements follow...
}

But there are more compact and flexible ways to do it.

Union

One way is to use the course set union operator, +.

all of {
  1 of { 
    ^CS_115 + ^CS_135 + ^CS_145 
  }
  # More requirements follow...
}

A course specification, on its own, is automatically interpreted as a 1 of rule. The above rule can be abbreviated to just:

all of {
  ^CS_115 + ^CS_135 + ^CS_145
  # More requirements follow...
}

Named Course Sets

A course set can be given a name. This name will appear in the degree audit output and can help give meaning to a set of courses. It also allows a course set to be used more than once.

all of {
  cs1A = ^CS_115 + ^CS_135 + ^CS_145

  cs1A
  3 from ^CS_.* - cs1A
  # More requirements follow...
}

cs1A is interpreted as 1 from cs1A. The second rule reuses cs1A to require 3 CS courses that are not in cs1A.

Names are scoped. This has several implications:

  1. A name is only valid between the opening and closing brackets {...} – and that includes any nested requirements.
  2. A duplicate name within the same scope will create an error. That is,
    all of {
       cs1A = ^CS_115 + ^CS_135 + ^CS_145
       cs1A = ^CS_115
    }
    will cause an error.
  3. A name can “hide” another name in an enclosing scope. For example:
    all of "Faculty requirements" {
     cs1A = ^CS_115 + ^CS_135 + ^CS_145
     # A
    
     all of "CS Major requirements" {
       cs1A = ^CS_135 + ^CS_145
       # B
       all of {
         # C
       }
     }
     # D
    }
    At points A and D, the first definition (with CS115) is applicable. At points B and C the second definition (omitting CS115) is applicable.

Course ranges

A range of courses can be specified with a hypen: ^CS_440-490 is a commonly used range in Computer Science. It matches any CS course with a catalog number between 440 and 490, inclusive. CS445L is considered to be part of this range.

Regular expressions

Course sets can also be specified with a regular expression syntax.
Some examples:

  • Square brackets match any of the characters they contain.
    • ^CS_1[134]5 matches any of CS_115, CS_135, or CS_145 but not CS_155.
    • ^CS_1[134][56] matches any of six courses, CS_115, CS_116, CS_135, CS_136, CS_145, and CS_146.
    • ^ENGL_108[A-Z] includes ENGL_108 with the 26 suffixes, A through Z.
  • A dot matches any character: ^CS_1.5 matches a total of ten courses: CS_105, CS_115, CS_125, …, CS_195. It would also match courses like CS_1u5 or even CS_1@5 if we had courses labeled like that!
  • \d is an abbreviation for [0123456789].
  • An asterisk, *, matches any number of what immediately precedes it. It often follows a dot, which matches any character.
    • ^CS_.* matches any CS course.
    • ^ENGL_108.* matches ENG_L108 (the .* matches 0 times) as well as ENGL_108A, ENGL_108B (useful), and ENGL_108ABBBB (not useful).
    • ^ENGL_108[A-Z]* is a more restricted version of the previous example.
  • A plus, + is like * except that it matches one or more (not zero) of what precedes it.
  • A question mark, ?, matches 0 or 1 occurrences of what precedes it. It’s probably the best way to represent something that is optional.
  • A “pipe”, |, is used for “or”. It’s often used with parentheses.
    • ^CS_(115|135|145) matches three CS courses.
    • ^(CS_|CO_)467 matches both CS_467 as well as CO_467.

Combining course sets

We’ve just seen many ways to create a course set, for example ^CS_1[134]5, ^CS_(115|135|145), and ^CS_115 + ^CS_135 + ^CS_145 are three different ways to specify the same set of three courses.

Addition (set union, revisited)

So far we’ve just used + to combine individual courses. But + can also combine whole sets (union). For example,

firstYearCS = ^CS_(115|135|145) + ^CS_1[134]6

gives a set of six courses.

Subtraction (set difference)

The subtraction operator says “all the courses in this set except for the ones in that set”. An example from Honours Science:

    sci = ^(BIOL_|CHEM_|EARTH_|MNS_|PHYS_|SCI_).* - ^SCI_237

matches a whole bunch of science courses – except for SCI_237, a survey course primarily for students in AHS, Arts, and Environment.

Here’s an example from Math:

mathCourses = ^(ACTSC_|AMATH_|CO_|CS_|MATBUS_|MATH_|PMATH_|STAT_).*
nonMathCourses = passedCourses - mathCourses

passedCourses is a pre-defined set of courses that includes all the courses where the student has earned credit.

Intersection

The /\ operator takes the intersection of two sets – all the courses that appear in both. Here are some examples from Honours Science:

    sci = ^(BIOL_|CHEM_|EARTH_|MNS_|PHYS_|SCI_).* - ^SCI_237
    sciYr4 = sci /\ ^[A-Z]+_4.*
    sciYr34 = sci /\ ^[A-Z]+_[34].*
    sciYr234 = sci /\ ^[A-Z]+_[234].*

Briefly, sci defines all Science courses. sciYr4 intersects that with the set of all fourth year courses (any subject specified with [A-Z]+_ followed by the digit 4 followed by any other characters for the remainder of the catalog number and any letters to designate labs, etc.).

Parentheses

Parentheses can be used to indicate order of operations:

(^CS_3.+ + ^CS_4.+) - ^CS_.3.

gives all third and fourth year CS courses except for those with a middle digit of 3.

Restrictions

It’s often the case that you want only courses that have a certain attribute, for example the student has earned credit for it! Restricting a set of courses is done with a where clause.

The basic syntax is a course set, defined with regular expressions, union, set difference, intersection, etc followed by the where keyword and then a comma-separated list of “functions”. Only those courses where all of the described attributes are true are left in the set.

For example, something similar to the following is automatically defined:

allCourses = ^[A-Z]+_[0-9]+[A-Z]?
passedCourses = allCourses where attemptClass(PASS)

That is, all courses where the student has passed the course. See below for other attempt classes.

The following where filters are currently defined. This list can be expanded relatively easily. Information between angle brackets is meant to be replaced, as described.

  • takenBefore(<TERMID>) requires that the course be taken before the specified term.

  • takenAfter(<TERMID>) requires the course to be taken after the beginning of the specified term (that is, takenAfter includes the term; takenBefore does not).

  • attemptClass(<CLASS>) requires the course to belong to one of the following classes. This is the easy way to find passed or failed courses because it takes into account requirement designations, etc.

    • PASS: The student passed the course, earned credit, etc. This is involves looking at the grading basis, requirement designations, etc.
    • FAIL: A failing grade for the course that hasn’t been cleared, etc.
    • COOP: A “course” related to Co-op Education – courses with a subject code of COOP, PD, WKRPT, etc.
    • IGNORE: Courses that have been cleared, audited, are extra to the degree, etc.
    • UNPRODUCTIVE: Withdrawn courses.
    • UNKNOWN: Courses that don’t have a grade assigned yet, including grades that are under review.
  • earnedCredit(<BOOLEAN>):

    • true: include courses where the earned credit flag is true.
    • false: include courses where the earned credit flag is false.
  • transferCredit(<BOOLEAN>):

    • true: include courses with transfer credit; exclude others (grading basis code is TRN).
    • false: exclude transfer credits.
  • courseGrade("<RE>"): The RE (regular expression) specifies the required grades to be included in the set of courses. Don’t use this to select passing grades – that’s too complex! A more common use would be marks above a certain threshold: courseGrade("[789][0-9]|100") (grades 70 and above).

  • usableCourseAttempt(<BOOLEAN>): filtering for the Math Faculty’s “usable attempt” rule. true includes usable attempts; false includes the unusable attempts.

  • courseComponenent("<RE>"): filtering for specified course components such as passedCourses where courseComponent("LEC|LAB") to choose only passed lecture and lab courses.

Precedence

Precedence is the implied order of operations – the equivalent of BEDMAS (Brackets, Exponents, Division/Multiplication, Addition/Subtraction) for arithmetic – but here we need it for course sets.

From highest precedence (do first) to lowest (do last):

  1. Parentheses
  2. Regular expressions (^CS_1[134]5), named course sets (coreCS), course ranges (^CS_440-489).
  3. Restrictions (where takenBefore(1239)) always bind to the course set expression immediately to their left.
  4. Intersection (/\), union (+) and difference (-)

When precedence is equal, proceed left to right.

In the following examples, the expression on the left is equivalent to the fully parenthesized expression on the right. a, b and c might be regular expressions such as ^CS_1[134]5, named expressions such as csCore, or a range such as ^CS_440-489. R is a restriction such as takenBefore(1239).

Expression Fully Parenthesized
^CS_135 + ^CS_136 + ^CS_137 ((^CS_135 + ^CS_136) + ^CS_137)
a - b + c ((a - b) + c)
a + b where R a + (b where R)
(a + b) where R (a + b) where R
a + b /\ c ((a + b) /\ c)

Degree audit includes a “pretty printer” which presents the degree requirements in a consistent format. The pretty printer drops parentheses that are not required. For example, it will print (a + b) + c as a + b + c.

Best Practices

It is NOT recommended to specify a requirement in the following way:

all of {
  cs1A = ^CS_115 + ^CS_135 + ^CS_145

  cs1A
}

It is easier to read if we wrote out the whole requirement, 1 from cs1A.

BWB says: 1 from cs1A will be rewritten by Degree Audit to be just cs1A.

Additionally, use brackets liberally! Do not skip out on them when specifying course sets.