ELEX 4653 Lab 5¶

This lab provides practice defining classes, recursion, doing file I/O, and regular expressions.

Instructions:

  • Modify this notebook by adding the Python code described below.

  • Test your code using the menu item Cell ► Run All

  • Save the notebook (the .ipynb file) and upload it to the appropriate Assignment folder on the course web site.

Question 1¶

Define a class (type) named die. The constructor takes one argument, the number of die faces, with a default value of 6. The type has one method, roll(), that returns a random integer between 1 and the number of faces.

For example:

d = die(4)
print(d.roll(),d.roll())

would print two pseudo-random numbers between 1 and 4.

Hints: (1) random.randint(a,b) returns a (pseudo-)random integer between a and b. (2) Objects in the class namespace, including imported modules, will be in the self namespace when a method is called.

In [1]:
class die():
    from random import randint
    def __init__(self,sides=6):
        self.sides = sides
    def roll(self):
        return self.randint(1,self.sides)
    
d = die(4)
print(d.roll(),d.roll())
2 1

Question 2¶

Write a recursive function, rsearch(t) that searches through an object t composed of nested two-element tuples. Each element of these tuples may be an integer or another two-element tuple. The data structure contains only one non-zero integer. Your function should return the value of this non-zero integer.

For example, rsearch((0,((33,0),(0,0)))) should return 33.

In [2]:
def rsearch(t):
    l,r = t
    if type(l) == tuple:
        l = rsearch(l)
    if type(r) == tuple:
        r = rsearch(r)
    return max(l,r)
          
rsearch((0,((33,0),(0,0))))
Out[2]:
33
In [ ]:
 

Question 3¶

Write a function named partnums() that opens a file named partnums.txt and returns a list of all the words (as defined by the str.split() method) that consist of:

  • the capital letter A, B, or C
  • followed by 3 or 4 digits
  • followed by either a dash ('-') or a colon (':')
  • followed by two lower-case letters OR two digits (either letters or digits, not a combination)

The lab check code will create the file named partnums.txt in the same directory as this notebook before calling partnums().

In [3]:
import re
def partnums():
    r = []
    for l in open('partnums.txt'):
        for s in l.split():
            if re.match(r'[ABC]\d{3,4}[-:]([a-z]{2}|[0-9]{2})$',s):
                r.append(s)
    return r
In [4]:
# lab validation code; do not modify
def labcheck():
    import copy, random, re, string, types
    from random import randint
            
    def checkre(pat,ok,nok):
        for s in ok:
            assert re.fullmatch(pat,s), \
                f"pattern '{pat}'\n did not match string '{s}'"
        for s in nok:
            assert not re.fullmatch(pat,s), \
                f"pattern '{pat}'\n matched string '{s}'"  

    def randwords(n,chars=string.ascii_lowercase,nl=(2,5)):
        l = set()
        while len(l)<n:
            l |= set((''.join([chars[randint(0,len(chars)-1)] for i in range(randint(*nl))]),))
        return list(l)
    
    def q1():
        n = randint(2,10)
        on = copy.deepcopy(n)
        c = die(n)
        minr, maxr = c.roll(), c.roll()
        for i in range(10000):
            r = c.roll()
            minr = min(minr,r)
            maxr = max(maxr,r)
        assert minr==1 and maxr==on, \
            f"After 10,000 rolls die({on}) roll() returned values from {minr} to {maxr}"

    def q2():
        n = randint(6,10)
        nt = randint(1,n)
        v = randint(1,999)

        def rantuple(n):
            nonlocal nt
            if n <= 1:
                nt -= 1
                return v if not nt else 0
            i = randint(1,n-1)
            j = n - i
            return (rantuple(i),rantuple(j))
        t = rantuple(n)
        ot = copy.deepcopy(t)
        r = rsearch(t)
        assert r == v,f"rsearch({ot}) returns {r}"

    def q3():
        import string
        ok, nok = [], []
        np = 4
        nw = 10
        dorl = [string.digits,string.ascii_lowercase]
        for i in range(nw):
            s=[]
            s += [random.sample("ABC",k=1)[0]]
            s += [''.join(random.sample(string.digits,k=random.randint(3,4)))]
            s += [':' if random.random()>0.5 else '-']
            s += [''.join(random.sample(dorl[randint(0,1)],k=2))]
            ok.append(s)
        for i in range(nw):
            s=[]
            s += [random.sample("abcDEF",k=1)[0]]
            s += [''.join(random.sample(string.digits,k=random.sample([1,2,5,6,8],k=1)[0]))]
            s += ["!=*"[randint(0,2)]]
            k=random.sample([1,3],k=1)[0]
            s += [''.join(random.sample(dorl[randint(0,1)],k=k))]
            nok.append(s)
        okw = [''.join(l) for l in ok]
        ich = [j%np for j in range(nw)]
        random.shuffle(ich)
        nokw = [''.join([nok[j][i] if i == ich[j] else ok[j][i] for i in range(np)]) for j in range(nw)]
        f = open('partnums.txt','w')
        words = okw+nokw
        random.shuffle(words)
        for s in [w+(' ' if randint(0,1) else '\n') for w in words]:
            f.write(s)
        f.close()
        r = partnums()
        shouldnot = set(r) & set(nokw)
        shouldnot = f"which should not include {shouldnot}" if shouldnot else ""
        should = set(okw) - set(r)        
        should = f"which should include {should}" if should else ""
        assert set(okw) == set(r),f"partnums({words}) returns {r} {shouldnot} {should}"
             
    for s,i in [(s,s[1:]) for s in locals().keys() if re.search(r'q\d+',s)]:
        try:
            locals()[s]()
            print(f"Question {i} OK.")
        except Exception as e:
            print(f"Failed check for Question {i}: {e}")
            
labcheck()
Question 1 OK.
Question 2 OK.
Question 3 OK.