Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

Python Functional Python The Lambda Lambada Recursion

mattamorphic
PLUS
mattamorphic
Courses Plus Student 6,641 Points

Functional Python : Recursion

I'm a little lost, when I run this code on my local instance I get the six element set back from the data structure no problem at all - when I try and run it in the challenge I get the old "bummer it didn't work" - not sure what's gone on here - am I missing something?

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'])
    [prereqs(requisite, pres) for requisite in data['prereqs']]
    return pres
mattamorphic
mattamorphic
Courses Plus Student 6,641 Points

As a reference point printing that from the command line gives :

set(['Flask Basics', 'Python Collections', 'Object-Oriented Python', 'Python Basics', 'Setting Up a Local Python Environment', 'Django Basics'])

Chris Grazioli
Chris Grazioli
31,225 Points

trying to painfully get through this last course... killing me SO: pres= pres or set()

what is this? assuming alot... pres (not knowing what the heck its even referring to) is itself if passed into the function as an argument, or none by default.....?

11 Answers

Chris Freeman
MOD
Chris Freeman
Treehouse Moderator 68,423 Points

I was also initially stumped on this one until I realized the desired solution is only the prerequisites and does not include the top-level course title. That is, the top course isn't a prerequisite of itself. Solution contains 5 courses.

You may rework your code on your own, if you wish, or here is the solution that worked for me:

def prereqs(data, pres=None):
    pres = pres or set()
    # for each prereq in this courses' prereqs...
    for prereq in data['prereqs']:
        # add title of this prereq course, then...
        pres.add(prereq['title'])
        # use recursive call to find further prerequisites of this
        # course, if any
        prereqs(prereq, pres)
    # return current 
    return pres
Chris Grazioli
Chris Grazioli
31,225 Points

So what is pres referring to.... is that just a bastardized way of being lazy and not wrtiting out prerequisites... if not I really have no clue as to what this function is doing. That assuming that prereqs() is taking courses for data and any prerequisites already know for arguments

Richard Li
Richard Li
9,751 Points

Hi Chris,

For this ancient question please indulge a new-comer a question about this:

Since in each recursive call, the local pres value is returned but not received by any name of the outer call, how did the outer-most finally pres get all the added value?

For the outer-most function, the only time pres gets assigned is at the empty set.

Chris Freeman
Chris Freeman
Treehouse Moderator 68,423 Points

Good question Richard Li!

Python passes references to objects when calling functions. So this call:

        prereqs(prereq, pres)

is calling prereqs to find the subsequent prerequisites with the for loop variable prereq and passing the reference to the object pres.

Since the reference pres points to the same object as the in the top level call to the function preteqs, all recursive executions of the line:

        pres.add(prereq[title])

modifes the same pres object! When all the recursion is complete, the top level reference to pres will also have all the added titles. Very cool!

Post back if you need more help. Good luck!!!

Kenneth Love
STAFF
Kenneth Love
Treehouse Guest Teacher

I've updated the prompt a bit. Hopefully that will clear up some confusion.

Chris Freeman
Chris Freeman
Treehouse Moderator 68,423 Points

Thanks Kenneth for the Labor Day help. I think there might still be some confusion. Looking at the new prompt

"Finish the prereqs function so that it recursively finds all of the prerequisite course titles in courses (like "Object-Oriented Python" is a prerequisite for "Django Basics"). You should add() the title to the pres set and then call prereqs again with the child courses. In the end, return the prereqs set."

The last sentence, if read on its own, could still be misread as: "You should add() the [top course] title to the pres set and then call prereqs again...."

If I could suggest one more tweak:

"You should add() the [prerequisite] title to the pres set and then call prereqs again ....", or

"[For each prerequisite y]ou should add() the title to the pres set and then call prereqs again...."

Thanks, Chris "The Overly Analytical"

Chris Grazioli
Chris Grazioli
31,225 Points

SPOON FEED ME: what is the function supposed to actually do and receive? looks like you want it to blow through some dict called courses that contains a count, title, and prereqs which corresponds to a list of dicts

I'm looking at the function they want us to finish. the arguments are data and pres=none... what are they. It looks like data will be the courses? and pres ( is a very confusing way to pass in pre-requisites for the courses) in data ??? Not at all sure what thats doing?

Kenneth Love
Kenneth Love
Treehouse Guest Teacher

