regexcanvas-lms

Alternation group without repetitions using a single regex in a Canvas Quiz


I work with Canvas in a middle school and am constructing quizzes which contain a question type called "fill-in-the-blank". A grading option for this type of question is to compare what the student typed in to a regex. The Quiz settings only allow me to enter a single regex and if the input matches the question is counted as correct. This is simple to do for single or few word answers. But I would like to ask something like, "list three different examples of environmental stimuli, ended with a period". So a correct input string might be the following:

rain. bright sunlight. a dog's bark.

but an incorrect input string would be:

water. water. a bicycle.

I can match individual phrases and split them using ([\w\s']*[^\.]?). This yields three matches in a regex tester. But I need to go further.

I need to check each phrase for a keyword, and then check if that keyword has been used before. Essentially, I need to match an alternation group without repetitions using a single regex pattern. Can this be done?


Solution

  • If I understood correctly, you want to have an alternation group without repetitions. This is a bit complex and has some limitations but can be done.

    We need to

    i.e. a pattern like this using the regex flags gxi (or inlining (some of) them (?x)(?i)

    ^                                    # begin of string
    (?:(?!(?:\1|\2|\3)).)*?              # ungreedy anything that is not captured by group 1,2,or 3
       (                                 # 1th capturing group
        (?:\brain\b|\bsunlight\b|\bbark) # Alternation group (answers) \b word-boundaries as needed
       )
    (?:(?!(?:\1|\2|\3)).)*?              # ungreedy anything that is not captured by group 1,2,or 3
       (                                 # 3th capturing group
        (?:(?!(?:\1|\3))(?:\brain\b|\bsunlight\b|\bbark)) # Answers not captured in group 1 or 3
       )
    (?:(?!(?:\1|\2|\3)).)*?              # ungreedy anything that is not captured by group 1,2,or 3
       (                                 # 4th capturing group
        (?:(?!(?:\2|\3))(?:\brain\b|\bsunlight\b|\bbark)) # Answers not captured by group 2 or 3
       )
    (?:(?!(?:\1|\2|\3)).)*               # anything that is not captured by group 1,2,or 3
    $                                    # end of string
    

    The pattern could be simplified but I tried to keep the pattern readable (Though, techniques like (?(DEFINE).(?"answers")) could help us to make the regex more maintainable.)

    Ruby Rublar Demo (g|global, and m|multiline flags are default on tubular),
    PCRE Demo (using gmxi-flags)
    Quizzes are using Ruby regular expressions which are very similar to PCRE but not exactly the same.

    This shows the basic idea and should get you started. Nonetheless, a regex is not a linguistics tool nor an AI. The pattern is context-free and does not verify if the answers make actual sense, e.g. if we define rain|sunlight|bark as possible answers this is valid:

    Barking elephant, green Sunlight, purple rain, and other non-sense