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 trialmattamorphic
Courses Plus Student 6,641 PointsFunctional 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 = {'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
Chris Grazioli
31,225 Pointstrying 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
Treehouse Moderator 68,457 PointsI 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
31,225 PointsSo 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
9,751 PointsHi 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
Treehouse Moderator 68,457 PointsGood 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
Treehouse Guest TeacherI've updated the prompt a bit. Hopefully that will clear up some confusion.
Chris Freeman
Treehouse Moderator 68,457 PointsThanks 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 Freeman
Treehouse Moderator 68,457 PointsYou are the Best!
Chris Grazioli
31,225 PointsSPOON 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
Treehouse Guest TeacherThe 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
35,864 PointsThought 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
16,360 PointsI 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
31,225 PointsKenneth 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
Treehouse Guest TeacherUh, what? Set is not deprecated at all and likely never will be.
kjarsenal
4,402 Pointsthanks for the help here; needed it - though still a bit unclear why we're using add() here and not append().
Chris Freeman
Treehouse Moderator 68,457 PointsThe 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
kjarsenal
4,402 PointsThanks for the explanation, Chris. Revisiting the set data structure documentation.
Chris Grazioli
31,225 PointsI'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
Treehouse Guest Teacherpres
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.
set
s have a method named add
that adds things to the set.
Chris Grazioli
31,225 Pointsthis is where the docs fail me everytime.... search set() get back a bunch of nonsense
Kenneth Love
Treehouse Guest TeacherWell, 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 if
s?
Richard Li
9,751 PointsIt'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
35,864 PointsRichard 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
Treehouse Moderator 68,457 PointsIt 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
Treehouse Moderator 68,457 PointsSince 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
35,864 PointsRichard 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
Treehouse Moderator 68,457 PointsI 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
9,751 PointsChris 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
20,299 PointsI 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
Treehouse Moderator 68,457 PointsGood 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
20,299 Points@chrisfreeman3 aha! I see. I got what you mean! Thanks
mattamorphic
Courses Plus Student 6,641 Pointsmattamorphic
Courses Plus Student 6,641 PointsAs 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'])