The function gets a dictionary of courses, some of which have prerequisites. The function needs to return a set of all of the prerequisites. You have to do this in a recursive manner. data would be all of the courses, pres is the current set of prerequisites.

David Lin
David Lin
35,864 Points

Thought I'd share something I learned while figuring out this challenge ...

Originally my answer was:

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

... which is the same as the accepted answer above, except I was passing back pres within the recursive calls, whereas the accepted answer did not.

      pres = prereqs(prereq, pres)

I realized that must be because the reference of pres was being passed by value.

So, I tried calling prereqs with the outer argument already initialized to an empty set, expecting that it would likewise contain the complete set after the function completed. However, much to my surprise, it was still empty!

aset = set()
prereqs(courses, aset)
print(aset) # still empty!

I finally realized it was empty because of the first line in the function:

    pres = pres or set()

Because I had passed in an empty set, it failed this condition, and the local reference of pres was rebound to another empty set, and the outer variable had no way of knowing, and thus still continued to point to the empty set outside of the function, even though the local variable was being populated as expected.

To solve, I either had to remove the condition, or make the outer set non-empty before calling prereqs:

aset = set()
aset.add('')
prereqs(courses, aset)
print(aset) # aset is now the complete set (plus an empty string)
Brady Huang
Brady Huang
16,360 Points

I think "it failed this condition" is not precise, because statement

pres = pres or set()

never fails, it just choose from the value which is not empty. So when you choose from pres = set() or set(), it wouldn't return the set you pass into this function as argument.

Chris Grazioli
Chris Grazioli
31,225 Points

Kenneth Love According to the docs Set is deprecated since 2.6, why do they just all of a sudden show up at the end of this course?

Kenneth Love
Kenneth Love
Treehouse Guest Teacher

Uh, what? Set is not deprecated at all and likely never will be.

thanks for the help here; needed it - though still a bit unclear why we're using add() here and not append().

Chris Freeman
Chris Freeman
Treehouse Moderator 68,423 Points

The method append() is used for lists. In this code we are using sets which do not have an append method:

>>> s = set()
>>> s.append("foo")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'set' object has no attribute 'append'

See "help(set)" or the docs

Thanks for the explanation, Chris. Revisiting the set data structure documentation.

Chris Grazioli
Chris Grazioli
31,225 Points

I'm trying not to get totally frustrated with this whole course here right at the end, but when did we even touch on add() or pres and set()? I rewatched a bunch of videos looking for all this stuff and I'm lost ?

Kenneth Love
Kenneth Love
Treehouse Guest Teacher

pres is an argument to the function. set is a data type, like list, and you can create one by using the set() function, like you would with lists or strings or dicts.

sets have a method named add that adds things to the set.

Chris Grazioli
Chris Grazioli
31,225 Points

Kenneth Love

this is where the docs fail me everytime.... search set() get back a bunch of nonsense

Kenneth Love
Kenneth Love
Treehouse Guest Teacher

Well, it's a common word, especially in programming. It's a data structure, it's an action ("set the variable to 5"), it's likely a method on some class or other. One of the challenges of tying programming languages to spoken languages, I guess.

Reading your comments, I think you need to take a step back from this challenge and look at it from another angle. It's a recursive challenge, so the function is going to be called again and again with, ideally, smaller and smaller sets (ha!) of data. Ignore all of that. How would you solve this with for loops and ifs?

Richard Li
Richard Li
9,751 Points

It's been a while since this post got any attention..

My questions is that since in each recursive call, the local pres value is returned but not received, how did the outer-most pres get all the value??

David Lin
David Lin
35,864 Points

Richard Li, it is because pres's reference is passed into each call. So, inside each recursive call, each update is effectively applied to the outer pres. The exception is when the inner pres is re-assigned to a new set based on the pres = pres or set() condition check ... then the outer pres won't be updated inside the function, because the reference to it is no longer to the outer pres. (See my comment above.)

Chris Freeman
Chris Freeman
Treehouse Moderator 68,423 Points

It is misleading to use the phrase “call by value” as this does not happen in Python. Python passes references to objects.

Because a reference to the current pres is passed into each recursion. The recursive modifications all operate on the same object.

Because of the passed pres references, the return pres is not needed during recursion. The return is only needed when the recursion is finished and the top level exits. This final return is what sends back the solution reference to the original calling statement.

