Welcome to the Treehouse Community

The Treehouse Community is a meeting place for developers, designers, and programmers of all backgrounds and skill levels to get support. Collaborate here on code errors or bugs that you need feedback on, or asking for an extra set of eyes on your latest project. Join thousands of Treehouse students and alumni in the community today. (Note: Only Treehouse students can comment or ask questions, but non-students are welcome to browse our conversations.)

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and a supportive community. Start your free trial today.

Python Functional Python The Lambda Lambada Recursion

Should we exclude the top title out of the prerequisite set?

Is the top item's title 'Django Basics' expected not to be in the output of prereqs()? The following code fails because of that, or else?

courses.py
courses = {'count': 2,
           'title': 'Django Basics',
           'prereqs': [{'count': 3,
                     'title': 'Object-Oriented Python',
                     'prereqs': [{'count': 1,
                               'title': 'Python Collections',
                               'prereqs': [{'count':0,
                                         'title': 'Python Basics',
                                         'prereqs': []}]},
                              {'count': 0,
                               'title': 'Python Basics',
                               'prereqs': []},
                              {'count': 0,
                               'title': 'Setting Up a Local Python Environment',
                               'prereqs': []}]},
                     {'count': 0,
                      'title': 'Flask Basics',
                      'prereqs': []}]}


def prereqs(data, pres=None):
    pres = pres or set()
    pres.add(data['title'])
    return reduce(lambda acc, x: prereqs(x, acc), data['prereqs'], pres)

2 Answers

Chris Freeman
MOD
Chris Freeman
Treehouse Moderator 67,989 Points

Yes. "Django Basics" is not a prerequisite.

Instead of adding title then reducing, try doing for each prerequisite add then reduce.

Thanks, Chris. Now I changed as below to drop the top title, alas it fails. Why?

def prereqs(data, pres=None):
    if pres == None:
      pres = set()
    else:
      pres.add(data['title'])
    return reduce(lambda acc, x: prereqs(x, acc), data['prereqs'], pres)

[MOD: added ```python markdown formatting -cf]

Chris Freeman
Chris Freeman
Treehouse Moderator 67,989 Points

You code passes for me... after I remember to import reduce

from functools import reduce

You code is running a double recursion in that it creates a new a function using lambda then calls prereqs using that function. It can be unrolled by using a for-loop:

Here is my alternative solution.

def prereqscf(data, pres=None):
    pres = pres or set()
    for course in data['prereqs']:
        pres.add(course['title'])
        pres = prereqscf(course, pres)
    return pres

Chris, I am a bit confused by the line

pres = prereqscf(course, pres)

in your alternate solution below - specifically, with how the syntax corresponds to what Python is doing behind the scenes. I have stepped through the code line by line a few times trying to wrap my head around the (double) recursion and I noticed that the variable pres always points to the set object and never to instances of the function object. I also noticed that removing the assignment from that line - i.e.

prereqscf(course, pres)

does not seem to change the behavior of the program at all. If you could shed any light on this for me, I would be very grateful! Thanks.

Chris Freeman
Chris Freeman
Treehouse Moderator 67,989 Points

Joshua, Yes, the two variations do provide the same results. The difference between assigned the recursive call to pres or simply calling prereqs() is more about clarity of coding.

Since pres is a set (container), when it is passed as an argument, certain operations will operate on the container in-place and not create a local version of the variable. set.append() is one of these functions. So when the set is appended, it is affecting pres everywhere since they are all pointing to the same object.

Because the function returns pres, the local value within this iteration will already be the same as the returned value making the assignment redundant. For clarity of coding, if a recursive function returns a value, I prefer to see how that value is used explicitly verses having to realize that the recursive call has a side-effect of updating pres.

This is a good practice to follow since not all objects will behave the same as a set in a recursive manner (such as string operations).

Adding a print to show the "id" of pres it will show that the object is the same throughout the execution:

def prereqscf(data, pres=None):
    pres = pres or set()
    print("id: {}, pres: {}".format(id(pres), pres))
    for course in data['prereqs']:
        pres.add(course['title'])
        pres = prereqscf(course, pres)
    return pres

Even the final results points to the same object:

$ python -i prereq2.py 
>>> results = prereqscf(courses)
id: 139714400522024, pres: set()
id: 139714400522024, pres: {'Object-Oriented Python'}
id: 139714400522024, pres: {'Object-Oriented Python', 'Python Collections'}
id: 139714400522024, pres: {'Python Basics', 'Object-Oriented Python', 'Python Collections'}
id: 139714400522024, pres: {'Python Basics', 'Object-Oriented Python', 'Python Collections'}
id: 139714400522024, pres: {'Setting Up a Local Python Environment', 'Python Basics', 'Object-Oriented Python', 'Python Collections'}
id: 139714400522024, pres: {'Setting Up a Local Python Environment', 'Python Basics', 'Flask Basics', 'Object-Oriented Python', 'Python Collections'}
>>> id(results)
139714400522024

Removing the assignment of the return value to pres produces the same output.

That makes sense. Thank you Chris!

Thank you, Chris! Yes, that's it!