ELEX 4653 Lab 3¶

This lab provides practice looking up built-in functions and using iterators and exceptions.

Version 2: compatible with Python 3.7.

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¶

The documentation in the Built-in Functions section of the Python Standard Library documentation describes Python's built-in functions.

Define a tuple named builtins where each element is a Python function that:

  • returns an object that allows you to read from a file

  • creates an object that can only take on values True or False

  • returns a single-character string corresponding to a Unicode value

  • returns the memory address of an object (at least, in the CPython implementation of Python)

    For example, your answer might look like:

builtins = (hash,help,hex,tuple)

The entries in the tuple should be the functions, not the names of the functions as strings.

In [1]:
builtins = (open,bool,chr,id)
#builtins = (open,bool,chr,enumerate)

Question 2¶

Write a function counttrue(i) that returns the number of values returned by the iterable object i that are logically true.

For example, counttrue([True, False, '', {1}]+list(range(-3,3))) should return 7.

Note: The argument i is iterable, but it is not a list.

In [2]:
def counttrue(i):
    n=0
    for x in i:
        n += 1 if x else 0
    return n

def counttrue(i):
    # return sum(map(lambda x: 1 if x else 0,i))+1
    return sum(map(lambda x: 1 if x else 0,i))

Question 3¶

Write a function, nrep(n,c) where n is an iterable returning integers and c is an iterable returning characters. The function should return a list of strings in which each string is the letter returned by the second iterable repeated the number of times returned by the first iterable.

For example, nrep([3,0,1],"abc") should return ['aaa', '', 'c'].

Note: n and c are iterables but not lists or strings.

In [3]:
def nrep(n,c):
    #return [(a+1)*b for a,b in zip(n,c)]
    return [a*b for a,b in zip(n,c)]

nrep([3,0,1],"abc")
Out[3]:
['aaa', '', 'c']

Question 4¶

Write a function stable(c) that returns the magnitude of the complex value c if it is less than one or raises the ValueError exception otherwise.

For example if stable(0.5+0.5j) would return 0.7071... but stable(1+0.5j) would raise exception ValueError.

In [4]:
def stable(c):
    r = abs(c)
    #if r < 1.5:
    if r < 1:
        return r
    else:
        raise ValueError
        
for s in (0, 0.5+0.5j, 1+0.5j):
    try:
        print(stable(s))
    except ValueError:
        pass
0
0.7071067811865476

Question 5¶

Write a function rerun(f,n) that repeatedly calls the function f() until f() has raised n ValueError exceptions and then returns.

Do not count the times for which no exception, or a different exception, is raised by f()

For example, if the function onetry() were defined as:

def onetry():
    import random
    if random.random() > 0.5:
        print("raising ValueError")
        raise ValueError
    else:
        raise NotImplementedError

Then rerun(onetry,5) should result in "raising ValueError" always being printed 5 times.

You do not need to define the function f() except for testing. The marking code will pass your rerun() function a different function f().

In [5]:
def onetry():
    import random
    if random.random() > 0.5:
        print("raising ValueError")
        raise ValueError
    else:
        raise NotImplementedError    

def rerun(f,n):
    while n>0:
        try:
            f()
        except ValueError:
            n -= 1
            #n -= 2
        except:
            pass
    return