Chris Freeman
Chris Freeman
Treehouse Moderator 68,423 Points

Since all the recursive calls include a pres argument, the pres = pres or set() would only create a new set at the top-level call to prereqs and never during an inner iteration.

David Lin
David Lin
35,864 Points

Richard Li, it is because pres's reference is passed into each call by value. So, inside each recursive call, each update is effectively applied to the outer pres. The exception is when the inner pres is re-assigned to a new set based on the pres = pres or set() condition check ... then the outer pres won't be updated inside the function, because the reference to it is no longer to the outer pres. (See my comment above.)

Chris,

Thanks ... what I meant is that references are passed by value for each call of the function, that is, a copy of the reference value is passed into the function. That's why when the local variable is reassigned to a different object, the global variable is not affected.

Effectively, (quoting Python parameter passing )

This means that Python initially behaves like call-by-reference, but as soon as we are changing the value of such a variable, Python "switches" to call-by-value.

But I can see how my original wording could be confusing, as it has the awkward phrasing "call by value," which taken out of context can be misleading, as you said. I'll edit my original comment to clarify, thanks.

Chris Freeman
Chris Freeman
Treehouse Moderator 68,423 Points

I apologize if I’m be overly specific. There isn’t any actual call-by-value. Python will seem to behave as a call-by-value when a global value or parameter is assigned to. It is this assignment to a variable of the same name as a parameter or global value that creates this local variable of the same name thereby protecting the global object from being overwritten.

Note that modifying a passed in object is not creating anything, so it’s the global or passed in object is modified.

Since this code only modifies the pres set, the global object is changed and no new local object is created.

Looking at the following code. It prints the value and ID of the passed values. Note it is at the increment when the local variable gets created and the object passed in isn’t affected by the increment (notice the new id value). The append modifies param2 so no new local object is created.

def func(param1, param2):
    print("2: ", param1, id(param1) , param2, id(param2))
    param1 += 1
    param2.append(param1)
    print("3: ", param1, id(param1) , param2, id(param2))

value1 = 6
value2 = []
print('1: ', value1, id(value1), value2, id(value2))
func(value1, value2)
print('4: ', value1, id(value1), value2, id(value2))

value1 += 10 

print('5: ', value1, id(value1), value2, id(value2))
func(value1, value2)
print('6: ', value1, id(value1), value2, id(value2))

# produces the output:
# 1:  60 4385563008 [] 4693608776
# 2:  60 4385563008 [] 4693608776
# 3:  61 4385563040 [61] 4693608776
# 4:  60 4385563008 [61] 4693608776
# 5:  70 4385563328 [61] 4693608776
# 2:  70 4385563328 [61] 4693608776
# 3:  71 4385563360 [61, 71] 4693608776
# 6:  70 4385563328 [61, 71] 4693608776
Richard Li
Richard Li
9,751 Points

Chris Freeman and David Lin@davejlin

Thank you so so much!! I now have a better understanding on the python recursion. It's like passing a pointer of the original set and all the appendings are happening at the same place.

Erika Suzuki
Erika Suzuki
20,299 Points

I just ran @chrisfreeman3 solution and I'm confused that it's accepted as a correct answer. The function returned incomplete prereqs.

{'Python Collections', 'Setting Up a Local Python Environment',
    'Object-Oriented Python', 'Python Basics', 'Flask Basics'}

While my solution with all the prereqs in it was wrong. Any thoughts? @kennethlove

def prereqs(data, pres=None):
    pres = pres or set()
    pres.add(data.get('title'))
    if data.get('prereqs'):
        for x in data.get('prereqs'):
            prereqs(x, pres)
        return pres

My solution's output:

{'Flask Basics', 'Django Basics', 'Setting Up a Local Python Environment',
    'Python Collections', 'Python Basics', 'Object-Oriented Python'}
Chris Freeman
Chris Freeman
Treehouse Moderator 68,423 Points

Good question! The challenge wording asks you to find “finds all of the prerequisite course titles in courses”, meaning all courses that are a prerequisite of another course. While “Django Basics” has prerequisites, itself is not a prerequisite of another course.

Post back if you need more help. Good luck!!!

Erika Suzuki
Erika Suzuki
20,299 Points

@chrisfreeman3 aha! I see. I got what you mean! Thanks