On Wed, Aug 11, 2021 at 4:14 AM Hope Rouselle <hrouselle@jevedi.xotimo> wrote: > > Chris Angelico <ros...@gmail.com> writes: > > > On Tue, Aug 10, 2021 at 7:25 AM Hope Rouselle <hrouselle@jevedi.xotimo> > > wrote: > >> I came up with the following question. Using strings of length 5 > >> (always), write a procedure histogram(s) that consumes a string and > >> produces a dictionary whose keys are each substrings (of the string) of > >> length 1 and their corresponding values are the number of times each > >> such substrings appear. For example, histogram("aaaaa") = {"a": 5}. > >> Students can "loop through" the string by writing out s[0], s[1], s[2], > >> s[3], s[4]. > > > > In other words, recreate collections.Counter? Seems decent, but you'll > > need to decide whether you want them to use defaultdict, use > > __missing__, or do it all manually. > > Yes, the course introduces very little so there is a lot of recreation > going on. Hm, I don't know defaultdict and I don't know how to use > __missing__. The course does introduce dict.get(), though. If students > use dict.get(), then the procedure could essentially be: > > def histogram(s): > d = {} > d[s[0]] = d.get(s[0], 0) + 1 > d[s[1]] = d.get(s[1], 0) + 1 > d[s[2]] = d.get(s[2], 0) + 1 > d[s[3]] = d.get(s[3], 0) + 1 > d[s[4]] = d.get(s[4], 0) + 1 > return d
There's nothing wrong with getting students to recreate things, but there are so many different levels on which you could do this, which will leave your more advanced students wondering what's legal. :) Here are several ways to do the same thing: >>> s = "hello world" >>> from collections import Counter; Counter(s) Counter({'l': 3, 'o': 2, 'h': 1, 'e': 1, ' ': 1, 'w': 1, 'r': 1, 'd': 1}) >>> from collections import defaultdict >>> hist = defaultdict(int) >>> for ltr in s: hist[ltr] += 1 ... >>> hist defaultdict(<class 'int'>, {'h': 1, 'e': 1, 'l': 3, 'o': 2, ' ': 1, 'w': 1, 'r': 1, 'd': 1}) >>> class Incrementer(dict): ... def __missing__(self, key): return 0 ... >>> hist = Incrementer() >>> for ltr in s: hist[ltr] += 1 ... >>> hist {'h': 1, 'e': 1, 'l': 3, 'o': 2, ' ': 1, 'w': 1, 'r': 1, 'd': 1} >>> hist = {} >>> for ltr in s: hist[ltr] = hist.get(ltr, 0) + 1 ... >>> hist {'h': 1, 'e': 1, 'l': 3, 'o': 2, ' ': 1, 'w': 1, 'r': 1, 'd': 1} >>> hist = {} >>> for ltr in s: ... if ltr in hist: hist[ltr] += 1 ... else: hist[ltr] = 1 ... >>> hist {'h': 1, 'e': 1, 'l': 3, 'o': 2, ' ': 1, 'w': 1, 'r': 1, 'd': 1} >>> hist = {} >>> for ltr in s: ... try: hist[ltr] += 1 ... except KeyError: hist[ltr] = 1 ... >>> hist {'h': 1, 'e': 1, 'l': 3, 'o': 2, ' ': 1, 'w': 1, 'r': 1, 'd': 1} A Counter shows the values ranked, all the others retain insertion order, but they all get to the same result. It seems *very* strange to have an exercise like this without looping. That seems counterproductive. But if you're expecting them to not use loops, you'll want to also be very clear about what other features they're allowed to use - or alternatively, stipulate what they ARE allowed to use, eg "Use only indexing and the get() method". ChrisA -- https://mail.python.org/mailman/listinfo/python-list