rerun(onetry,5)
raising ValueError
raising ValueError
raising ValueError
raising ValueError
raising ValueError
In [6]:
# lab validation code; do not modify
def labcheck(testing=0,ntest=10):
    '''
    Python exercise checking.
    Ed.Casas 2023-5-22
    Calls functions q<n>* and checks HMAC of return value[0].
    On mismatch prints return value[1] (e.g. function, arguments and return values).
    Setting testing=1 prints HMACs of correct results; paste into 'hashvalues'.
    Notes:
    If q<n>* result not JSON-able, convert to string.
    Result order matters for comparison. Sort result if specific order not required.
    '''
    
    import base64, copy, hashlib, json, random, re, string, types 
    from random import randint
    
    # compare regex to strings that should/shouldn't match
    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}'"  
    
    # list of n words with nl letters from chars without repeats
    def randwords(n,chars=string.ascii_lowercase,nl=(2,5)):
        l = []
        while len(l)<n:
            w = ''.join([chars[randint(0,len(chars)-1)] for i in range(randint(*nl))])
            if w not in l:
                l.append(w)
        return l
    
    # convert sets to dicts and dict keys to strings so they can be sorted
    def orderkeys(o):
        if isinstance(o,set):
            return {str(k):None for k in o}
        if isinstance(o,dict):
            return {str(k):orderkeys(v) for k,v in o.items()}
        return o

    class newlist(list):
        pop, copy = None, None
        def __getitem__(self,i):
            raise NotImplementedError("it's not a list!")

    # Q1a, Q1b, ...
    names=[]
    for f in builtins:
        try:
            names.append(f.__name__)
        except:
            names.append("???")
    def Q1a(): return names[0],f"builtins[0] ({names[0]}) is wrong"
    def Q1b(): return names[1],f"builtins[1] ({names[1]}) is wrong"
    def Q1c(): return names[2],f"builtins[2] ({names[2]}) is wrong"
    def Q1d(): return names[3],f"builtins[3] ({names[3]}) is wrong"

    def Q2():
        l = []
        ni,ns,nw = [randint(0,3) for i in range(3)]
        nt = [ni,ns,nw]
        l.extend([1]*ni + [0]*randint(0,3))
        l.extend([{1}]*ns + [{}]*randint(0,3))
        l.extend(randwords(nw) + ['']*randint(0,3))
        random.shuffle(l)
        l = newlist(l)
        ol = copy.deepcopy(l)
        r = counttrue(l)
        return r, f"counttrue({ol}) returned {r}"
    
    def Q3():
        nl = randint(3,5)
        c = newlist((chr(randint(ord('a'),ord('z'))) for i in range(nl)))
        n = newlist([randint(0,4) for i in range(nl)])
        l=copy.deepcopy([n,c])
        r = nrep(*l)
        return r,f"nrep({n},{c}) returns {r}"
    
    def Q4():
        r = []
        for i in range(40):
            c = complex(random.random(),random.random())
            try:
                stable(c)
            except ValueError:
                abs(c) <= 1 and r.append(f"stable({c:.3f}) did not raise ValueError")
                continue
            except Exception as e:
                r.append(f"stable({c:.3f}) raised exception {e}")
                continue
            abs(c) > 1 and r.append(f"stable({c:.3f}) did not raise exception")
        return r,'\n'.join(r)
    
    def Q5():
        n=randint(3,8)
        m=0
        def f():
            nonlocal m
            i = randint(1,3)
            if i == 1:
                m += 1
                raise ValueError
            elif i == 2:
                raise RuntimeWarning           
        rerun(f,n)
        return f"{n}{m}",f"rerun(f,{n}) asserted ValueError {m} times."
    
             
    hashvalues = '''
zY5pzY5pzY5pzY5pzY5pzY5pzY5pzY5pzY5pzY5p
YRUxYRUxYRUxYRUxYRUxYRUxYRUxYRUxYRUxYRUx
sjVusjVusjVusjVusjVusjVusjVusjVusjVusjVu
yT6RyT6RyT6RyT6RyT6RyT6RyT6RyT6RyT6RyT6R
+Hd6LgOnHreh6x5+LgOndp1qLgOnZEo+HrehVj+X
bVYN3Dd7w+Wq9iVBo1+Y/pjXUBBgm+F+oZytc2QJ
q1ESq1ESq1ESq1ESq1ESq1ESq1ESq1ESq1ESq1ES
2rEtmXGz2445mXGz2rEtmXGz2rEtmXGzmXGzxbQr
'''.split()

    newhash = ''
    dsize = 3 # HMAC base64 digest size (bytes, use 3 or 6 for 4 or 8 char digests)
    dlen = ((dsize*8+5)//6+3)//4*4
    
    for n,f in [(n,f) for n,f in locals().items() if callable(f) and re.search(r'^[Qq]\d+.*',n)]:
        random.seed(n)      
        hashes = '0'*dlen*ntest if testing else hashvalues.pop(0)
        err = ''
        while hashes and not err:
            h, hashes = hashes[:dlen], hashes[dlen:] 
            try:
                v,s = f()
                b = json.dumps(orderkeys(v),sort_keys=True).encode()
                c = base64.b64encode(hashlib.blake2b(b,digest_size=dsize).digest()).decode()
                if testing:
                    print(s)
                    newhash += c
                else:
                    if c != h:
                        err = f"Wrong result for test {n}: {s} (HMAC={c} instead of {h})"
            except Exception as e:
                err = f"Error during test {n}: {e}"
                if testing:
                    print(err)
        if testing:
            newhash += '\n'
        else:
            print(err or f"Passed test {n}.")
            
    if testing:
        print(newhash)

labcheck()
Passed test Q1a.
Passed test Q1b.
Passed test Q1c.
Passed test Q1d.
Passed test Q2.
Passed test Q3.
Passed test Q4.
Passed test Q5.