https://www.ocf.berkeley.edu/~shidi/cs61a/w/api.php?action=feedcontributions&user=Jeffreylu017&feedformat=atomCS 61A Wiki - User contributions [en]2022-05-20T12:54:38ZUser contributionsMediaWiki 1.22.6https://www.ocf.berkeley.edu/~shidi/cs61a/wiki/Past_examsPast exams2015-07-02T23:31:36Z<p>Jeffreylu017: </p>
<hr />
<div>This is a list of '''past exams'''.<br />
<br />
{| class="wikitable" border="1"<br />
|-<br />
! Semester<br />
! MT1<br />
! MT2<br />
! Final<br />
|-<br />
! Fall 2011<br />
| {{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-fa11-midterm1.pdf|exam}} ({{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-fa11-midterm1-solutions.pdf|sol}})<br />
| {{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-fa11-midterm2.pdf|exam}} ({{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-fa11-midterm2-solutions.pdf|sol}})<br />
| {{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-fa11-final.pdf|exam}} ({{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-fa11-final-solutions.pdf|sol}})<br />
|-<br />
! Summer 2012<br />
| {{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-su12-midterm1.pdf|exam}} ({{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-su12-midterm1-solutions.pdf|sol}})<br />
| {{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-su12-midterm2.pdf|exam}} ({{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-su12-midterm2-solutions.pdf|sol}})<br />
| {{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-su12-final.pdf|exam}} ({{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-su12-final-solutions.pdf|sol}})<br />
|-<br />
! Fall 2012<br />
| {{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-fa12-midterm1.pdf|exam}} ({{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-fa12-midterm1-solutions.pdf|sol}})<br />
{{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-fa12-midterm1-alt.pdf|alt}} ({{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-fa12-midterm1-alt-solutions.pdf|sol}})<br />
| {{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-fa12-midterm2.pdf|exam}} ({{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-fa12-midterm2-solutions.pdf|sol}})<br />
{{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-fa12-midterm2-alt.pdf|alt}} ({{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-fa12-midterm2-alt-solutions.pdf|sol}})<br />
| {{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-fa12-final.pdf|exam}} ({{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-fa12-final-solutions.pdf|sol}})<br />
|-<br />
! Spring 2013<br />
| {{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-sp13-midterm1.pdf|exam}} ({{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-sp13-midterm1-solutions.pdf|sol}})<br />
| {{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-sp13-midterm2.pdf|exam}} ({{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-sp13-midterm2-solutions.pdf|sol}})<br />
| {{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-sp13-final.pdf|exam}} ({{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-sp13-final-solutions.pdf|sol}})<br />
|-<br />
! Summer 2013<br />
| {{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-su13-midterm1.pdf|exam}} ({{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-su13-midterm1-solutions.pdf|sol}})<br />
| {{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-su13-midterm2.pdf|exam}} ({{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-su13-midterm2-solutions.pdf|sol}})<br />
| {{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-su13-final.pdf|exam}} ({{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-su13-final-solutions.pdf|sol}})<br />
|-<br />
! Fall 2013<br />
| {{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-fa13-midterm1.pdf|exam}} ({{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-fa13-midterm1-solutions.pdf|sol}})<br />
| {{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-fa13-midterm2.pdf|exam}} ({{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-fa13-midterm2-solutions.pdf|sol}})<br />
| {{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-fa13-final.pdf|exam}} ({{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-fa13-final-solutions.pdf|sol}})<br />
|-<br />
! Spring 2014<br />
| {{Plain link|http://inst.eecs.berkeley.edu/~cs61a/su14/exams/sp14-test1-solutions.pdf|sol}}<br />
| {{Plain link|http://inst.eecs.berkeley.edu/~cs61a/su14/exams/sp14-test2-solutions.pdf|sol}}<br />
|<br />
|-<br />
! Summer 2014<br />
| {{Plain link|http://www.ocf.berkeley.edu/~shidi/cs61a/61a-su14-midterm1.pdf|exam}} ({{Plain link|http://www.ocf.berkeley.edu/~shidi/cs61a/61a-su14-midterm1-sol.pdf|sol}})<br />
{{Plain link|https://docs.google.com/a/berkeley.edu/document/d/1DYjptJtKeWpGuWcFWADza0mADC3CMEaX3778dZpolZU/|env diagrams}}<br />
| {{Plain link|https://d1b10bmlvqabco.cloudfront.net/attach/hxmdrtyyder49a/hkppg6doems102/i3fcek7u13ee/61asu14midterm2.pdf|exam}} ({{Plain link|https://d1b10bmlvqabco.cloudfront.net/attach/hxmdrtyyder49a/hkppg6doems102/i3fca2dak3bj/61asu14midterm2_solutions.pdf|sol}})<br />
{{Plain link|https://d1b10bmlvqabco.cloudfront.net/attach/hx2jz3h2i112h8/hkppg6doems102/i1sdqa1ejx6r/q3.pdf|(q3 sol)}}<br />
{{Plain link|http://goo.gl/rpb3wy|(q2b sol)}}<br />
| {{Plain link|https://d1b10bmlvqabco.cloudfront.net/attach/hxmdrtyyder49a/hkppg6doems102/i3fcouxga40y/61asu14final.pdf|exam}} ({{Plain link|https://d1b10bmlvqabco.cloudfront.net/attach/hxmdrtyyder49a/hkppg6doems102/i3fd0rep17w8/61asu14final_sol.pdf|sol}})<br />
|-<br />
! Fall 2014<br />
| {{Plain link|http://inst.eecs.berkeley.edu/~cs61a/fa14/assets/pdfs/61a-fa14-mt1.pdf|exam}} ({{Plain link|http://inst.eecs.berkeley.edu/~cs61a/fa14/assets/pdfs/61a-fa14-mt1-solution.pdf|sol}})<br />
| {{Plain link|http://inst.eecs.berkeley.edu/~cs61a/fa14/assets/pdfs/61a-fa14-mt2.pdf|exam}} ({{Plain link|http://inst.eecs.berkeley.edu/~cs61a/fa14/assets/pdfs/61a-fa14-mt2-solution.pdf|sol}})<br />
| {{Plain link|https://www.dropbox.com/s/ahhi0vbtpzfgjk9/CS61A%20Fa2014%20Final.pdf|exam}} ({{Plain link|https://www.dropbox.com/s/gcwqar7e49oixps/cs61A-fa2014-final-Denero-soln.pdf|sol}})<br />
|-<br />
! Spring 2015<br />
| {{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp15/assets/pdfs/61a-sp15-mt1.pdf|exam}} ({{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp15/assets/pdfs/61a-sp15-mt1-solution.pdf|sol}})<br />
| {{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp15/assets/pdfs/61a-sp15-mt2.pdf|exam}} ({{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp15/assets/pdfs/61a-sp15-mt2-solution.pdf|sol}})<br />
| {{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp15/assets/pdfs/61a-sp15-final.pdf|exam}} ({{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp15/assets/pdfs/61a-sp15-final-solution.pdf|sol}})<br />
|}<br />
<br />
== Other Exams ==<br />
* [http://inst.eecs.berkeley.edu/~cs61a/su14/exams/61a-su14-midterm3.pdf Summer 2014 Exam 3] ([http://inst.eecs.berkeley.edu/~cs61a/su14/exams/61a-su14-midterm3_sol.pdf Solutions])<br />
<br />
[[Category:Logistical articles]]</div>Jeffreylu017https://www.ocf.berkeley.edu/~shidi/cs61a/wiki/Past_examsPast exams2015-07-02T23:29:36Z<p>Jeffreylu017: </p>
<hr />
<div>This is a list of '''past exams'''.<br />
<br />
{| class="wikitable" border="1"<br />
|-<br />
! Semester<br />
! MT1<br />
! MT2<br />
! Final<br />
|-<br />
! Fall 2011<br />
| {{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-fa11-midterm1.pdf|exam}} ({{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-fa11-midterm1-solutions.pdf|sol}})<br />
| {{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-fa11-midterm2.pdf|exam}} ({{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-fa11-midterm2-solutions.pdf|sol}})<br />
| {{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-fa11-final.pdf|exam}} ({{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-fa11-final-solutions.pdf|sol}})<br />
|-<br />
! Summer 2012<br />
| {{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-su12-midterm1.pdf|exam}} ({{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-su12-midterm1-solutions.pdf|sol}})<br />
| {{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-su12-midterm2.pdf|exam}} ({{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-su12-midterm2-solutions.pdf|sol}})<br />
| {{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-su12-final.pdf|exam}} ({{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-su12-final-solutions.pdf|sol}})<br />
|-<br />
! Fall 2012<br />
| {{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-fa12-midterm1.pdf|exam}} ({{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-fa12-midterm1-solutions.pdf|sol}})<br />
{{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-fa12-midterm1-alt.pdf|alt}} ({{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-fa12-midterm1-alt-solutions.pdf|sol}})<br />
| {{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-fa12-midterm2.pdf|exam}} ({{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-fa12-midterm2-solutions.pdf|sol}})<br />
{{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-fa12-midterm2-alt.pdf|alt}} ({{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-fa12-midterm2-alt-solutions.pdf|sol}})<br />
| {{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-fa12-final.pdf|exam}} ({{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-fa12-final-solutions.pdf|sol}})<br />
|-<br />
! Spring 2013<br />
| {{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-sp13-midterm1.pdf|exam}} ({{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-sp13-midterm1-solutions.pdf|sol}})<br />
| {{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-sp13-midterm2.pdf|exam}} ({{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-sp13-midterm2-solutions.pdf|sol}})<br />
| {{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-sp13-final.pdf|exam}} ({{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-sp13-final-solutions.pdf|sol}})<br />
|-<br />
! Summer 2013<br />
| {{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-su13-midterm1.pdf|exam}} ({{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-su13-midterm1-solutions.pdf|sol}})<br />
| {{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-su13-midterm2.pdf|exam}} ({{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-su13-midterm2-solutions.pdf|sol}})<br />
| {{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-su13-final.pdf|exam}} ({{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-su13-final-solutions.pdf|sol}})<br />
|-<br />
! Fall 2013<br />
| {{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-fa13-midterm1.pdf|exam}} ({{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-fa13-midterm1-solutions.pdf|sol}})<br />
| {{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-fa13-midterm2.pdf|exam}} ({{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-fa13-midterm2-solutions.pdf|sol}})<br />
| {{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-fa13-final.pdf|exam}} ({{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp14/exams/61a-fa13-final-solutions.pdf|sol}})<br />
|-<br />
! Spring 2014<br />
| {{Plain link|http://inst.eecs.berkeley.edu/~cs61a/su14/exams/sp14-test1-solutions.pdf|sol}}<br />
| {{Plain link|http://inst.eecs.berkeley.edu/~cs61a/su14/exams/sp14-test2-solutions.pdf|sol}}<br />
|<br />
|-<br />
! Summer 2014<br />
| {{Plain link|http://www.ocf.berkeley.edu/~shidi/cs61a/61a-su14-midterm1.pdf|exam}} ({{Plain link|http://www.ocf.berkeley.edu/~shidi/cs61a/61a-su14-midterm1-sol.pdf|sol}})<br />
{{Plain link|https://docs.google.com/a/berkeley.edu/document/d/1DYjptJtKeWpGuWcFWADza0mADC3CMEaX3778dZpolZU/|env diagrams}}<br />
| {{Plain link|https://d1b10bmlvqabco.cloudfront.net/attach/hxmdrtyyder49a/hkppg6doems102/i3fcek7u13ee/61asu14midterm2.pdf|exam}} ({{Plain link|https://d1b10bmlvqabco.cloudfront.net/attach/hxmdrtyyder49a/hkppg6doems102/i3fca2dak3bj/61asu14midterm2_solutions.pdf|sol}})<br />
{{Plain link|https://d1b10bmlvqabco.cloudfront.net/attach/hx2jz3h2i112h8/hkppg6doems102/i1sdqa1ejx6r/q3.pdf|(q3 sol)}}<br />
{{Plain link|http://goo.gl/rpb3wy|(q2b sol)}}<br />
| {{Plain link|https://d1b10bmlvqabco.cloudfront.net/attach/hxmdrtyyder49a/hkppg6doems102/i3fcouxga40y/61asu14final.pdf|exam}} ({{Plain link|https://d1b10bmlvqabco.cloudfront.net/attach/hxmdrtyyder49a/hkppg6doems102/i3fd0rep17w8/61asu14final_sol.pdf|sol}})<br />
|-<br />
! Fall 2014<br />
| {{Plain link|http://inst.eecs.berkeley.edu/~cs61a/fa14/assets/pdfs/61a-fa14-mt1.pdf|exam}} ({{Plain link|http://inst.eecs.berkeley.edu/~cs61a/fa14/assets/pdfs/61a-fa14-mt1-solution.pdf|sol}})<br />
| {{Plain link|http://inst.eecs.berkeley.edu/~cs61a/fa14/assets/pdfs/61a-fa14-mt2.pdf|exam}} ({{Plain link|http://inst.eecs.berkeley.edu/~cs61a/fa14/assets/pdfs/61a-fa14-mt2-solution.pdf|sol}})<br />
| {{Plain link|https://www.dropbox.com/s/ahhi0vbtpzfgjk9/CS61A%20Fa2014%20Final.pdf|exam}} ({{Plain link|https://www.dropbox.com/s/gcwqar7e49oixps/cs61A-fa2014-final-Denero-soln.pdf|sol}})<br />
|-<br />
! spring 2015<br />
| {{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp15/assets/pdfs/61a-sp15-mt1.pdf|exam}} ({{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp15/assets/pdfs/61a-sp15-mt1-solution.pdf|sol}})<br />
| {{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp15/assets/pdfs/61a-sp15-mt2.pdf|exam}} ({{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp15/assets/pdfs/61a-sp15-mt2-solution.pdf|sol}})<br />
| {{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp15/assets/pdfs/61a-sp15-final.pdf|exam}} ({{Plain link|http://inst.eecs.berkeley.edu/~cs61a/sp15/assets/pdfs/61a-sp15-final-solution.pdf|sol}})<br />
|}<br />
<br />
== Other Exams ==<br />
* [http://inst.eecs.berkeley.edu/~cs61a/su14/exams/61a-su14-midterm3.pdf Summer 2014 Exam 3] ([http://inst.eecs.berkeley.edu/~cs61a/su14/exams/61a-su14-midterm3_sol.pdf Solutions])<br />
<br />
[[Category:Logistical articles]]</div>Jeffreylu017https://www.ocf.berkeley.edu/~shidi/cs61a/wiki/Towers_of_HanoiTowers of Hanoi2014-07-06T05:06:37Z<p>Jeffreylu017: /* Base Case */</p>
<hr />
<div>{{Purge}}<br />
<br />
'''Tower of Hanoi''' is a puzzle that involves three pegs and <code>n</code> disks. The each disk is of a unique size, with smaller disks always going on top of larger disks on a peg. At the beginning of the puzzle, all the disks are stacked on one starting peg from smallest (on the top) to the largest (on the bottom). The goal is to make the necessary valid moves to move the disks from the <code>start</code> peg to the desired <code>end</code> peg.<br />
<br />
Valid moves are such that<br />
* Only one disk may be moved at a time.<br />
* Each move consists of taking the upper disk from one of the rods and sliding it onto another rod, on top of the other disks that may already be present on that rod.<br />
* No disk may be placed on top of a smaller disk.<br />
<br />
<br />
== Problem ==<br />
The problem can be stated as such:<br />
<blockquote>Complete the definition of <code>towers_of_hanoi</code> which prints out the steps to solve this puzzle for any number of <code>n</code> disks starting from the <code>start</code> rod and moving them to the <code>end</code> rod:<br />
<source lang="python"><br />
def towers_of_hanoi(n, start, end):<br />
"""Print the moves required to solve the towers of hanoi game if we start<br />
with n disks on the start pole and want to move them all to the end pole.<br />
<br />
The game is to assumed to have 3 poles (which is traditional).<br />
<br />
>>> towers_of_hanoi(1, 1, 3)<br />
Move 1 disk from rod 1 to rod 3<br />
>>> towers_of_hanoi(2, 1, 3)<br />
Move 1 disk from rod 1 to rod 2<br />
Move 1 disk from rod 1 to rod 3<br />
Move 1 disk from rod 2 to rod 3<br />
>>> towers_of_hanoi(3, 1, 3)<br />
Move 1 disk from rod 1 to rod 3<br />
Move 1 disk from rod 1 to rod 2<br />
Move 1 disk from rod 3 to rod 2<br />
Move 1 disk from rod 1 to rod 3<br />
Move 1 disk from rod 2 to rod 1<br />
Move 1 disk from rod 2 to rod 3<br />
Move 1 disk from rod 1 to rod 3<br />
"""<br />
"*** YOUR CODE HERE ***"<br />
<br />
<br />
def move_disk(start, end):<br />
print("Move 1 disk from rod", start, "to rod", end)<br />
</source></blockquote><br />
<br />
== Approach ==<br />
=== Overview ===<br />
When we approach a problem recursively, we need to try and break it down into a smaller version of the same problem. Just like a recursive <code>factorial</code> function utilizes the fact that $n!=n\cdot(n-1)!$ to make recursive calls, our approaches to other recursive problems should do something similar. We need a recursive definition, which tells Python how to recurse through this problem, and a base case, which tells Python where to stop recursing.<br />
<br />
=== Base Case ===<br />
So let's start with the base case. The base case should be a case where we do not need to make recursive calls to know what the answer is ($1!=1$, for <code>factorial</code>). In <code>towers_of_hanoi</code>, what is the easiest base case to solve? What should the function do in that case?<br />
<br />
=== Recursive Case ===<br />
Now what about the recursive case? Trying to figure out the algorithm to correctly move each disk is a bit difficult, so let's only deal with the nth disk, or the bottom-most one, and somehow have recursion deal with the other <code>n - 1</code> disks. Here, we have the start of the problem:<br />
<br />
[[File:hanoi0.png]]<br />
<br />
To move all the disks from start to end we need to eventually move disk n. But since we can only move the top-most disk of any tower, we have to first move all the <code>n - 1</code> disks to the offset, so that later we can <br />
move disk <code>n</code> to the <code>end</code>:<br />
<br />
[[File:hanoi1.png]]<br />
<br />
At this point, disk <code>n</code> is free to move, so we can place it at the end:<br />
<br />
[[File:hanoi2.png]]<br />
<br />
And then, we just need to move the <code>n - 1</code> disks to the end as well:<br />
<br />
[[File:hanoi3.png]]<br />
<br />
At this point, the only thing that we may be unsure of is how to move the <code>n - 1</code> disks from the start to the offset. But note exactly what <code>towers_of_hanoi</code> does. The function <code>towers_of_hanoi</code> prints the procedure of how to move <code>n</code> disks from <code>start</code> to the <code>end</code>.<br />
<br />
And already, we've come across a smaller version of this problem! We need to move the n - 1 disks from start to the offset, which in itself is a towers_of_hanoi problem, only with a different destination, and one less disk. We encounter the problem again when after we move the nth disk, we need to move the n - 1 disks again to the end as well.<br />
<br />
=== The Trick ===<br />
Of course, we're not done defining towers_of_hanoi yet, but we know what the goal of it is anyways. Once we're finished, it should work as we had intended it to, so let's take a recursive leap of faith, and go ahead and make recursive calls to <code>towers_of_hanoi</code>to move the <code>n - 1</code> disks out of our way. The only thing that may be tricky is to figure out which peg is the "offset" peg. Consider the following:<br />
{|<br />
|-<br />
! start !! end !! offset<br />
|-<br />
| 1 || 3 || 2<br />
|-<br />
| 3 || 1 || 2<br />
|-<br />
| 1 || 2 || 3<br />
|-<br />
| 2 || 1 || 3<br />
|-<br />
| 2 || 3 || 1<br />
|-<br />
| 3 || 2 || 1<br />
|-<br />
|}<br />
<br />
After a lot of thought, one might come up with <code>offset = 6 - start - end</code>. This is just an expression that matches the table above, so don't worry if you didn't come up with this.</div>Jeffreylu017https://www.ocf.berkeley.edu/~shidi/cs61a/wiki/GuidesGuides2014-06-21T03:01:58Z<p>Jeffreylu017: /* Higher-order functions */</p>
<hr />
<div>== Higher-order functions ==<br />
=== Difference between currying and nested <code>def</code> functions ===<br />
<br />
'''Student Question'''<br />
<br />
What's the difference between currying and nested def functions? Don't they both have multiple arguments/functions within?<br />
<br />
'''Student Answer'''<br />
<br />
According to our [http://composingprograms.com/pages/16-higher-order-functions.html#currying online reading], curry is to "use higher-order functions to convert a function that takes multiple arguments into a chain of functions that each take a single argument. More specifically, given a function <code>f(x, y)</code>, we can define a function <code>g</code> such that <code>g(x)(y)</code> is equivalent to <code>f(x, y)</code>."<br />
I think nested def functions is the a to realize the aim of "currying". You can also nest def function with aim other than "currying". For example:<br />
<pre>def sum_fact(n):<br />
def fact(n):<br />
i, product = 1, 1<br />
while i &lt;= n:<br />
product *= i<br />
i += 1<br />
i, sum = 1, 0<br />
while i&lt;= n:<br />
sum += fact(i)<br />
return sum<br />
<br />
</pre><br />
In this function, fact only serves as a helper function to calculate factorial instead of currying, namely breaking down <code>f(x,y)</code> to <code>f(x)(y)</code>.<br />
<br />
== Environment diagrams ==<br />
=== Environment diagram Rules ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=131 Source: Spring 2014 Piazza (131)]<br />
<br />
Environment Diagrams are very important in our understanding of how the computer interprets our code.<br />
<br />
'''We will test you on this in every exam.'''<br />
<br />
'''It will never go away.'''<br />
<br />
'''Given that, master it as quickly as you can! :)'''<br />
<br />
Below are the rules I follow when drawing environment diagrams. If you understand and faithfully follow these rules when drawing them, you'll never get them wrong.<br />
<br />
One thing you haven't learned yet is nonlocal. You can skip that particular step for now (step 2 of Assignment).<br />
<br />
Post here if you have any questions!<br />
<br />
You can also take a look at this link for some examples of environment diagrams: http://albertwu.org/cs61a/notes/environments<br />
<br />
For a different perspective on the rules, check out: http://markmiyashita.com/cs61a/sp14/environment_diagrams/rules_of_environment_diagrams/<br />
<br />
A handout with detailed instructions on drawing environment diagrams is also available here (linked on the bottom of the course homepage): http://inst.eecs.berkeley.edu/~cs61a/sp14/pdfs/environment-diagrams.pdf<br />
<br />
<pre>Environment Diagram Rules<br />
=========================<br />
<br />
Creating a Function<br />
--------------------<br />
1. Draw the func <name>(<arg1>, <arg2>, ...)<br />
2. The parent of the function is wherever the function was defined<br />
(the frame we're currently in, since we're creating the function).<br />
3. If we used def, make a binding of the name to the value in the current frame.<br />
<br />
Calling User Defined Functions<br />
------------------------------<br />
1. Evaluate the operator and operands.<br />
2. Create a new frame; the parent is whatever the operator s parent is.<br />
Now this is the current frame.<br />
3. Bind the formal parameters to the argument values (the evaluated operands).<br />
4. Evaluate the body of the operator in the context of this new frame.<br />
5. After evaluating the body, go back to the frame that called the function.<br />
<br />
Assignment<br />
----------<br />
1. Evaluate the expression to the right of the assignment operator (=).<br />
2. If nonlocal, find the frame that has the variable you re looking for,<br />
starting in the parent frame and ending just before the global frame (via<br />
Lookup rules). Otherwise, use the current frame. Note: If there are multiple<br />
frames that have the same variable, pick the frame closest to the current<br />
frame.<br />
3. Bind the variable name to the value of the expression in the identified<br />
frame. Be sure you override the variable name if it had a previous binding.<br />
<br />
Lookup<br />
------<br />
1. Start at the current frame. Is the variable in this frame?<br />
If yes, that's the answer.<br />
2. If it isn't, go to the parent frame and repeat 1.<br />
3. If you run out of frames (reach the Global frame and it's not there), complain.<br />
<br />
Tips<br />
----<br />
1. You can only bind names to values.<br />
No expressions (like 3+4) allowed on environment diagrams!<br />
2. Frames and Functions both have parents.</pre><br />
<br />
== Sequences ==<br />
=== Reversing tuples ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=639 Source: Spring 2014 Piazza (639)]<br />
<br />
'''Student Question'''<br />
<br />
Why does [::-1] tuple work while the tuple [0:3:-1] doesn't?<br />
<br />
I thought the -1 after the second semicolon meant that the interpreter is going to read the indexes "backwards". <br />
<br />
'''Student Answer'''<br />
<br />
The syntax of slicing is <tt>tup[start:end:step]</tt>:<br />
<ul><li>start from index <tt>start</tt> and end just before index <tt>end</tt>, incrementing the index by <tt>step</tt> each time<br />
</li><li>if no <tt>step</tt> is provided, <tt>step</tt> = 1<br />
</li><li>if <tt>step</tt> is positive, default values if not provided: <tt>start</tt> = 0, <tt>end</tt> = <tt>len(tup)</tt><br />
</li><li>if <tt>step</tt> is negative, default values if not provided: <tt>start</tt> = -1, <tt>end</tt> = one position before the start of the string<br />
</li></ul><br />
<br />
<pre>&gt;&gt;&gt; (1, 2, 3)[::-1] # start at index -1, end one position before the start of the string<br />
(3, 2, 1)<br />
&gt;&gt;&gt; (1, 2, 3)[0:3:-1] # start at 0 and go to 3, but step is negative, so this doesn't make sense and an empty tuple is returned<br />
()<br />
</pre><br />
<br />
This is a helpful visualization from http://en.wikibooks.org/wiki/Python_Programming/Strings#Indexing_and_Slicing:<br />
<blockquote><br />
To understand slices, it's easiest not to count the elements themselves. It is a bit like counting not on your fingers, but in the spaces between them. The list is indexed like this:<br />
<pre>Element: 1 2 3 4<br />
Index: 0 1 2 3 4<br />
-4 -3 -2 -1<br />
</pre><br />
</blockquote><br />
More info about slicing at http://stackoverflow.com/a/13005464/2460890.<br />
<br />
=== Slicing with negative step ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=702 Source: Spring 2014 Piazza (702)]<br />
<br />
'''Student Question'''<br />
<br />
if the third example returns an empty tuple because you can't take negative steps from 0 to 4, shouldn't the second example also return an empty tuple?<br />
<br />
Can someone explain why each example returns the respective answers?<br />
<br />
Thanks<br />
<br />
<pre>>>> x= (1,2,3,4)<br />
>>> x[0::-1]<br />
(1,)<br />
>>> x[::-1]<br />
(4, 3, 2, 1)<br />
>>> x[0:4:-1]<br />
()<br />
>>> x[1::-1]<br />
(2, 1)</pre><br />
<br />
'''Instructor Answer'''<br />
<br />
(For reference, the notation is <tt>x[start:end:step]</tt>)<br />
<br />
Python does something a very strange when the step is negative: if you omit the arguments to start and end, Python will fill them with what makes sense for a negative step. In the simple case of <tt>x[::-1]</tt>, Python fills in the start with <tt>len(x)-1</tt> and the end with <tt>-(len(x)+1)</tt>. The end term is strange, but remember that the end term isn't included. We therefore can't use 0, but we can't use -1 either, since that clearly refers to the last element of the tuple. We need to fully wrap the negative index around, to refer to the element "before" the 0th index. This way, Python will start at the end of the tuple and proceed to the beginning of the tuple.<br />
<br />
That's why <tt>x[0:4:-1]</tt> doesn't make sense: how can we start at 0 and end at 4, if we're proceeding backwards?<br />
<br />
And that's why <tt>x[0::-1]</tt> makes sense (albeit, in a strange way): Python is proceeding from the 0 index to the beginning of the list. It includes the start index, which is why you see a 1 pop up.<br />
<br />
Let me know if that was confusing!<br />
<br />
== Recursion ==<br />
=== Recursion visualizer ===<br />
[https://zvzzt.wordpress.com/2014/05/03/python-recursion-visualization-with-rcviz/ Source: Python Recursion Visualization with rcviz]<br />
<br />
== Data abstraction ==<br />
== Time complexity ==<br />
=== Andrew Huang's guide to order of growth and function runtime ===<br />
[https://docs.google.com/document/d/1TxfKmM3MlH032hjSUh92I0kQDVcvmitTSzYObGMr8Bk Source: Guide to Order of Growth and Function Runtime (Retrieved June 16th, 2014)]<br />
<br />
'''Introduction'''<br />
<br />
Confused by $O$, $\Omega$, and $\Theta$?<br />
<br />
Want to figure out the runtime of that tricky function?<br />
<br />
Read this.<br />
<br />
'''NOTE THAT THIS GUIDE STARTS WITH BIG O, WHICH IS DIFFERENT FROM THETA. IF YOU UNDERSTAND BIG O, THETA IS EASY (IN FACT, IT DEFINES THETA IN TERMS OF BIG O BELOW).'''<br />
<br />
First some math.<br />
<br />
Formal definition of O(Big O):<br />
<br />
Let $f(n)$ and $g(n)$ be functions from positive integers to positive reals. We say $f \in O(g)$ (“f grows no faster than g”) if there is a constant $0 < c < \inf$ <such that $f(n) \leq c \cdot g(n)$.<br />
<br />
(Paraphrased from Dasgupta, Papadimitriou, & Vazirani)<br />
<br />
(You'll see this again in CS 170)<br />
<br />
What the heck does that mean?<br />
<br />
Let’s look at math functions for a second (just a second).<br />
<br />
Say $f(n)=5n$ and $g(n)=n^{2}$<br />
<br />
What does that look like on a graph?<br />
<br />
http://www.wolframalpha.com/input/?i=plot+5n+and+n%5E2+from+0+to+10<br />
<br />
There’s a section where $n$ dominates $n^{2}$, from 0 to 5, but we don’t really care, because after that point, $n^{2}$ is larger, all the way to infinity! By the definition, we could scale $n^{2}$ by 5 and we would span that initial gap.<br />
<br />
Thus we can say $5n \in O(n^{2})$ or $f \in O(g)$.<br />
<br />
Can we say the converse? That is, is $n^{2} \in O(5n)$?<br />
<br />
Not at all! From the graph we see that $n^{2}$ grows too quickly for $n$ to catch up, no matter what constant we scale $n$ by.<br />
<br />
So what if $f(n)=n+1000$ and $g(n)=n^{2}$?<br />
<br />
It turns out $n+1000 \in O(n^{2})$ still, because according to the definition, as long as we can multiply $n^{2}$ by some $c$, such that the gap of 1000 is spanned, we’re good. In the case, $c=1001$.<br />
<br />
'''What about and $\Omega$ and $\Theta$?'''<br />
<br />
If you digested all of the above, the rest isn’t scary! (Note, $a \equiv b$ means $a$ is equivalent to $b$)<br />
<br />
$f \in \Omega(g) \equiv g \in O(f)$ (You'll see this again briefly in CS 170)<br />
<br />
$f \in \Theta(g) (f \in O(g) and g \in O(f))$<br />
<br />
'''This means that if $f$ is Theta of $g$, then there exist some $c_{1}$ and $c_{2}$ such that'''<br />
<br />
'''$c_{1}g > f$ and'''<br />
<br />
'''$c_{2}g < f$'''<br />
<br />
'''for all positive integers.'''<br />
<br />
'''What does that mean for Python functions?'''<br />
<br />
Given a function $f$, we want to find out how fast that function runs. One way of doing this is to take out a stopwatch, and clock the amount of time it takes for $f$ to run on some input. However, there are tons of problems with that (different computers => different speeds; only one fixed input? Maybe $f$ is really fast for that input but slow for everything else; next year, all the measurements need to be redone on new computers; etc.) Instead, we'll count the steps that a function needs to perform as a function of its input. For example, here are some of the functions that take one step regardless of their input:<br />
<br />
<code>mul</code><br />
<br />
<code>add</code><br />
<br />
<code>sub</code><br />
<br />
<code>print</code><br />
<br />
<code>return</code><br />
<br />
<code>...</code><br />
<br />
So for example, <code>(3 + 3 * 8) % 3</code> would be 3 steps--one for the multiply, one of the add, and one for the mod.<br />
<br />
Let's take a simple example:<br />
<pre>def square(x):<br />
return x * x</pre><br />
<code>square</code> is a function that for any input, always takes two steps, one of the multiplication, and one for returning. Using the notation, we can say square ∈ Θ(1).<br />
<br />
Functions with iteration (for loops, recursion, etc.), usually multiply the steps by some factor. For example, consider factorial:<br />
<pre>def factorial(n):<br />
if n == 0:<br />
return 1<br />
else:<br />
return n * factorial(n-1)</pre><br />
factorial ∈ Θ(n). Why? Well given some input <code>n</code>, we do <code>n</code> recursive calls. At each recursive call, we carry out 4 steps, one for if <code>n == 0</code>, one for subtraction, one for multiply, one for return. Plus, we have the base case, which is another 2 steps, one for if and one for return. So <code>factorial(n)</code> takes $4n+2$ steps => ∈ Θ(n).<br />
<br />
As mentioned, we care about how the running time (how long the function takes to run) of the function changes, '''as we increase the size of the argument'''. So if we imagine a graph, then the x-axis represents the size of our input, and the y-axis represents how long the function took to run for each x. As the size of the input increases, the function’s runtime does something on the graph. So when we say something like “$O(n^{2})$ where $n$ is the length of the list”, we are saying as we double the size of the list, the function is expected to run at most four times as long. '''NOTE ALSO THAT I SAID WHAT $n$ IS! ALWAYS GIVE YOUR UNITS.'''<br />
<br />
This means that when we compare two functions A and B, A may be overall slower than B as we increase the size of their arguments. However, it’s possible at some specific arguments, the A may run faster (like the $f(n)=5n$ and $g(n)=n^{2}$ example above.)<br />
<br />
'''This also means we do not care about the time taken of any particular input! This implies that all those constant-time base cases all those functions don’t really matter, because they don’t scale. That is, only one specific input causes the base case to be reached, and if we increased the size of the argument, $O(1)$ doesn't necessarily hold.'''<br />
<br />
'''Brief “What runs faster than what”'''<br />
<br />
Sorted from fastest to slowest. This is by no means comprehensive.<br />
* $\Theta(1)$<br />
* $\Theta(\log(n))$<br />
* $\Theta(n)$<br />
* $\Theta(n \log(n))$<br />
* $\Theta(n^{2})$<br />
* $\Theta(n^{3})$<br />
* $\Theta(2^{n})$<br />
* (Anything past this point is kind of ridiculous)<br />
* $\Theta(n!)$<br />
* $\Theta(n^{n})$<br />
<br />
'''So we know about the math and the motivation, now how do we actually assign runtimes to real Python functions?'''<br />
<br />
What you must understand, is that there is no one method for finding the runtime. You MUST look at a function holistically or you won’t get the right answer. What does this mean? '''In order to get the correct runtime, you first must understand what the function is doing! You cannot pattern-match your way to becoming good at this.'''<br />
<br />
'''This cannot be stressed enough: UNITS MATTER, if you say O((n)), you must tell us what $n$ is.'''<br />
<br />
General tips<br />
# UNDERSTAND WHAT THE FUNCTION IS DOING!!!<br />
# Try some sample input. That is, pretend you’re the interpreter and execute the code with some small inputs. What is the function doing with the input? Having concrete examples lets you do tip 1 better. You can also graph how the runtime increases as the argument size increases.<br />
# If applicable, draw a picture of the tree of function calls. This shows you the "growth" of the function or how the function is getting "bigger", which will help you do tip 1 better.<br />
# If applicable, draw a picture of how the input is being modified through the function calls. For example, if your input is a list and your function recursively does something to that list, draw out a list, then draw out parts of the list underneath it that are called during the recursion. Helps with tip 1.<br />
# See tip 1.<br />
<br />
Anyways, let's examine some common runtimes (keep scrolling). Remember, '''this is in no way a comprehensive list, NOR IS IT TRYING TO TEACH YOU HOW TO FIND THEM.''' This post is just to give you a starting point into orders of growth by showing you some examples and basic details about each runtime.<br />
<br />
'''Constant $\Theta(1)$'''<br />
<br />
''What it looks like:''<br />
<br />
http://www.wolframalpha.com/input/?i=plot+5<br />
<br />
''Example:''<br />
<br />
<pre>def add(x, y):<br />
return x + y</pre><br />
$add \in \Theta(1)$, where 1 is.. well a constant...<br />
<br />
''Approach:''<br />
<br />
The key behind constant time functions is that regardless of the size of the input, they always run the same number of instructions.<br />
<br />
''Don’t fall for this Trap:''<br />
<br />
<pre>def bar(n):<br />
if n % 7 == 0:<br />
return "Bzzst"<br />
else:<br />
return bar(n -1)</pre><br />
$\mathtt{bar} \in \Theta(1)$. Why?<br />
<br />
'''Logarithmic $\Theta(\log(n))$'''<br />
<br />
''What it looks like:''<br />
<br />
http://www.wolframalpha.com/input/?i=plot+4log3n+from+0+to+10<br />
<br />
''Example:''<br />
<br />
<pre>def binary_search(sorted_L, n):<br />
""" sorted_L is a list of numbers sorted from<br />
smallest to largest<br />
"""<br />
if sorted_L == []:<br />
return False<br />
mid_num = sorted_L[len(sorted_L) // 2]<br />
if n == mid_num:<br />
return True<br />
elif n < mid_num:<br />
return binary_search(sorted_L[:mid_num], n)<br />
else:<br />
return binary_search(sorted_L[mid_num:], n)</pre><br />
$\mathtt{binary\_search} \in \Theta(log(n))$, where $n$ is the number of elements in <code>sorted_L</code>.<br />
<br />
''Approach:''<br />
<br />
Logarithmic functions scale down the size of the problem by some constant every iteration (either with a recursive loop, a for loop, or a while loop). Also, logarithmic functions do not branch out--they generally do not make more than one call to themselves per recursion.<br />
<br />
'''Linear $\Theta(n)$'''<br />
<br />
''What it looks like:''<br />
<br />
http://www.wolframalpha.com/input/?i=plot+8n+from+0+to+10<br />
<br />
''Examples:''<br />
<br />
<pre>def sum_list(L):<br />
sum = 0<br />
for e in L:<br />
sum += e<br />
return sum</pre><br />
$\mathtt{sum\_list} \in \Theta(n)$, where $n$ is the number of elements in $L$.<br />
<br />
</pre>def countdown(n):<br />
if n > 0:<br />
print(n)<br />
countdown(n - 1)<br />
else:<br />
print("Blast off!")</pre><br />
$\mathtt{countdown} \in \Theta(n)$, where $n$ is n.<br />
<br />
''Approach:''<br />
<br />
Linear functions usually act on sequences or other collections of data. In that case, the function will go through the elements once or twice or ''k'' times, where $k<<n$. If the function acts on a number, the number usually gets smaller by a constant each iteration.<br />
<br />
Don't fall for this trap:<br />
<pre>def two_for_loops(n):<br />
for a in range(n):<br />
if n == 4:<br />
for y in range(n):<br />
print("Admiral Ackbar")<br />
else:<br />
print("It's a trap!")</pre><br />
$\mathtt{two\_for\_loops} \in \Theta(n)$, where $n$ is n. Why?<br />
<br />
'''Loglinear/Linearithmic $\Theta(n \log(n))$'''<br />
<br />
''What it looks like:''<br />
<br />
http://www.wolframalpha.com/input/?i=plot+nlog%28n%29+from+0+to+10<br />
<br />
''Example:''<br />
<br />
<pre>def merge(s1, s2):<br />
if len(s1) == 0:<br />
return s2<br />
elif len(s2) == 0:<br />
return s1<br />
elif s1[0] < s2[0]:<br />
return [s1[0]] + merge(s1[1:], s2)<br />
else:<br />
return [s2[0]] + merge(s1, s2[1:])<br />
<br />
def mergesort(lst):<br />
if len(lst) <= 1:<br />
return lst<br />
else:<br />
middle = len(lst) // 2<br />
return merge(mergesort(lst[:middle]), \<br />
mergesort(lst[middle:]))</pre><br />
$\mathtt{mergesort} \in \Theta(n \log(n))$, where $n$ is the number of elements in <code>lst</code>.<br />
<br />
Approach:<br />
These functions tend to make two recursive calls, each making the problem smaller by a half. There's a neat way to see this. For example in mergesort, start with an entire line, which represents mergesort called on the initial list. From there, the list gets split in half by the two recursive calls to mergesort in the code, so draw the another line right below the first, of the same length, but with a small gap in the middle to represent the split. Repeat until you're tired. At the end, you get a rectangle that's nwide and (n)tall!<br />
<pre><nowiki><br />
---------------<br />
------- -------<br />
--- --- --- ---<br />
- - - - - - - -<br />
</nowiki></pre><br />
The total area is the runtime, $\Theta(n \log(n))$<br />
<br />
''Don’t fall for this trap:''<br />
<br />
Don’t confuse functions that have an average running time of n(n)(like quicksort) with functions that are in (n(n))<br />
<br />
'''Polynomial $\Theta(n^{2})$,$\Theta(n^{3})$, etc.'''<br />
<br />
''What it looks like:''<br />
<br />
http://www.wolframalpha.com/input/?i=plot+n%5E2%2B3+from+0+to+10<br />
<br />
''Example:''<br />
<br />
<pre>def print_a_grid(n):<br />
for _ in range(n):<br />
for _ in range(n):<br />
print("+", end="")<br />
print("")</pre><br />
$\mathtt{print\_a\_grid} \in \Theta(n^{2})$, where $n$ is n.<br />
<br />
''Approach:''<br />
<br />
Polynomial functions will examine each element of an input many, many times, as opposed to linear functions, which examine some constant number of times.<br />
<br />
''Don’t fall into this trap:''<br />
<br />
Don’t get polynomial confused with exponential (below).<br />
<br />
'''Exponential'''<br />
<br />
''What it looks like:''<br />
<br />
http://www.wolframalpha.com/input/?i=plot+2%5En+from+0+to+10<br />
<br />
''Example:''<br />
<br />
<pre>(define (strange-add x)<br />
(if (zero? x)<br />
1<br />
(+ (strange-add (- x 1))<br />
(strange-add (- x 1)) )))<br />
<br />
def strange_add(x):<br />
if x == 0:<br />
return 1<br />
else:<br />
return strange_add(x - 1) + strange_add(x - 1)</pre><br />
$\mathtt{strange\_add} \in \Theta(2^{n})$, where $n$ is x.<br />
<br />
''Approach:''<br />
<br />
Exponential functions tend to branch out as you get deeper and deeper into their call tree, and each call only makes the work smaller by a little bit. For example, <code>(strange-add 8)</code> calls <code>(strange-add 7)</code> and <code>(strange-add 7)</code>. Those two calls each make two calls, <code>(strange-add 6)</code>, <code>(strange-add 6)</code>, <code>(strange-add 6)</code>, and <code>(strange-add 6)</code> respectively, and so on.<br />
<br />
== Mutability ==<br />
=== Michelle Chang's guide to immutability and mutability ===<br />
[https://d1b10bmlvqabco.cloudfront.net/attach/hoxc5uu6sud761/gozdkhgdUbT/htdlpko411i0/Python__Immutable_vs_Mutable.pdf Source: What You Should Know about Immutability vs Mutability]<br />
<br />
== Mutable data-structures ==<br />
== Object-oriented programming ==<br />
=== Inheritance and class vs instance attributes ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=1413 Source: Spring 2014 Piazza (1413)]<br />
<br />
'''Student Question'''<br />
<br />
I'm confused on how Classes and Inheritance work.<br />
<br />
If there's a Parent class and a Child class, when coding in the Child class, when do you write <code>Parent.attribute</code>, when do you write <code>Child.attribute</code>, and when do you write <code>self.attribute</code>?<br />
<br />
Also, I'm also confused as to when to put <code>self</code> into the parentheses as well.<br />
<br />
'''Instructor Answer'''<br />
<br />
<code>Parent.attribute</code> and <code>Child.attribute</code> would both be ways of accessing a'''class variable'''. These are variables that can be accessed without creating new '''instances''' of the that class.<br />
<br />
<code>self.attribute</code> would be used in '''methods''' to access an '''instance variable''' (an attribute specific to an instance).<br />
<br />
So for example, <code>Insect.watersafe</code> is <code>False</code>, but <code>Bee.watersafe</code> is <code>True</code>. These are class attributes because you don't have to create an Insect object or a Bee object in order to say <code>Insect.watersafe</code> or <code>Bee.watersafe</code>.<br />
<br />
However it wouldn't make any sense to say <code>Bee.armor</code>, since armor is an '''instance variable'''. You have to first create a new Bee before you could ask it for it's armor. If you created a second Bee after that, the second Bee would also have its own armor.<br />
<br />
There's a lot of vocab (in bold) that might trip you up. Try reading Discussion 6 and posting a followup if you're still unsure!<br />
<br />
== Iterables, iterators and generators ==<br />
== Python semantics and syntax ==<br />
=== While vs. If ===<br />
[https://piazza.com/class/hktwdlzfjnw680?cid=397 Source: Fall 2013 Piazza (397)]<br />
<br />
'''Student Question'''<br />
<br />
I'm a bit confused, what is the exact function of the "while" function, and how is this different from the "if" function?<br />
<br />
'''Instructor Answer'''<br />
<br />
A while loop will keep evaluating the body of the while loop when its conditional expression is true, whereas an if statement will evaluate its body only once after evaluating its conditional expression.<br />
<br />
i.e. Consider the following two functions! <code>f()</code> will return <code>10</code>, while <code>g()</code> will return <code>1</code>.<br />
<pre>def f():<br />
i = 0<br />
while i &lt; 10:<br />
i += 1<br />
return i<br />
<br />
</pre><br />
vs<br />
<br />
<pre>def g():<br />
i = 0<br />
if i &lt; 10:<br />
i += 1<br />
return i<br />
<br />
</pre><br />
<br />
=== @property ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=3015 Source: Spring 2014 Piazza (3015)]<br />
<br />
'''Student Question'''<br />
<br />
Is it possible to call <code>@property</code> on methods that have arguments?<br />
<br />
'''Student Answer'''<br />
<br />
<code>@property</code> is used to define "setters" and "getters". It essentially gives you control of what happens when a variable is assigned or retrieved.<br />
<pre>class A:<br />
def __init__(self):<br />
self._x = 5<br />
@property<br />
def x(self):<br />
return self._x<br />
@x.setter<br />
def x(self, value):<br />
print("Hello World")<br />
self._x = value<br />
<br />
&gt;&gt;&gt; a = A()<br />
&gt;&gt;&gt; a.x = 52<br />
"Hello World"</pre><br />
As such, there isn't really room for arguments, since it is treated similar to a variable rather than a function. It is retrieved as "a.x" and set as "a.x = value". <br />
<br />
If you would like to pass arguments when assigning something, it would probably be best to just have them use that method. e.g.<br />
<pre>def set_x(self, value1, value2, value3):<br />
self._x = value1 * value2 * value3</pre><br />
Of course, you won't get the syntactic sugar of "a.x = ", but it gets the job done.<br />
<br />
The cool thing about properties is that you can also place restrictions on a variable. For example, if you leave out the "x.setter" method in the example above, then assigning a.x = 5 would throw an error since it isn't defined (you can still assign a._x though). It would only be available for reading. Sometimes that is a good sign to let any users know to use some method to set it (e.g. set_x) or to not set it at all.<br />
<br />
== Scheme ==<br />
=== Scheme semantics and syntax ===<br />
----<br />
==== Difference between <code>eq?</code>, <code>eqv?</code>, <code>equal?</code> and <code>=</code> ====<br />
[http://stackoverflow.com/questions/16299246/what-is-the-difference-between-eq-eqv-equal-and-in-scheme/17719745#17719745 What is the difference between eq?, eqv?, equal?, and = in Scheme?]<br />
<br />
Let's start with the <code>=</code> equivalence predicate. The <code>=</code> predicate is used to check whether two numbers are equal. If you supply it anything else but a number then it will raise an error:<br />
<br />
<pre>(= 2 3) => #f<br />
(= 2.5 2.5) => #t<br />
(= '() '()) => error</pre><br />
The <code>eq?</code> predicate is used to check whether its two parameters respresent the same object in memory. For example:<br />
<br />
<code>(define x '(2 3))<br />
(define y '(2 3))<br />
(eq? x y) => #f<br />
(define y x)<br />
(eq? x y) => #t</code><br />
Note however that there's only one empty list <code>'()</code> in memory (actually the empty list doesn't exist in memory, but a pointer to the memory location <code>0</code> is considered as the empty list). Hence when comparing empty lists <code>eq?</code> will always return <code>#t</code> (because they represent the same object in memory):<br />
<br />
<pre>(define x '())<br />
(define y '())<br />
(eq? x y) => #t</pre><br />
Now depending upon the implementation <code>eq?</code> may or may not return <code>#t</code> for primitive values such as numbers, strings, etc. For example:<br />
<br />
(<pre>eq? 2 2) => depends upon the implementation<br />
(eq? "a" "a") => depends upon the implementation</pre><br />
This is where the <code>eqv?</code> predicate comes into picture. The <code>eqv?</code> is exactly the same as the <code>eq?</code> predicate, except that it will always return <code>#t</code> for same primitive values. For example:<br />
<br />
<pre>(eqv? 2 2) => #t<br />
(eqv? "a" "a") => #t</pre><br />
Hence <code>eqv?</code> is a superset of <code>eq?</code> and for most cases you should use <code>eqv?</code> instead of <code>eq?</code>.<br />
<br />
Finally we come to the <code>equal?</code> predicate. The <code>equal?</code> predicate is exactly the same as the <code>eqv?</code> predicate, except that it can also be used to test whether two lists, vectors, etc. have corresponding elements which satisfy the <code>eqv?</code> predicate. For example:<br />
<br />
<pre>(define x '(2 3))<br />
(define y '(2 3))<br />
(equal? x y) => #t<br />
(eqv? x y) => #f</pre><br />
In general:<br />
# Use the <code>=</code> predicate when you wish to test whether two numbers are equivalent.<br />
# Use the <code>eqv?</code> predicate when you wish to test whether two non-numeric values are equivalent.<br />
# Use the <code>equal?</code> predicate when you wish to test whether two lists, vectors, etc. are equivalent.<br />
# Don't use the <code>eq?</code> predicate unless you know exactly what you're doing.<br />
<br />
=== Scheme lists ===<br />
----<br />
==== Using <code>cons</code> ====<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=3004 Source: Spring 2014 Piazza (3004)]<br />
<br />
'''Student Question<br />
<br />
What's the difference between the following in Scheme?<br />
<br />
(cons 1 2)<br />
(cons 1 . 2)<br />
<br />
(cons 1 (cons 2 (cons 3 nil)))<br />
(cons 1 . (cons 2 . (cons 3 . 4)))<br />
<br />
Why does putting a dot before "(cons" cause it to be a malformed list? But when you put in (cons 1 2) it returns (1 . 2)? Is the dot something that only the interpreter returns, and that the user can't use in defining a list?<br />
<br />
'''Student Answer'''<br />
<br />
I struggled with this a bit as well. It helps to know the difference between a list containing a "." and one that doesn't. <br />
<br />
First off, scheme lists love to be recursive, kind of like rlists. That is, if you did cdr on the list, you would keep getting a list until you finally get an empty list/nil.<br />
<pre>STk&gt; (define a '(1 2 3))<br />
a<br />
STk&gt; a<br />
(1 2 3)<br />
STk&gt; (cdr a)<br />
(2 3)<br />
STk&gt; (cddr a)<br />
(3)<br />
STk&gt; (cdddr a)<br />
()</pre><br />
Note how each call of cdr returns a list. Even (3) is a list. It is just a list containing one member. So what happens with something like (1 2 . 3)?<br />
<pre>STk&gt; (define a '(1 2 . 3))<br />
a<br />
STk&gt; a<br />
(1 2 . 3)<br />
STk&gt; (cdr a)<br />
(2 . 3)<br />
STk&gt; (cddr a)<br />
3</pre><br />
Notice how the last <code>cdr</code> returns a simple 3. Running <code>cdddr</code> would throw an error, because the list stops at 3. A list containing a "." is known as an <em>improper </em>list.<br />
<br />
As for the ".", you won't be using it unless you use it in combination with a quote, otherwise it will always return a malformed list and throw an error. For example:<br />
<pre>STk&gt; (define a (1 2 . 3))<br />
*** Error:<br />
eval: malformed list: (1 2 . 3)<br />
Current eval stack:<br />
__________________<br />
0 (1 2 . 3)<br />
1 (define a (1 2 . 3))<br />
STk&gt; (define a '(1 2 . 3))<br />
a</pre><br />
Basically, the "." is seen in output, but not input. The only exception is the quote. Think of scheme as having two stages. First, it interprets your commands to construct the lists etc. Next, it will simplify the expression. Think of the quote as skipping straight to the second stage.<br />
<br />
Finally, the only way to get the standard lists is to end the list with nil or to use the "list" function (or to use a quote). If the list doesn't end with nil, then it will become an improper list. That is why you <em>can't</em> do things like <code>(1 . 2 . 3 . 4)</code> to make <code>(1 2 3 4)</code>. In order to form a "proper" list, each element must be represented by a list. You <em>can</em> do <code>(1 . (2 . (3 . (4))))</code> because you are treating each element like a list. When in doubt, just test some output:<br />
<pre>STk&gt; (cons 1 2)<br />
(1 . 2) ; doesn't end in an empty list/nil<br />
STk&gt; (cons 1 (cons 2 '()))<br />
(1 2)<br />
STk&gt; (cons 1 (cons 2 (3)))<br />
; ERROR<br />
STk&gt; (cons 1 (cons 2 (list 3)))<br />
(1 2 3) ; lists are formed with an nil at the end, so this works<br />
STk&gt; '(1 . (2 . (3 . 4)))<br />
(1 2 3 . 4)<br />
STk&gt; '(1 . (2 . (3 . (4)))<br />
(1 2 3 4)<br />
STk&gt; '(1 (2 . 3) 4)<br />
(1 (2 . 3) 4) ; still works, but this has 3 elements: (1), (2 . 3), and (4)<br />
STk&gt; (cons 1 (cons (cons 2 3) (cons 4 nil))) <br />
(1 (2 . 3) 4) ; equivalent to above, without quote</pre><br />
<br />
==== <code>append</code> vs <code>cons</code> vs <code>list</code> ====<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=2067 Source: Spring 2014 Piazza (2067)]<br />
<br />
This post isn't meant to be comprehensive. '''Ask questions in lab or as a followup here if you're confused.'''<br />
One of its major flaws is that it doesn't cover box and pointers. '''LEARN BOX AND POINTERS. '''<br />
<br />
Here is a beautiful web based Scheme interpreter that will draw box and pointer diagrams for you. Run through the examples below with this thing:<br />
http://xuanji.appspot.com/js-scheme-stk/index.html<br />
<br />
In order to understand these three procedures, you first have to understand a little about Pairs and Lists.<br />
<br />
'''Pairs''' are data structures that have two slots. You can put different stuff in these slots, like numbers or words or sentences or booleans--pretty much anything. You make a pair using cons.<br />
<pre>STk> (cons 'foo 'bar)<br />
(foo . bar)<br />
<br />
STk> (cons 1 'ring)<br />
(1 . ring)<br />
<br />
STk> (cons (+ 1 2 3) (member? 3 '(the 3 stooges)))<br />
(6 . #t)</pre><br />
<br />
In order to get stuff from a pair that you have made, you use <code>car</code> and <code>cdr</code>. <code>car</code> gets the thing in the first slot. <code>cdr</code> gets the thing in the second slot.<br />
<br />
<pre>STk> (define foo (cons 'x 'y))<br />
foo<br />
<br />
STk> foo<br />
(x . y)<br />
<br />
STk> (car foo)<br />
x<br />
<br />
STk> (cdr foo)<br />
y</pre><br />
That was straightforward. Now for the trippy part:<br />
'''You can put pairs inside of pairs:'''<br />
<pre>STk> (define foo (cons (cons 3 4) 5))<br />
foo<br />
<br />
STk> foo<br />
((3 . 4) . 5)<br />
<br />
STk> (car foo)<br />
(3 . 4)<br />
<br />
STk> (car (car foo))<br />
3<br />
<br />
STk> (caar foo) ; functionally equivalent as above.<br />
3<br />
<br />
STk> (cdr foo)<br />
5<br />
<br />
STk> (cdr (car foo))<br />
4<br />
<br />
STk> (cdar foo) ; functionally equivalent as above.<br />
4</pre> <br />
<br />
There's a certain style of pair nesting that is especially useful&mdash;'''Lists.'''<br />
<br />
Each list has these properties:<br />
* Every list is a pair or the empty list (denoted by '() or nil).<br />
* The car of a nonempty list is some item.<br />
* The cdr of a nonempty list must be another list.<br />
<pre>STk> (cons 1 (cons 2 (cons 3 '()))) ; list of numbers<br />
(1 2 3)<br />
<br />
STk> (define stooges (cons 'larry (cons 'curly (cons 'moe nil))))<br />
stooges<br />
<br />
STk> stooges<br />
(larry curly moe)<br />
<br />
STk> (car stooges)<br />
larry<br />
<br />
STk> (cdr stooges) ; Calling cdr on a non-empty list gives you another list!<br />
(curly moe)<br />
<br />
STk> (cadr stooges)<br />
curly<br />
<br />
STk> (cdar stooges) ; Why does this break?<br />
*** Error:<br />
cdar: bad list: (moe larry curly)<br />
Current eval stack:<br />
__________________<br />
0 (cdar stooges)<br />
<br />
STk> (define not-a-list (cons 'foo (cons 'bar 'baz))) ; This is not a list.<br />
not-a-list<br />
<br />
STk> not-a-list ; What property does this break?</pre><br />
Notice how Scheme knew that we were making lists. Before we had <code>parens</code> and periods which organized our items. Scheme now recognizes that we're making a list and does away with the periods and some of the <code>parens</code>.<br />
<br />
If you stare a bit at the list rules above, you can notice we used a recursive definition to define lists. Recursion... '''on data'''!<br />
<br />
Let's talk about <code>list</code>. <code>list</code> takes a bunch of stuff and makes a list out of them. The stuff can be anything. Words, numbers, pairs, other lists. <code>list</code> doesn't care. [picture of a honey badger]<br />
<pre>STk> (list 'foo 'bar' 'baz) ; Lists takes anything and makes a list out of it.<br />
(foo bar baz)<br />
<br />
STk> (list 'foo ((lambda (x) (+ x 4)) 8) #f (cons 1 (cons 3 4)) (cons 1 (cons 2 nil)) (list 1 2 3)) ; ANYTHING <br />
(foo 12 #f (1 3 . 4) (1 2) (1 2 3))<br />
<br />
STk> (list 'x 'y 'z)<br />
(x y z)<br />
<br />
STk> '(x y z) ; Sometimes you can get away with using quote to make literal lists. Yes, sentences are secretly lists.<br />
(x y z)</pre><br />
Now we can talk about <code>append</code>:<br />
<pre>STk> (append '(a b c) '(d e f) '(g h i)) ; Append takes in lists and appends them together.<br />
(a b c d e f g h i)<br />
<br />
STk> (append 'foo '(1 2 3)) ; foo is not a list. Stuff will break.<br />
*** Error:<br />
append: argument is not a list: foo<br />
Current eval stack:<br />
__________________<br />
0 (append (quote foo) (quote (1 2 3)))</pre><br />
You know that <code>cons</code> makes a pair. You also know that you can make a list out of pairs. You can abuse <code>cons</code> for your own maniacal purposes.<br />
<pre>STk> (cons 'joe stooges) ; Put stuff at the beginning of a list!<br />
(joe larry curly moe)</pre><br />
'''The following only applies to the STk interpreter.'''<br />
<pre>STk> (append '(1 2 3) 'foo) ; Wait... what?<br />
(1 2 3 . foo)<br />
<br />
STk> (append '(1 2 3) (cons 4 5)) ; The plot thickens!<br />
(1 2 3 4 . 5)<br />
<br />
STk> (append stooges 'shemp) ; You should really figure out why this works.<br />
(larry curly moe . shemp)</pre><br />
<br />
To summarize:<br />
* append takes in lists and outputs a big list.<br />
* cons takes in things and makes a pair out of them. However, we know that lists are made of pairs, so we can throw together a list if we use cons a certain way<br />
* list takes in things and makes a list out of those things, regardless of what they are.<br />
<br />
=== Tail recursion ===<br />
----<br />
==== Mark Miyashita's guide on tail recursion ====<br />
[http://markmiyashita.com/cs61a/sp14/tail_recursion/tail_recursion_and_tail_optimized_calls/ Source: Tail Recursion and Tail Optimized Calls]<br />
<br />
First off, I think this is an excellent article to read about tail recursion and tail calls in Python: [http://paulbutler.org/archives/tail-recursion-in-python/ here]<br />
<br />
Basically, you can write tail recursive functions in any language. Tail recursion, in one sentence, is where you return the answer in the final frame instead of following the frames back up to the original frame. For example, we have factorial which is normally not tail recursive:<br />
<br />
<pre>def factorial(n):<br />
if n == 1:<br />
return 1<br />
return n * factorial(n - 1)</pre><br />
<br />
because it needs to keep track of the <code>n *</code> at each level of recursion.<br />
<br />
The following implementation of <code>factorial</code>, is tail recursive because at the end of the last frame, it can return the answer, instead of going back up through all the frames to multiply and compute the answer:<br />
<br />
<pre>def factorial(n):<br />
def helper(n, total):<br />
if n == 1:<br />
return total<br />
return helper(n - 1, total * n)<br />
return helper(n, 1)</pre><br />
<br />
You can’t have tail optimized calls in Python – at least, not like the code that we defined above. You can define your own sort of tail optimized way of evaluating the functions by using lambdas, and I believe the article linked at the top of this post goes into detail about how to implement this if you are interested. In Scheme, the language detects when you have something like the helper function in the example above where your return statement consists of only the recursive call. In the first example, we have the <code>n *</code> and the recursive call which means it cannot be tail optimized because it needs to keep track of all of the frames that it creates. In a tail optimized call, Scheme will get rid of the frames that are no longer necessary.<br />
<br />
'''tl;dr''' – Tail recursion can be done in any language where the basic idea is that you return the answer in the final frame of recursion. Tail optimized calls are a Scheme (and some other languages, not including Python) feature where it will get rid of the frames above, if certain conditions are met – such as where the return statement is only the recursive call and nothing else. The cases in which Scheme uses a tail optimized call are located on the lecture slides located [http://www-inst.eecs.berkeley.edu/~cs61a/sp13/slides/35-TailCalls_6pp.pdf here].<br />
<br />
==== Tail recursion in Python ====<br />
[http://kylem.net/programming/tailcall.html Source: http://kylem.net/programming/tailcall.html (Retrieved June 16th, 2014)]<br />
<br />
In this page, we’re going to look at tail call recursion and see how to force Python to let us eliminate tail calls by using a trampoline. We will go through two iterations of the design: first to get it to work, and second to try to make the syntax seem reasonable. I would not consider this a useful technique in itself, but I do think it’s a good example which shows off some of the power of decorators.<br />
<br />
The first thing we should be clear about is the definition of a tail call. The “call” part means that we are considering function calls, and the “tail” part means that, of those, we are considering calls which are the last thing a function does before it returns. In the following example, the recursive call to f is a tail call (the use of the variable <code>ret</code> is immaterial because it just connects the result of the call to <code>f</code> to the return statement), and the call to <code>g</code> is not a tail call because the operation of adding one is done after <code>g</code> returns (so it’s not in “tail position”).<br />
<br />
<pre>def f(n) :<br />
if n > 0 :<br />
n -= 1<br />
ret = f(n)<br />
return ret<br />
else :<br />
ret = g(n)<br />
return ret + 1</pre><br />
<br />
'''1. Why tail calls matter'''<br />
<br />
Recursive tail calls can be replaced by jumps. This is called “tail call eliminination,” and is a transformation that can help limit the maximum stack depth used by a recursive function, with the benefit of reducing memory traffic by not having to allocate stack frames. Sometimes, recursive function which wouldn’t ordinarily be able to run due to stack overflow are transformed into function which can.<br />
<br />
Because of the benefits, some compilers (like <code>gcc</code>) perform tail call elimination[1], replacing recursive tail calls with jumps (and, depending on the language and circumstances, tail calls to other functions can sometimes be replaced with stack massaging and a jump). In the following example, we will eliminate the tail calls in a piece of code which does a binary search. It has two recursive tail calls.<br />
<br />
<pre>def binary_search(x, lst, low=None, high=None) :<br />
if low == None : low = 0<br />
if high == None : high = len(lst)-1<br />
mid = low + (high - low) // 2<br />
if low > high :<br />
return None<br />
elif lst[mid] == x :<br />
return mid<br />
elif lst[mid] > x :<br />
return binary_search(x, lst, low, mid-1)<br />
else :<br />
return binary_search(x, lst, mid+1, high)</pre><br />
<br />
Supposing Python had a <code>goto</code> statement, we could replace the tail calls with a jump to the beginning of the function, modifying the arguments at the call sites appropriately:<br />
<br />
<pre>def binary_search(x, lst, low=None, high=None) :<br />
start:<br />
if low == None : low = 0<br />
if high == None : high = len(lst)-1<br />
mid = low + (high - low) // 2<br />
if low > high :<br />
return None<br />
elif lst[mid] == x :<br />
return mid<br />
elif lst[mid] > x :<br />
(x, lst, low, high) = (x, lst, low, mid-1)<br />
goto start<br />
else :<br />
(x, lst, low, high) = (x, lst, mid+1, high)<br />
goto start</pre><br />
<br />
which, one can observe, can be written in actual Python as<br />
<br />
<pre>def binary_search(x, lst, low=None, high=None) :<br />
if low == None : low = 0<br />
if high == None : high = len(lst)-1<br />
while True :<br />
mid = low + (high - low) // 2<br />
if low > high :<br />
return None<br />
elif lst[mid] == x :<br />
return mid<br />
elif lst[mid] > x :<br />
high = mid - 1<br />
else :<br />
low = mid + 1</pre><br />
<br />
I haven’t tested the speed difference between this iterative version and the original recursive version, but I would expect it to be quite a bit faster because of there being much, much less memory traffic.<br />
<br />
Unfortunately, the transformation makes it harder to prove the binary search is correct in the resulting code. With the original recursive algorithm, it is almost trivial by induction.<br />
<br />
Programming languages like Scheme depend on tail calls being eliminated for control flow, and it’s also necessary for continuation passing style.[2]<br />
<br />
'''2. A first attempt'''<br />
<br />
Our running example is going to be the factorial function (a classic), written with an accumulator argument so that its recursive call is a tail call:<br />
<br />
<pre>def fact(n, r=1) :<br />
if n <= 1 :<br />
return r<br />
else :<br />
return fact(n-1, n*r)</pre><br />
<br />
If <code>n</code> is too large, then this recursive function will overflow the stack, despite the fact that Python can deal with really big integers. On my machine, it can compute <code>fact(999)</code>, but <code>fact(1000)</code> results in a sad <code>RuntimeError: Maximum recursion depth exceeded</code>.<br />
<br />
One solution is to modify fact to return objects which represent tail calls and then to build a trampoline underneath fact which executes these tail calls after fact returns. This way, the stack depth will only contain two stack frames: one for the trampoline and another for each call to fact.<br />
<br />
First, we define a tail call object which reifies the concept of a tail call:<br />
<br />
<pre>class TailCall(object) :<br />
def __init__(self, call, *args, **kwargs) :<br />
self.call = call<br />
self.args = args<br />
self.kwargs = kwargs<br />
def handle(self) :<br />
return self.call(*self.args, **self.kwargs)</pre><br />
<br />
This is basically just the thunk <code>lambda : call(*args, **kwargs)</code>, but we don’t use a thunk because we would like to be able to differentiate between a tail call and returning a function as a value.<br />
<br />
The next ingredient is a function which wraps a trampoline around an arbitrary function:<br />
<br />
<pre>def t(f) :<br />
def _f(*args, **kwargs) :<br />
ret = f(*args, **kwargs)<br />
while type(ret) is TailCall :<br />
ret = ret.handle()<br />
return ret<br />
return _f</pre><br />
<br />
Then, we modify fact to be<br />
<br />
<pre>def fact(n, r=1) :<br />
if n <= 1 :<br />
return r<br />
else :<br />
return TailCall(fact, n-1, n*r)</pre><br />
<br />
Now, instead of calling <code>fact(n)</code>, we must instead invoke <code>t(fact)(n)</code> (otherwise we’d just get a TailCall object).<br />
<br />
This isn’t that bad: we can get tail calls of arbitrary depth, and it’s Pythonic in the sense that the user must explicitly label the tail calls, limiting the amount of unexpected magic. But, can we eliminate the need to wrap t around the initial call? I myself find it unclean to have to write that <code>t</code> because it makes calling fact different from calling a normal function (which is how it was before the transformation).<br />
<br />
'''3. A second attempt'''<br />
<br />
The basic idea is that we will redefine fact to roughly be <code>t(fact)</code>. It’s tempting to just use <code>t</code> as a decorator:<br />
<br />
<pre>@t<br />
def fact(n, r=1) :<br />
if n <= 1 :<br />
return r<br />
else :<br />
return TailCall(fact, n-1, n*r)</pre><br />
<br />
(which, if you aren’t familiar with decorator syntax, is equivalent to writing <code>fact = t(fact)</code> right after the function definition). However, there is a problem with this in that the fact in the returned tail call is bound to <code>t(fact)</code>, so the trampoline will recursively call the trampoline, completely defeating the purpose of our work. In fact, the situation is now worse than before: on my machine, <code>fact(333)</code> causes a <code>RuntimeError</code>!<br />
<br />
For this solution, the first ingredient is the following class, which defines the trampoline as before, but wraps it in a new type so we can distinguish a trampolined function from a plain old function:<br />
<br />
<pre>class TailCaller(object) :<br />
def __init__(self, f) :<br />
self.f = f<br />
def __call__(self, *args, **kwargs) :<br />
ret = self.f(*args, **kwargs)<br />
while type(ret) is TailCall :<br />
ret = ret.handle()<br />
return ret</pre><br />
<br />
and then we modify <code>TailCall</code> to be aware of <code>TailCallers</code>:<br />
<br />
<pre>class TailCall(object) :<br />
def __init__(self, call, *args, **kwargs) :<br />
self.call = call<br />
self.args = args<br />
self.kwargs = kwargs<br />
def handle(self) :<br />
if type(self.call) is TailCaller :<br />
return self.call.f(*self.args, **self.kwargs)<br />
else :<br />
return self.call(*self.args, **self.kwargs)</pre><br />
<br />
Since classes are function-like and return their constructed object, we can just decorate our factorial function with <code>TailCaller</code>:<br />
<br />
<pre>@TailCaller<br />
def fact(n, r=1) :<br />
if n <= 1 :<br />
return r<br />
else :<br />
return TailCall(fact, n-1, n*r)</pre><br />
<br />
And then we can call fact directly with large numbers!<br />
<br />
Also, unlike in the first attempt, we can now have mutually recursive functions which all perform tail calls. The first-called <code>TailCall</code> object will handle all the trampolining.<br />
<br />
If we wanted, we could also define the following function to make the argument lists for tail calls be more consistent with those for normal function calls:[3]<br />
<br />
<pre>def tailcall(f) :<br />
def _f(*args, **kwargs) :<br />
return TailCall(f, *args, **kwargs)<br />
return _f</pre><br />
<br />
and then fact could be rewritten as<br />
<br />
<pre>@TailCaller<br />
def fact(n, r=1) :<br />
if n <= 1 :<br />
return r<br />
else :<br />
return tailcall(fact)(n-1, n*r)</pre><br />
<br />
One would hope that marking the tail calls manually could just be done away with, but I can’t think of any way to detect whether a call is a tail call without inspecting the source code. Perhaps an idea for further work is to convince Guido von Rossum that Python should support tail recursion (which is quite unlikely to happen).<br />
<br />
[1] This is compiler-writer speak. For some reason, “elimination” is what you do when you replace a computation with something equivalent. In this case, it’s true that the call is being eliminated, but in its place there’s a jump. The same is true for “common subexpression elimination” (known as CSE), which takes, for instance,<br />
<br />
<pre>a = b + c<br />
d = (b + c) + e<br />
and replaces it with<br />
a = b + c<br />
d = a + e</pre><br />
<br />
Sure, the <code>b+c</code> is eliminated from the second statement, but it’s not really gone...<br />
The optimization known as “dead code elimination” actually eliminates something, but that’s because dead code has no effect, and so it can be removed (that is, be replaced with nothing).<br />
<br />
[2] In Scheme, all loops are written as recursive functions since tail calls are the pure way of redefining variables (this is the same technique Haskell uses). For instance, to print the numbers from 1 to 100, you’d write<br />
<br />
<pre>(let next ((n 1))<br />
(if (<= n 100)<br />
(begin<br />
(display n)<br />
(newline)<br />
(next (+ n 1)))))</pre><br />
<br />
where next is bound to be a one-argument function which takes one argument, <code>n</code>, and which has the body of the <code>let</code> statement as its body. If that <code>100</code> were some arbitrarily large number, the tail call to next had better be handled as a jump, otherwise the stack would overflow! And there’s no other reasonable way to write such a loop!<br />
<br />
Continuation passing style is commonly used to handle exceptions and backtracking. You write functions of the form<br />
<br />
<pre>(define (f cont)<br />
(let ((cont2 (lambda ... (cont ...) ...)))<br />
(g cont2)))</pre><br />
<br />
along with functions which take multiple such f’s and combines them into another function which also takes a single cont argument. I’ll probably talk about this more in another page, but for now notice how the call to g is in the tail position.<br />
<br />
[3] This is basically a curried[4] version of <code>TailCall</code>.<br />
<br />
[4] That is, ''Schönfinkelized''.<br />
<br />
=== Miscellaneous ===<br />
----<br />
==== Useful Scheme Procedures ====<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=2226 Source: Spring 2014 Piazza (2226)]<br />
<br />
Here is a short list of Scheme procedures that you might use in writing your programs:<br />
<pre>; define - defines a variable or a procedure<br />
(define my-variable 4)<br />
(define (square x)<br />
(* x x))<br />
<br />
; if - conditional branching akin to if ... else<br />
(define (fib n)<br />
(if (< n 2)<br />
n<br />
(+ (fib (- n 1)) (fib (- n 2))) ))<br />
<br />
; conditional branching akin to if ... elif ... elif ... else<br />
(define (deep-map f lst)<br />
(cond ((null? lst) lst)<br />
((list? (car lst))<br />
(cons (deep-map f (car lst))<br />
(deep-map f (cdr lst))))<br />
(else<br />
(cons (f (car lst))<br />
(deep-map f (cdr lst)))) ))<br />
<br />
; and - outputs the the rightmost value if all of the arguments evaluate to #t. Outputs #f otherwise.<br />
STk> (and 0 1 2 3)<br />
3<br />
<br />
; or - outputs the the first value that evaluates to #t. Outputs #f otherwise.<br />
STk> (or 0 1 2 3)<br />
0<br />
<br />
; equal - tests if symbols are the same<br />
STk> (equal? 'foo 'bar)<br />
#f<br />
STk> (equal? 'foo 'foo)<br />
#t<br />
<br />
STk> (list? 'foo)<br />
#f<br />
STk> (list? '(1 2 3))<br />
#t<br />
STk> (list? '())<br />
#t<br />
<br />
STk> (null? '(1 2 3))<br />
#f<br />
STk> (null? ())<br />
#t<br />
<br />
; member? - tests if a symbol is in a list EDIT: NOT BUILT IN (BUT SUPER USEFUL SEE FOLLOWUP)<br />
STk> (member? 'quick '(the quick brown fox jumped over the lazy dog))<br />
#t<br />
<br />
; number? - checks if input is a number<br />
STk> (number? 42)<br />
#t<br />
STk> (number? #t)<br />
#f<br />
<br />
; remainder - computes the remainder of the first number divided by the second<br />
STk> (remainder 100 21)<br />
16</pre><br />
<br />
== Streams ==<br />
== Logic ==<br />
=== Quick Guide to Logic Programming ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=2524 Source: Spring 2014 Piazza (2524)]<br />
''Note: Someone should convert this from scmlog to Logic notation''<br />
<br />
'''Here's something I wrote a long time ago. The logic interpreter scmlog still exists and you should be able to access it using your cs61a-xx account. Post a followup if you have any questions. Hope this helps!'''<br />
<br />
<br />
'''Introduction:''' logic programming is a completely different way to think about telling computers to do stuff. Instead of telling the computer what to compute, you give the computer facts and ask it questions. The computer does its own thinking given the facts and the question, and then returns an answer.<br />
<br />
'''Table of Contents'''<br />
- Introduction<br />
- What is Logic Programming<br />
- - Giving Facts and Asking Questions<br />
- - More Complicated Facts and Questions<br />
- How to write Logic Programs<br />
- - Common Pitfalls<br />
- More Resources<br />
<br />
<br />
'''What is Logic Programming?'''<br />
Logic Programming is a way to ask the computer questions and get answers without telling it explicitly how to reach the conclusion. Kind of like this: http://youtu.be/tpKx7Oi0oeM<br />
<br />
Anyways, we have a logic programming interpreter called Scmlog. It interprets a simpler version of the logic programming language, Prolog. The first few things to understand about it is that '''SCMLOG IS NOT SCHEME'''. It just happens to look like it. Scmlog is its own language and so it has its own rules. You can't program in Scmlog like you would in Scheme or Python, so it might be good to forget what you know about those languages for a second.<br />
<br />
When you fire up scmlog (type scmlog into a terminal on the school computers), you get this prompt:<br />
<pre>star [501] ~ # scmlog<br />
scmlog (Prolog in Scheme), v. 0.2<br />
Type 'help' for help; 'quit' to exit.<br />
?- </pre><br />
You can interact with this prompt in two ways:<br />
# Give facts - "Let's tell the computer some things it should know!"<br />
# Ask questions - "Let's ask the computer questions about the things we told it about!"<br />
<br />
'''Giving Facts and Asking Questions'''<br />
A basic fact takes this form: <code>(fact (assertion))</code>, '''Where each assertion is simply a relation between things.'''<br />
<br />
For example: <code>(fact (likes potstickers brian))</code> relates three ideas, liking something, potstickers, and some guy name Brian. "Brian likes potstickers."<br />
Note that we put the relation first, and then the parties the relation acts upon. In this example, "likes" is the relation, it hooks up "brian" and "potstickers". Here's some more:<br />
<pre>?- (fact (likes potstickers brian)) <br />
?- (fact (likes potstickers andrew)) <br />
?- (fact (likes the_beatles brian)) <br />
?- (fact (likes the_beatles andrew)) <br />
?- (fact (likes led_zeppelin andrew)) <br />
?- (fact (dislikes led_zeppelin brian))</pre><br />
Now that we've given the computer a bunch of facts, how do we ask questions about them? Just replace "fact" with a "?", and replace any part of the relation (besides the relation itself) with a variable prefixed by an underscore. This is called "querying":<br />
<pre>?- (? (likes _what brian))<br />
_what : potstickers<br />
More? <br />
_what : the_beatles<br />
More? <br />
?- (? (likes potstickers _who))<br />
_who : brian<br />
More? <br />
_who : andrew<br />
More? <br />
?- (? (dislikes _what brian))<br />
_what : led_zeppelin<br />
More?<br />
?- (? (likes led_zeppelin andrew))<br />
Yes.<br />
?- (? (dislikes the_beatles _who))<br />
No.</pre><br />
Notice:<br />
* We can query any part of the assertion, besides the relation itself (can't replace "likes" with a variable).<br />
* All possible answers to the question show up.<br />
* If Scmlog couldn't find a fact that matched your query, it'll say "No."<br />
* Asking a question without any variables essentially asks if that fact exists. To which Scmlog will answer "Yes." or "No."<br />
<br />
'''More Complicated Facts and Questions'''<br />
Now this isn't the whole picture. We also have the ability to make more powerful assertions via variables, hypotheses, and conclusions:<br />
<br />
<code>(fact (ancestor _x _y) (parent _x _y))</code> "X is an ancestor of Y if X is a parent of Y"<br />
<br />
Here _x and _y are variables like usual. However, we have two parts to this fact, the conclusion ("X is an ancestor of Y") and the hypothesis ("X is a parent of Y"). We can have more than one hypotheses, and they can be any kind of query. Here's ancestor in action:<br />
<pre>?- (fact (parent george paul))<br />
?- (fact (parent martin george))<br />
?- (fact (parent martin martin_jr))<br />
?- (fact (parent martin donald))<br />
?- (fact (parent george ann))<br />
?- (fact (ancestor _X _Y) (parent _X _Y))<br />
?- (fact (ancestor _X _Y) (parent _X _Z) (ancestor _Z _Y))<br />
?- (? (ancestor paul george))<br />
No.<br />
?- (? (ancestor george paul))<br />
Yes.<br />
?- (? (ancestor george george))<br />
No.<br />
?- (? (ancestor martin paul))<br />
Yes.</pre><br />
Now there's one more thing you need to know about Scmlog. Scmlog knows about pairs and lists:<br />
<pre>?- (fact (lst (1 2 3)))<br />
?- (? (lst _x))<br />
_x : (1 2 3)<br />
More?<br />
?- (? (lst (1 . _x)))<br />
_x : (2 3)<br />
More? <br />
?- (? (lst (1 2 . _x)))<br />
_x : (3)<br />
More? <br />
?- (? (lst (_x . _y)))<br />
_x : 1<br />
_y : (2 3)<br />
?- (fact (my_pair (2 . 3)))<br />
?- (? (my_pair _y))<br />
_y : (2 . 3)<br />
?- (? (my_pair (2 . _x)))<br />
_x : 3</pre><br />
<br />
'''How to write Logic Programs'''<br />
<br />
The trick behind writing logic programs is to forget everything you know about programming. You instead want to focus on the relation you're trying to establish. Take append for example:<br />
<pre>?- (fact (append () _b _b))<br />
?- (fact (append (_x . _rest) _b (_x . _z)) (append _rest _b _z))<br />
?- (? (append (1 2 3) (3 2 1) _answer))<br />
_answer : (1 2 3 3 2 1)<br />
?- (append (1 2) 3 (1 2 3))<br />
Huh?<br />
?- (? (append (1 2) 3 (1 2 3))<br />
)<br />
No.<br />
?- (? (append (1 2) (3) (1 2 3))<br />
)<br />
Yes.<br />
?- (? (append (1 2) (3) (1 2 3)))<br />
Yes.<br />
?- (? (append _x (3 4 9) (1 0 3 2 3 4 9)))<br />
_x : (1 0 3 2)<br />
More? <br />
?- (? (append _x _y (2 1)))<br />
_x : ()<br />
_y : (2 1)<br />
More? <br />
_x : (2)<br />
_y : (1)<br />
More? <br />
_x : (2 1)<br />
_y : ()</pre><br />
<br />
What is append? Well the first fact establishes that if we append an empty list to something, the result is that something.<br />
The next fact is the meat of the code. You can almost think of this as a recursive relation. Any append relation between some three lists a b c must also fulfill another append relation between the rest of a, b, and the rest of c, respectively. Think about why that will always be true for all inputs. If the example above doesn't make sense to you, see the following:<br />
<br />
<br />
'''Common Pitfalls'''<br />
<br />
'''- One of the biggest issues people have with writing logic programs is that they don't realize that the variables don't work like they do in Scheme or Python. All Scmlog ever does is pattern match:'''<br />
<pre>?- (fact (x (0 1 8)))<br />
?- (fact (x (1 8 4)))<br />
?- (fact (x (9 4 4)))<br />
?- (fact (x (3 0 8)))<br />
?- (? (x _y))<br />
_y : (0 1 8)<br />
More? <br />
_y : (1 8 4)<br />
More? <br />
_y : (9 4 4)<br />
More? <br />
_y : (3 0 8)<br />
?- (? (x (0 1 . _z)))<br />
_z : (8)</pre><br />
<br />
- SCMLOG IS NOT SCHEME. SCMLOG IS NOT PYTHON.<br />
<br />
More Resources:<br />
* http://www-inst.eecs.berkeley.edu/~cs61a/sp12/lectures/prolog/ReadMe<br />
* http://www-inst.eecs.berkeley.edu/~cs61a/sp12/lectures/prolog/<br />
* http://www-inst.eecs.berkeley.edu/~cs61a/sp12/discussion/week14/<br />
* http://www-inst.eecs.berkeley.edu/~cs61a/fa13/slides/31-Logic_6pp.pdf<br />
<br />
=== Logic Mathematics ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=3050 Source: Spring 2014 Piazza (3050)]<br />
<br />
'''Student Question'''<br />
<br />
Can someone explain the intuition behind the implementation of the increments and the addition facts? The following is from Mark's website<br />
<br />
<pre>(fact (increment 0 1))<br />
(fact (increment 1 2))<br />
(fact (increment 2 3))<br />
(fact (increment 3 4))<br />
(fact (increment 4 5))<br />
(fact (increment 5 6))<br />
(fact (increment 6 7))<br />
(fact (increment 7 8))<br />
(fact (increment 8 9))<br />
(fact (increment 9 10))<br />
(fact (increment 10 11))<br />
(fact (increment 11 12))<br />
(fact (increment 12 13))<br />
<br />
(fact (add 1 ?x ?x+1)<br />
(increment ?x ?x+1))<br />
<br />
(fact (add ?x+1 ?y ?z+1)<br />
(increment ?x ?x+1)<br />
(increment ?z ?z+1)<br />
(add ?x ?y ?z))<br />
<br />
(query (add 2 4 6))<br />
; expect Success!</pre><br />
<br />
'''Student Answer'''<br />
<br />
First, we state a bunch of facts that denote relations between a number and the number that follows it (e.g. 1 and 2, 2 and 3, and so on). This is the increment fact.<br />
<br />
Then, we state a "base" fact, which is:<br />
<pre>(fact (add 1 ?x ?x+1)<br />
(increment ?x ?x+1))</pre><br />
In English, this fact states that some value <code>?x</code> added to 1 will give us some value <code>?x+1</code> (<code>x+1</code> is a valid variable name in Logic) '''if and only if''' the fact (<code>increment ?x ?x+1</code>) is true. As an example, <code>(add 1 2 3)</code> is true '''if and only if''' (<code>increment 2 3</code>) is true. This is true because of the increment facts we stated before.<br />
<br />
Now, to deal with additions that aren't just the sum of two numbers in which one number is a 1, we need:<br />
<pre>(fact (add ?x+1 ?y ?z+1)<br />
(increment ?x ?x+1)<br />
(increment ?z ?z+1)<br />
(add ?x ?y ?z))</pre><br />
I will try to give an intuition as to what is happening here.<br />
<br />
We state our fact: that two numbers, <code>?x+1</code> and <code>?y</code>, will add up to some number <code>?z+1</code> if and only if the following 3 hypotheses are true:<br />
* (<code>increment ?x ?x+1</code>) - "There exists some number <code>?x</code> that is 1 less than <code>?x+1</code>, and"<br />
* (<code>increment ?z ?z+1</code>) - "There exists some number <code>?z</code> that is 1 less than <code>?z+1</code>, and"<br />
* (<code>add ?x ?y ?z</code>) - "The numbers <code>?x</code> and <code>?y</code> will add up to <code>?z</code>."<br />
<br />
Consider the example of:<br />
<pre>(query (add 2 4 6))</pre><br />
Here's an idea of what's happening when Logic tries to match the query with the facts you've stated.<br />
* ?x+1 = 2, ?y = 4, ?z+1 = 6<br />
* It finds a match for <code>?x = 1</code>, since (<code>increment ?x ?x+1</code>) gives <code>?x = 1</code> because <code>?x+1 = 2</code><br />
* It finds a match for <code>?z = 5</code>, since (<code>increment ?z ?z+1</code>) gives <code>?z = 5</code> because <code>?z+1 = 6</code><br />
* It then checks for a match for (<code>add ?x ?y ?z</code>) which in this case is (<code>add 1 4 5</code>). This goes to our first "base" fact for add. (<code>add 1 4 5</code>) is a success because (<code>increment 4 5</code>) is a true fact [refer again to the "base fact" to see why this is the case]. This is also where we would get a "Failed.", if it turns out that (<code>increment ?x ?x+1</code>) wasn't actually true!<br />
* Hence, all 3 of our hypotheses are true, and so (<code>query (add 2 4 6)</code>) is a success!<br />
<br />
In this example, we only have to recurse once to get to our "base" fact. In other examples, where <code>?x+1</code> is not 2, but some number greater, such as 5, we will have to recurse 4 whole times to get to 1, at which point our "base" fact is reached. <br />
<br />
This recursion is similar to this idea in mathematical equations:<br />
<pre>x + y = z</pre><br />
is the same as<br />
<pre>(x - 1) + y = (z - 1)</pre><br />
is the same as<br />
<br />
<pre>(x - 2) + y = (z - 2)</pre?<br />
and so on... In Logic, we stop when we find that the first term (x) is 1, and then we use our increment facts to determine if the original statement is true, because all of these equations are equivalent.<br />
<br />
== Python syntax and semantics ==<br />
=== <code>print</code> vs <code>return</code> ===<br />
----<br />
==== Andrew's tips ====<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=779 Source: Spring 2014 Piazza (779)]<br />
<br />
Remember the differences between return and print.<br />
* <code>return</code> can only be used in a <code>def</code> statement. It returns a value from a function. Once Python evaluates a <code>return</code> statement, it immediately exits the function.<br />
* <code>print</code> is a function that displays its argument on the screen. It always returns <code>None</code>.<br />
<br />
Examples:<br />
<pre>def foo1(x):<br />
return x<br />
<br />
def foo2(x):<br />
print(x)<br />
<br />
>>> foo2(1) # In foo2, we print 1 ourselves using the print function<br />
1<br />
>>> foo1(1) # HERE, THE PYTHON INTERPRETER PRINTS THE RETURN VALUE OF FOO1. CANNOT STRESS HOW IMPORTANT TO UNDERSTAND THIS<br />
1<br />
>>> foo1(1) + 1<br />
2<br />
>>> foo2(1) + 1<br />
1<br />
Traceback (most recent call last):<br />
File "<stdin>", line 1, in <module><br />
TypeError: unsupported operand type(s) for +: 'NoneType' and 'int'</pre><br />
<br />
=== Function decorators ===<br />
<br />
----<br />
<br />
==== How function decorators work ====<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=638 Source: Spring 2014 Piazza (638)]<br />
<br />
'''Student Question'''<br />
<br />
I'm having difficulties understanding what exactly a function decorator is. Can someone elaborate and potentially provide me with an example other than the one in the readings?<br />
<br />
'''Instructor Answer'''<br />
<br />
So imagine you wanted your functions to print their arguments before they executed them. Here's one way to do this.<br />
<pre>def loud(fn):<br />
def new_fn(*args):<br />
print(args)<br />
return fn(*args)<br />
return new_fn </pre><br />
Here's a function loud that takes in a function and returns a new function that when called, prints out its arguments, and then does what the old function does.<br />
<br />
For example:<br />
<pre>def sq(x):<br />
return x * x<br />
>>> sq(4)<br />
16<br />
>>> sq = loud(sq) # replace the old square with our loud one.<br />
>>> sq(4)<br />
(4,)<br />
16</pre><br />
A function decorator does the same thing as the above. Assuming loud is defined, we can do this:<br />
<pre>@loud<br />
def sq(x):<br />
return x * x<br />
<br />
>>> sq(4)<br />
(4,)</pre><br />
<br />
== Student guides ==<br />
=== How to learn computer science ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=241 Source: Spring 2014 Piazza (241)]<br />
<br />
If you've never programmed before, or if you've never taken a class quite like 61A before, things right now might be scary. Everything is strange and new and there quite a lot to take in all at once. So if you're having a hard time so far, here are a few articles that might help.<br />
<br />
Note: these articles are pretty long, so feel free to read them in multiple sittings.<br />
<br />
'''At the beginning, everything seems a bit scary in CS'''. Michelle Bu, a Berkeley alum and a crazy good hacker, shares one of her experiences when she was a wee n00b in [http://blog.michellebu.com/2013/03/21-nested-callbacks/ 21 Nested Callbacks].<br />
<br />
'''Start here!''' [http://www.jamesmaa.com/2013/08/26/a-beginners-guide-to-computer-science/ "A Beginner's Guide to Computer Science"] Written by Berkeley's own James Maa. James is known for his killer walkthroughs (check out his Productivity guide). This article gives you some background on learning CS and then provides a practical guide on how to learn effectively.<br />
<br />
'''How do we learn?''' Mark Eichenlaub explains in this [http://www.quora.com/Learning/Do-grad-school-students-remember-everything-they-were-taught-in-college-all-the-time/answer/Mark-Eichenlaub Introduction to Learning Theory]. This is quite possibly the best introduction to Learning Theory.<br />
<br />
'''Sometimes, you're stuck and you end up really, really frustrated.''' Marcus Geduld explains [http://www.quora.com/Why-do-we-get-frustrated-when-learning-something/answer/Marcus-Geduld Why do we get frustrated when learning something?]<br />
<br />
=== Quick guide on getting unstuck ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=1264 Source: Quick Guide on Getting Unstuck (Retrieved June 16th, 2014)]<br />
<br />
A major frustration you might encounter in 61A is when you stare at a homework problem and have no idea where to start. Or you write some code and it doesn't pass the doctests, but now what? You work at it for a while, but next thing you know, you've been stuck for hours on the the same problem and have little to show about it.<br />
<br />
So here's a checklist of things you can do when you're stuck. Experienced programmers do these things almost naturally (because of how much practice they've had being stuck), and so while they get stuck just as much as your or I, they always know what to do next.<br />
<br />
# Do I understand what the problem is asking?<br />
## If not, which part of the problem is confusing me?<br />
### Identify the exact sentences/phrases/words/etc.<br />
## Check the given examples. Do they make sense to me?<br />
## Can I come up with my own examples? '''A good indicator that you understand the question is that you can come up with some nontrivial examples of how the function works.'''<br />
# What concepts should I use here?<br />
## Do I understand the concepts? '''Can I explain the concept in English to one of my friends such that they get it?'''<br />
### If not, go back and relearn the specific concepts that are unclear (through discussion, lab, lecture, etc.) Don't read the entire book in order to solve one problem..<br />
## How do I apply the concept to the given problem?<br />
# Write your code and test it.<br />
## Use doctests, '''BUT ALSO LOAD IT INTERACTIVELY (python3 -i ...)'''<br />
### '''Saying "my function works because the doctests pass" is a lot like saying "this airplane will fly because it has wings."'''<br />
## If your code breaks, ask yourself:<br />
### Does it error? Is it a....<br />
#### Syntax error? If so, find the syntax bug and fix it.<br />
#### Logic error? Is it something weird that you don't understand? (E.g. cannot add integer and tuple)<br />
### Why did it do that? Why didn't it do what I expected? Trace through the code by hand with an example (sample values) you came up with in step 0. '''Add calls to <code>print</code> in order to figure out how your function is handling the arguments.'''<br />
# Am I missing a trick?<br />
## Oftentimes you've never seen this type of problem before. This is expected on homework (and this is why homework can take a long time) because if you see it on the homework, then you will be familiar with it on the exam and when you program for fun and profit. <br />
## The key here is just to learn the trick however you need to.<br />
### Stare at it yourself<br />
### Stare at it with others (peers in the class)<br />
### Ask on PIazza what the approach is.<br />
### Stare at it with the TAs/lab Assistants<br />
## '''Once you figure it out, remember the trick so that you can use it next time.'''<br />
# At any point you identify what you're stuck on, you can begin to resolve it.<br />
## Use the tips above. Try things out on the interpreter. Review the lecture/discussion/labs/etc. Do whatever helps you get a better understanding of the problem.<br />
## Once you have something specific that you're stuck on, you can ask other people in the class.<br />
### '''Don't be afraid to ask. Everyone gets stuck and feels stupid sometimes. However, you get to choose how you react to it.'''<br />
### '''At the same time, it really helps to work with people who are on about the same level in the course.'''<br />
## Look on Piazza. Ask questions if yours hasn't come up yet. Be that awesome guy/girl who helps answer questions.<br />
## You can ask the TA if all else fails. We are here to help you learn!<br />
<br />
Here is an old algorithm for studying for tests (the final in this case), salvaged from the sands of time:<br />
<pre>For each topic on the final, find problems on them and do them.<br />
If you can solve them on your own, move on.<br />
Else if you are stuck, look at the solution and figure out if you<br />
are missing a trick or if you do not understand the concepts.<br />
If the problem is that you are stuck on some random trick,<br />
just learn the trick.<br />
Stare at the solutions, ask Piazza, your TA, etc.<br />
Questions you should ask at this stage:<br />
What is the problem asking me to do?<br />
How was I suppose to follow the instructions<br />
to solve the problem?<br />
What part of the problem do I not understand?<br />
What is the fastest way to clear up that misunderstanding?<br />
Then if you think you are still stuck conceptually, review<br />
and learn the concept, however you learn best.<br />
Suggestions for picking up concepts quickly (~1-2 hours):<br />
Discussion notes typically have a very concise recap of the<br />
thing they are going over.<br />
There are guides for particularly tricky things on Piazza,<br />
like Logic, Pairs and Lists in Scheme, etc.<br />
Find them and go over them.<br />
Ask a TA: "what is the best way to learn X?"<br />
If these do not work and you are still shaky after an hour<br />
or two, it might be worth watching a lecture or reading<br />
the notes.</pre><br />
<br />
== Composition ==<br />
=== General style guidelines from 61A website ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=149 Source: Spring 2014 Piazza (149)]<br />
<br />
'''Student Question'''<br />
<br />
Are we required to add any comments to our code to say what a function does, etc.? And does clarity of code count for this project, in which case should we write comments at the end of not-so-clear statements? Thanks.<br />
<br />
'''Student Answer'''<br />
<br />
Docstrings of each function are already provided. If you add a helper function, you should write a docstring for it.<br />
<br />
The [http://inst.eecs.berkeley.edu/~cs61a/sp14/style_guide.html#comments style guide on the course website] advises: "Your actual code should be self-documenting -- try to make it as obvious as possible what you are doing without resorting to comments. Only use comments if something is not obvious or needs to be explicitly emphasized"<br />
<br />
'''Instructor Answer'''<br />
<br />
You should always aim to make your code "self-documenting," meaning it is clear what your code is doing without the aid of comments. You should try to keep the number of comments to a minimum, but if there are lines which you think are unclear/ambiguous, feel free to add a comment.<br />
<br />
All projects in this class contain a 3 point component that is judged solely on your code "composition" -- i.e. whether your code is clear, concise, and easy to read.<br />
<br />
=== Simplifying code ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=1116 Source: Spring 2014 Piazza (1116)]<br />
<br />
Hi everyone, here's some tips about certain functions in Python that can greatly simplify your code for the Trends project.<br />
<br />
'''Sorting keys'''<br />
<br />
You should be familiar with the max and min functions in python, which can take in many arguments and return the maximum value.<br />
<br />
<pre>>>> max(1,3,2)<br />
3</pre><br />
<br />
These functions can also take in lists:<br />
<br />
<pre>>>> min([1,5,1,6])<br />
1</pre><br />
<br />
(In fact they can take in any ''iterable'' and return the maximum/minimum value)<br />
<br />
These functions work because Python knows how to compare the elements in the list (they are all integers). But what if the elements in the list are not integers? Fortunately, there is a way for you to tell Python how to turn each element of the list into a number that it can understand.<br />
<br />
Lets start with an example. Lets say you have a list of strings, and want to find the shortest string in the list. Here's what you can do:<br />
<br />
<pre>>>> min(['hihi', 'bye', 'a', 'zebra'], key=len)<br />
'a'</pre><br />
<br />
Notice the new keyword argument key we are passing into the min function. key is a function that min applies to each element of the list. In this case, the key is the len function, which returns the length of each string. Applying the key function to each element will return a cooresponding integer, which Python can easily use to find the minimum element.<br />
<br />
You can also use keys in the <code>sorted</code> function too, which returns a sorted list of its inputs, based on the key function passed in.<br />
<br />
<pre>>>> sorted(['hihi', 'bye', 'a', 'zebra'], key=len)<br />
['a', 'bye', 'hihi', 'zebra']</pre><br />
<br />
We can have more complex key functions. Here we sort a list of people by their age, which is the second element in the tuple. A key function, once defined, works for sorted, min and max:<br />
<br />
<pre>>>> names = [('Alice', 19, 'F'), ('Bob', 5, 'M'), ('Charlie', 12, 'M')]<br />
>>> get_age = lambda name: name[1]<br />
>>> sorted(names, key=get_age)<br />
[('Bob', 5, 'M'), ('Charlie', 12, 'M'), ('Alice', 19, 'F')]<br />
>>> max(names, key=get_age)<br />
('Alice', 19, 'F')</pre><br />
<br />
'''Dictionary default values'''<br />
<br />
Suppose we have a dictionary mapping names to counts:<br />
<br />
<pre>>>> d = {'apples': 1, 'pears': 9000}</pre><br />
<br />
If we want to add a new pear to the dictionary, we can use:<br />
<br />
<pre>>>> d['pears'] = d['pears'] + 1<br />
>>> d<br />
{'apples': 1, 'pears': 9001}</pre><br />
However we cannot use the same code to add a new item that is not already in the dictionary.<br />
<br />
<pre>>>> d['oranges'] = d['oranges'] + 1<br />
Traceback (most recent call last):<br />
...<br />
KeyError: 'oranges'</pre><br />
<br />
To solve this problem, we have to use <code>dict.setdefault(key, default)</code>. If <code>key</code> is in <code>dict</code>, it will return <code>dict[key]</code>. If not, it will insert <code>key</code> with a value of <code>default</code> and return <code>default</code>. Now we can write:<br />
<br />
<pre>>>> d['oranges'] = d.setdefault('oranges', 0) + 1<br />
>>> d<br />
{'oranges': 1, 'apples': 1, 'pears': 9001}<br />
>>> d['oranges'] = d.setdefault('oranges', 0) + 1<br />
>>> d<br />
{'oranges': 2, 'apples': 1, 'pears': 9001}</pre><br />
<br />
There's actually a even better way of doing this. If you are curious to find out, look up <code>collections.defaultdict</code>.<br />
<br />
'''For loops'''<br />
<br />
If you are iterating through a list and want to get both the item and the index the item is at, the built-in function enumerate is helpful here.<br />
<br />
<pre>>>> a = ["apple", "pear", "orange"]<br />
>>> for index, fruit in enumerate(a):<br />
... print(index, fruit)<br />
...<br />
0 apple<br />
1 pear<br />
2 orange</pre><br />
<br />
You can iterate through each key-value pair in a dictionary with dictionary.items. This is useful if you want to access both the key and the value at the same time.<br />
<br />
<pre>>>> prices = {"apple": 3, "pear": 5, "orange": 20}<br />
>>> for fruit, price in prices.items():<br />
... print(fruit, price)<br />
...<br />
apple 3<br />
pear 5<br />
orange 20</pre><br />
<br />
Hope this helps for the project!<br />
<br />
=== Programming style in scheme ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=2068 Source: Spring 2014 Piazza (2068)]<br />
<br />
Since Scheme has no rules on whitespace and indentation, you could technically write all your Scheme in one line like this:<br />
<pre>(define (fib n) (if (< n 1) n (+ (fib (- n 1)) (fib (- n 2)))))</pre><br />
<br />
But that would be terrible and everyone who had to read your code would hate you for it. Here is a more reasonable version:<br />
<pre>(define (fib n)<br />
(if (< n 1) ; Arguments to if are flush<br />
n ; Each argument gets a new line<br />
(+ (fib (- n 1)) ; Sometimes it makes to insert a newline<br />
(fib (- n 2))))) ; so that you can see arguments side by side</pre><br />
<br />
Remember that code is primarily for humans to read and incidentally for computers to run.<br />
<br />
Here are some more examples:<br />
<pre>(define (deep-map f lst)<br />
(cond ((null? lst) lst)<br />
((list? (car lst))<br />
(cons (deep-map f (car lst))<br />
(deep-map f (cdr lst))))<br />
(else<br />
(cons (f (car lst))<br />
(deep-map f (cdr lst)))) ))<br />
<br />
STk> (deep-map (lambda (x) (* x x)) (list 1 2 (list (list 3) 4)))<br />
(1 4 ((9) 16))</pre><br />
<br />
<pre>(define (reverse lst)<br />
(define (helper lst result)<br />
(if (null? lst)<br />
result<br />
(helper (cdr lst)<br />
(cons (car lst) result)) ))<br />
(helper lst ()) )<br />
<br />
STk> (reverse (list 1 2 3))<br />
(3 2 1)</pre><br />
<br />
=== ucb.py's <code>trace</code> method ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=3000 Source: Spring 2014 Piazza (3000)]<br />
<br />
'''Student Question'''<br />
<br />
How does the trace in ucb.py actually work?<br />
<br />
'''Student Answer'''<br />
<br />
It's actually very similar to the <code>printed</code> function that was defined in the Hog spec!<br />
<br />
<pre>def printed(fn):<br />
def print_and_return(*args):<br />
result = fn(*args)<br />
print('Result:', result)<br />
return result<br />
return print_and_return<br />
</pre><br />
<br />
The main idea is still the same in <code>trace</code> -- we want to figure out all the arguments and save the result of calling the function with those arguments (so we can print it before returning it). One (sort of) major improvement is <code>**kwds</code>. Just like how <code>*args</code> collected all the "positional arguments", <code>**kwds</code> captures all the "keyword arguments" (the ones of the form <code>param='some_val'</code>). This can be a bit confusing, but a couple of examples might help!<br />
<br />
<pre>&gt;&gt;&gt; def add_three(a, b, c):<br />
... return a + b + c<br />
&gt;&gt;&gt; add_three(1, 2, 3) # all arguments are positional (normal)<br />
&gt;&gt;&gt; add_three(1, 2, c=3) # a, b are positional arguments, c is a keyword argument<br />
<br />
&gt;&gt;&gt; def fn(*args, **kwargs):<br />
... print(args)<br />
... print(kwargs)<br />
&gt;&gt;&gt; fn(1, 2, 3)<br />
(1, 2, 3)<br />
{}<br />
&gt;&gt;&gt; fn(1, k=2)<br />
(1,)<br />
{'k' : 2}<br />
&gt;&gt;&gt; fn(a=1, b=2, c=3)<br />
()<br />
{'a':1, 'b':2, 'c':3}<br />
</pre><br />
<br />
Since there are only two types of arguments, having both <code>*args</code> and <code>**kwds</code> covers all our bases. If we passed <code>printed</code> a keyword argument, it could cause an error!<br />
<br />
Everything else in <code>trace</code> just makes the output prettier and more helpful. <code>trace</code> uses the <code>_PREFIX</code> global variable to keep track of how far to indent the next print statement. It catches exceptions and prints them out, before re-raising that exception. It also uses some Python black magic to figure out the name of the function so we can print <code>some_fn</code> instead of <code>&lt;function some_fn at 0x...&gt;</code>.<br />
<br />
If there's a particular aspect of <code>trace</code> that you're confused about, feel free to post a followup!<br />
<br />
== Debugging ==<br />
== Miscellaneous ==<br />
=== Andrew Huang's tips ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=779 Source: Spring 2014 Piazza (779)]<br />
<br />
Order of evaluation matters. The rules for evaluating call expressions are<br />
<br />
# Evaluate the operator<br />
# Evaluate the operands<br />
# Call the operator on the operands (and draw a new frame...)<br />
<br />
For example:<br />
<pre>def baz():<br />
print("this was first")<br />
def bar(x):<br />
print(x)<br />
return lambda x: x * x<br />
return bar # baz is a function that when called, returns a function named bar<br />
<br />
>>> baz() # the operator is baz, there are no operands<br />
this was first<br />
<function bar at 0x2797e20><br />
>>> baz()("this was second") # the operator is baz(), the operand is "this was second"<br />
this was first<br />
this was second<br />
<function <lambda> at 0x2120e20><br />
>>> baz()("this was second")(3) # the operator is baz()("this was second"), the operand is 3<br />
this was first<br />
this was second<br />
9<br />
>>> def bar(x):<br />
... print(x)<br />
... return 3<br />
... <br />
>>> baz()("this was second")(bar("this was third")) # the operator is baz()("this was second"), the operand is bar("this was third")<br />
this was first<br />
this was second<br />
this was third<br />
9</pre><br />
<br />
In order to solve any problem, you must first understand what the problem is asking. Often times it helps to try to explain it concisely in English. It also helps to come up with small examples. For example:<br />
<br />
<pre>def mouse(n):<br />
if n >= 10:<br />
squeak = n // 100<br />
n = frog(squeak) + n % 10<br />
return n<br />
<br />
def frog(croak):<br />
if croak == 0:<br />
return 1<br />
else:<br />
return 10 * mouse(croak+1)<br />
<br />
mouse(21023508479)</pre><br />
<br />
So the goal is to figure out what <code>mouse(21023508479)</code> evaluates to.<br />
<br />
One way is to just step-by-step evaluate this, as an interpreter would.<br />
<br />
Another way, is to understand what the functions are doing.<br />
<br />
Looking at <code>mouse</code>, we see that it takes in a number and outputs that same number if it is smaller than 10. otherwise, it'll return something weird. In order to understand that weird thing, we have to understand what <code>frog</code> is doing. <code>frog</code> takes in a number and if that number is <code>0</code>, return <code>1</code>. Otherwise, return ten times <code>mouse(croak+1)</code>. Well, this is still confusing. Let's try a small example.<br />
<pre>>>> mouse(357)<br />
47<br />
>>> mouse(123)<br />
23<br />
>>> mouse(1234)<br />
44<br />
>>> mouse(12345)<br />
245</pre><br />
There is a pattern. We notice that the resulting number is composed of every other digit of the original, plus one (except for the last one.)<br />
So <code>21023508479</code> is <code>[2+1][0+1][3+1][0+1][4+1][9] = 314159</code>. Can you see how the code reflects that?<br />
However in this particular example, the pattern is definitely tricky to find here, so it might make more sense to brute force it.<br />
<br />
<br />
Remember for recursion, you always need to find three things:<br />
* One or more base cases<br />
* One or more was to reduce the problem<br />
* A way to solve the problem given solutions to smaller problems<br />
For example, the discussion notes, we asked you to write count_stairs. This function takes in n, the number of steps, and returns all the ways you can climb up them if at each step, you can take either one or two steps.<br />
<br />
* Base cases: if we consider n to be the number of steps left to climb, then it makes sense that if there is 1 step left, then there is exactly one way. If there are two steps left, then there are exactly 2 ways (1 step, 1 step, or two steps). Why do we need two base cases here?<br />
* We can make the problem smaller by reducing the n. At each step, we can take one step (resulting in count_stairs(n-1)) or two steps (count_stairs(n-2)).<br />
* Assuming we get the solutions to the two recursive calls, we should add them together to get all the ways we can climb the stairs.<br />
<br />
Thus we end up with<br />
<pre>def count_stairs(n):<br />
if n <= 2:<br />
return n<br />
else:<br />
return count_stairs(n-1) + count_stars(n-2)</pre><br />
Notice that at each stair step, we either take one step or two steps. This is a common pattern in tree recursion. Look through Discussion 3 for more info.<br />
<br />
=== Y combinators (in Scheme) ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=2450 Source: Spring 2014 Piazza (2450)]<br />
<br />
'''Student Question'''<br />
<br />
Can someone explain this to me?<br />
<pre>scm> (((lambda (f) (lambda (x) (f f x)))<br />
(lambda (f k) (if (zero? k) 1 (* k (f f (- k 1)))))) 5)</pre><br />
I've edited the code as follows:<br />
<br />
<pre>(<br />
(<br />
(lambda (f) <br />
(lambda (x) (f f x))<br />
)<br />
(lambda (f k) <br />
(if (zero? k) 1 <br />
(* k (f f (- k 1)))<br />
)<br />
)<br />
) 5<br />
)</pre><br />
<br />
My understanding is that the second lambda function is passed as the first <code>f</code> in the first lambda function and the <code>5</code> is passed in as <code>x</code>. But does that mean <code>f f x</code> becomes the second lambda function with itself and <code>x</code> passed as the arguments to <code>(f k)</code>?<br />
<br />
'''Student Answer'''<br />
<br />
You're on the right track. The first lambda function is a higher order function that takes in a function, and then returns a function that takes one argument. It's actually the third lambda that is then passed into the first lambda (currying!) and then 5 is then passed into the resulting function.<br />
<br />
In case you're curious, this is the Python equivalent:<br />
<br />
<pre>>>> (lambda f: (lambda x: f(f, x))) (lambda f, k: 1 if k == 0 else (k * f(f, k - 1)))(5)</pre><br />
Which is then equivalent to:<br />
<br />
<pre>>>> def func1(f):<br />
def func2(x):<br />
return f(f, x)<br />
return func2<br />
>>> def func3(f, k):<br />
if k == 0:<br />
return 1<br />
else:<br />
return k * f(f, k - 1)<br />
>>> func1(func3)(5)<br />
120</pre><br />
By the way, this is just a fancy way of recursively calculating the factorial using only lambda functions. If you're still curious as to how this works, you could try this in Python tutor. Except I would recommend calculating 3! instead of 5, because it's a lot of frames.<br />
<br />
'''Instructor Answer'''<br />
<br />
Maybe it will look a little nicer in Python:<br />
<br />
<pre>(lambda f: lambda x: f(f, x))(lambda g, k: 1 if k == 0 else (k * g(g, k-1)))(5)</pre><br />
Or maybe not.<br />
<br />
So the idea is, you define a lambda function that takes a function <code>f</code>, and that returns a lambda function that takes an argument <code>x</code> and returns <code>f(f, x)</code>. Then, you call this lambda function you just defined on another lambda function (let's call this <code>func</code>) that takes a function g and another argument k, and is basically the factorial function. This first call returns the inner lambda of the first part, and when that's called with <code>5</code> you're essentially calling <code>func(func, 5)</code>. The chain of recursive calls then works as follows:<br />
<br />
<pre>func(func, 5) -> 5 * func(func, 4) -> 5 * 4 * func(func, 3) -> ... -> 120</pre><br />
In functional programming theory, this is known as a Y Combinator, and it is how you achieve recursion with just lambda functions. If you're wondering why we need <code>func</code> to take in a function as the first parameter, see what would happen if you took that part out!</div>Jeffreylu017https://www.ocf.berkeley.edu/~shidi/cs61a/wiki/GuidesGuides2014-06-21T02:48:01Z<p>Jeffreylu017: /* Python semantics and syntax */</p>
<hr />
<div>== Higher-order functions ==<br />
== Environment diagrams ==<br />
=== Environment diagram Rules ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=131 Source: Spring 2014 Piazza (131)]<br />
<br />
Environment Diagrams are very important in our understanding of how the computer interprets our code.<br />
<br />
'''We will test you on this in every exam.'''<br />
<br />
'''It will never go away.'''<br />
<br />
'''Given that, master it as quickly as you can! :)'''<br />
<br />
Below are the rules I follow when drawing environment diagrams. If you understand and faithfully follow these rules when drawing them, you'll never get them wrong.<br />
<br />
One thing you haven't learned yet is nonlocal. You can skip that particular step for now (step 2 of Assignment).<br />
<br />
Post here if you have any questions!<br />
<br />
You can also take a look at this link for some examples of environment diagrams: http://albertwu.org/cs61a/notes/environments<br />
<br />
For a different perspective on the rules, check out: http://markmiyashita.com/cs61a/sp14/environment_diagrams/rules_of_environment_diagrams/<br />
<br />
A handout with detailed instructions on drawing environment diagrams is also available here (linked on the bottom of the course homepage): http://inst.eecs.berkeley.edu/~cs61a/sp14/pdfs/environment-diagrams.pdf<br />
<br />
<pre>Environment Diagram Rules<br />
=========================<br />
<br />
Creating a Function<br />
--------------------<br />
1. Draw the func <name>(<arg1>, <arg2>, ...)<br />
2. The parent of the function is wherever the function was defined<br />
(the frame we're currently in, since we're creating the function).<br />
3. If we used def, make a binding of the name to the value in the current frame.<br />
<br />
Calling User Defined Functions<br />
------------------------------<br />
1. Evaluate the operator and operands.<br />
2. Create a new frame; the parent is whatever the operator s parent is.<br />
Now this is the current frame.<br />
3. Bind the formal parameters to the argument values (the evaluated operands).<br />
4. Evaluate the body of the operator in the context of this new frame.<br />
5. After evaluating the body, go back to the frame that called the function.<br />
<br />
Assignment<br />
----------<br />
1. Evaluate the expression to the right of the assignment operator (=).<br />
2. If nonlocal, find the frame that has the variable you re looking for,<br />
starting in the parent frame and ending just before the global frame (via<br />
Lookup rules). Otherwise, use the current frame. Note: If there are multiple<br />
frames that have the same variable, pick the frame closest to the current<br />
frame.<br />
3. Bind the variable name to the value of the expression in the identified<br />
frame. Be sure you override the variable name if it had a previous binding.<br />
<br />
Lookup<br />
------<br />
1. Start at the current frame. Is the variable in this frame?<br />
If yes, that's the answer.<br />
2. If it isn't, go to the parent frame and repeat 1.<br />
3. If you run out of frames (reach the Global frame and it's not there), complain.<br />
<br />
Tips<br />
----<br />
1. You can only bind names to values.<br />
No expressions (like 3+4) allowed on environment diagrams!<br />
2. Frames and Functions both have parents.</pre><br />
<br />
== Sequences ==<br />
=== Reversing tuples ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=639 Source: Spring 2014 Piazza (639)]<br />
<br />
'''Student Question'''<br />
<br />
Why does [::-1] tuple work while the tuple [0:3:-1] doesn't?<br />
<br />
I thought the -1 after the second semicolon meant that the interpreter is going to read the indexes "backwards". <br />
<br />
'''Student Answer'''<br />
<br />
The syntax of slicing is <tt>tup[start:end:step]</tt>:<br />
<ul><li>start from index <tt>start</tt> and end just before index <tt>end</tt>, incrementing the index by <tt>step</tt> each time<br />
</li><li>if no <tt>step</tt> is provided, <tt>step</tt> = 1<br />
</li><li>if <tt>step</tt> is positive, default values if not provided: <tt>start</tt> = 0, <tt>end</tt> = <tt>len(tup)</tt><br />
</li><li>if <tt>step</tt> is negative, default values if not provided: <tt>start</tt> = -1, <tt>end</tt> = one position before the start of the string<br />
</li></ul><br />
<br />
<pre>&gt;&gt;&gt; (1, 2, 3)[::-1] # start at index -1, end one position before the start of the string<br />
(3, 2, 1)<br />
&gt;&gt;&gt; (1, 2, 3)[0:3:-1] # start at 0 and go to 3, but step is negative, so this doesn't make sense and an empty tuple is returned<br />
()<br />
</pre><br />
<br />
This is a helpful visualization from http://en.wikibooks.org/wiki/Python_Programming/Strings#Indexing_and_Slicing:<br />
<blockquote><br />
To understand slices, it's easiest not to count the elements themselves. It is a bit like counting not on your fingers, but in the spaces between them. The list is indexed like this:<br />
<pre>Element: 1 2 3 4<br />
Index: 0 1 2 3 4<br />
-4 -3 -2 -1<br />
</pre><br />
</blockquote><br />
More info about slicing at http://stackoverflow.com/a/13005464/2460890.<br />
<br />
=== Slicing with negative step ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=702 Source: Spring 2014 Piazza (702)]<br />
<br />
'''Student Question'''<br />
<br />
if the third example returns an empty tuple because you can't take negative steps from 0 to 4, shouldn't the second example also return an empty tuple?<br />
<br />
Can someone explain why each example returns the respective answers?<br />
<br />
Thanks<br />
<br />
<pre>>>> x= (1,2,3,4)<br />
>>> x[0::-1]<br />
(1,)<br />
>>> x[::-1]<br />
(4, 3, 2, 1)<br />
>>> x[0:4:-1]<br />
()<br />
>>> x[1::-1]<br />
(2, 1)</pre><br />
<br />
'''Instructor Answer'''<br />
<br />
(For reference, the notation is <tt>x[start:end:step]</tt>)<br />
<br />
Python does something a very strange when the step is negative: if you omit the arguments to start and end, Python will fill them with what makes sense for a negative step. In the simple case of <tt>x[::-1]</tt>, Python fills in the start with <tt>len(x)-1</tt> and the end with <tt>-(len(x)+1)</tt>. The end term is strange, but remember that the end term isn't included. We therefore can't use 0, but we can't use -1 either, since that clearly refers to the last element of the tuple. We need to fully wrap the negative index around, to refer to the element "before" the 0th index. This way, Python will start at the end of the tuple and proceed to the beginning of the tuple.<br />
<br />
That's why <tt>x[0:4:-1]</tt> doesn't make sense: how can we start at 0 and end at 4, if we're proceeding backwards?<br />
<br />
And that's why <tt>x[0::-1]</tt> makes sense (albeit, in a strange way): Python is proceeding from the 0 index to the beginning of the list. It includes the start index, which is why you see a 1 pop up.<br />
<br />
Let me know if that was confusing!<br />
<br />
== Recursion ==<br />
=== Recursion visualizer ===<br />
[https://zvzzt.wordpress.com/2014/05/03/python-recursion-visualization-with-rcviz/ Source: Python Recursion Visualization with rcviz]<br />
<br />
== Data abstraction ==<br />
== Time complexity ==<br />
=== Andrew Huang's guide to order of growth and function runtime ===<br />
[https://docs.google.com/document/d/1TxfKmM3MlH032hjSUh92I0kQDVcvmitTSzYObGMr8Bk Source: Guide to Order of Growth and Function Runtime (Retrieved June 16th, 2014)]<br />
<br />
'''Introduction'''<br />
<br />
Confused by $O$, $\Omega$, and $\Theta$?<br />
<br />
Want to figure out the runtime of that tricky function?<br />
<br />
Read this.<br />
<br />
'''NOTE THAT THIS GUIDE STARTS WITH BIG O, WHICH IS DIFFERENT FROM THETA. IF YOU UNDERSTAND BIG O, THETA IS EASY (IN FACT, IT DEFINES THETA IN TERMS OF BIG O BELOW).'''<br />
<br />
First some math.<br />
<br />
Formal definition of O(Big O):<br />
<br />
Let $f(n)$ and $g(n)$ be functions from positive integers to positive reals. We say $f \in O(g)$ (“f grows no faster than g”) if there is a constant $0 < c < \inf$ <such that $f(n) \leq c \cdot g(n)$.<br />
<br />
(Paraphrased from Dasgupta, Papadimitriou, & Vazirani)<br />
<br />
(You'll see this again in CS 170)<br />
<br />
What the heck does that mean?<br />
<br />
Let’s look at math functions for a second (just a second).<br />
<br />
Say $f(n)=5n$ and $g(n)=n^{2}$<br />
<br />
What does that look like on a graph?<br />
<br />
http://www.wolframalpha.com/input/?i=plot+5n+and+n%5E2+from+0+to+10<br />
<br />
There’s a section where $n$ dominates $n^{2}$, from 0 to 5, but we don’t really care, because after that point, $n^{2}$ is larger, all the way to infinity! By the definition, we could scale $n^{2}$ by 5 and we would span that initial gap.<br />
<br />
Thus we can say $5n \in O(n^{2})$ or $f \in O(g)$.<br />
<br />
Can we say the converse? That is, is $n^{2} \in O(5n)$?<br />
<br />
Not at all! From the graph we see that $n^{2}$ grows too quickly for $n$ to catch up, no matter what constant we scale $n$ by.<br />
<br />
So what if $f(n)=n+1000$ and $g(n)=n^{2}$?<br />
<br />
It turns out $n+1000 \in O(n^{2})$ still, because according to the definition, as long as we can multiply $n^{2}$ by some $c$, such that the gap of 1000 is spanned, we’re good. In the case, $c=1001$.<br />
<br />
'''What about and $\Omega$ and $\Theta$?'''<br />
<br />
If you digested all of the above, the rest isn’t scary! (Note, $a \equiv b$ means $a$ is equivalent to $b$)<br />
<br />
$f \in \Omega(g) \equiv g \in O(f)$ (You'll see this again briefly in CS 170)<br />
<br />
$f \in \Theta(g) (f \in O(g) and g \in O(f))$<br />
<br />
'''This means that if $f$ is Theta of $g$, then there exist some $c_{1}$ and $c_{2}$ such that'''<br />
<br />
'''$c_{1}g > f$ and'''<br />
<br />
'''$c_{2}g < f$'''<br />
<br />
'''for all positive integers.'''<br />
<br />
'''What does that mean for Python functions?'''<br />
<br />
Given a function $f$, we want to find out how fast that function runs. One way of doing this is to take out a stopwatch, and clock the amount of time it takes for $f$ to run on some input. However, there are tons of problems with that (different computers => different speeds; only one fixed input? Maybe $f$ is really fast for that input but slow for everything else; next year, all the measurements need to be redone on new computers; etc.) Instead, we'll count the steps that a function needs to perform as a function of its input. For example, here are some of the functions that take one step regardless of their input:<br />
<br />
<code>mul</code><br />
<br />
<code>add</code><br />
<br />
<code>sub</code><br />
<br />
<code>print</code><br />
<br />
<code>return</code><br />
<br />
<code>...</code><br />
<br />
So for example, <code>(3 + 3 * 8) % 3</code> would be 3 steps--one for the multiply, one of the add, and one for the mod.<br />
<br />
Let's take a simple example:<br />
<pre>def square(x):<br />
return x * x</pre><br />
<code>square</code> is a function that for any input, always takes two steps, one of the multiplication, and one for returning. Using the notation, we can say square ∈ Θ(1).<br />
<br />
Functions with iteration (for loops, recursion, etc.), usually multiply the steps by some factor. For example, consider factorial:<br />
<pre>def factorial(n):<br />
if n == 0:<br />
return 1<br />
else:<br />
return n * factorial(n-1)</pre><br />
factorial ∈ Θ(n). Why? Well given some input <code>n</code>, we do <code>n</code> recursive calls. At each recursive call, we carry out 4 steps, one for if <code>n == 0</code>, one for subtraction, one for multiply, one for return. Plus, we have the base case, which is another 2 steps, one for if and one for return. So <code>factorial(n)</code> takes $4n+2$ steps => ∈ Θ(n).<br />
<br />
As mentioned, we care about how the running time (how long the function takes to run) of the function changes, '''as we increase the size of the argument'''. So if we imagine a graph, then the x-axis represents the size of our input, and the y-axis represents how long the function took to run for each x. As the size of the input increases, the function’s runtime does something on the graph. So when we say something like “$O(n^{2})$ where $n$ is the length of the list”, we are saying as we double the size of the list, the function is expected to run at most four times as long. '''NOTE ALSO THAT I SAID WHAT $n$ IS! ALWAYS GIVE YOUR UNITS.'''<br />
<br />
This means that when we compare two functions A and B, A may be overall slower than B as we increase the size of their arguments. However, it’s possible at some specific arguments, the A may run faster (like the $f(n)=5n$ and $g(n)=n^{2}$ example above.)<br />
<br />
'''This also means we do not care about the time taken of any particular input! This implies that all those constant-time base cases all those functions don’t really matter, because they don’t scale. That is, only one specific input causes the base case to be reached, and if we increased the size of the argument, $O(1)$ doesn't necessarily hold.'''<br />
<br />
'''Brief “What runs faster than what”'''<br />
<br />
Sorted from fastest to slowest. This is by no means comprehensive.<br />
* $\Theta(1)$<br />
* $\Theta(\log(n))$<br />
* $\Theta(n)$<br />
* $\Theta(n \log(n))$<br />
* $\Theta(n^{2})$<br />
* $\Theta(n^{3})$<br />
* $\Theta(2^{n})$<br />
* (Anything past this point is kind of ridiculous)<br />
* $\Theta(n!)$<br />
* $\Theta(n^{n})$<br />
<br />
'''So we know about the math and the motivation, now how do we actually assign runtimes to real Python functions?'''<br />
<br />
What you must understand, is that there is no one method for finding the runtime. You MUST look at a function holistically or you won’t get the right answer. What does this mean? '''In order to get the correct runtime, you first must understand what the function is doing! You cannot pattern-match your way to becoming good at this.'''<br />
<br />
'''This cannot be stressed enough: UNITS MATTER, if you say O((n)), you must tell us what $n$ is.'''<br />
<br />
General tips<br />
# UNDERSTAND WHAT THE FUNCTION IS DOING!!!<br />
# Try some sample input. That is, pretend you’re the interpreter and execute the code with some small inputs. What is the function doing with the input? Having concrete examples lets you do tip 1 better. You can also graph how the runtime increases as the argument size increases.<br />
# If applicable, draw a picture of the tree of function calls. This shows you the "growth" of the function or how the function is getting "bigger", which will help you do tip 1 better.<br />
# If applicable, draw a picture of how the input is being modified through the function calls. For example, if your input is a list and your function recursively does something to that list, draw out a list, then draw out parts of the list underneath it that are called during the recursion. Helps with tip 1.<br />
# See tip 1.<br />
<br />
Anyways, let's examine some common runtimes (keep scrolling). Remember, '''this is in no way a comprehensive list, NOR IS IT TRYING TO TEACH YOU HOW TO FIND THEM.''' This post is just to give you a starting point into orders of growth by showing you some examples and basic details about each runtime.<br />
<br />
'''Constant $\Theta(1)$'''<br />
<br />
''What it looks like:''<br />
<br />
http://www.wolframalpha.com/input/?i=plot+5<br />
<br />
''Example:''<br />
<br />
<pre>def add(x, y):<br />
return x + y</pre><br />
$add \in \Theta(1)$, where 1 is.. well a constant...<br />
<br />
''Approach:''<br />
<br />
The key behind constant time functions is that regardless of the size of the input, they always run the same number of instructions.<br />
<br />
''Don’t fall for this Trap:''<br />
<br />
<pre>def bar(n):<br />
if n % 7 == 0:<br />
return "Bzzst"<br />
else:<br />
return bar(n -1)</pre><br />
$\mathtt{bar} \in \Theta(1)$. Why?<br />
<br />
'''Logarithmic $\Theta(\log(n))$'''<br />
<br />
''What it looks like:''<br />
<br />
http://www.wolframalpha.com/input/?i=plot+4log3n+from+0+to+10<br />
<br />
''Example:''<br />
<br />
<pre>def binary_search(sorted_L, n):<br />
""" sorted_L is a list of numbers sorted from<br />
smallest to largest<br />
"""<br />
if sorted_L == []:<br />
return False<br />
mid_num = sorted_L[len(sorted_L) // 2]<br />
if n == mid_num:<br />
return True<br />
elif n < mid_num:<br />
return binary_search(sorted_L[:mid_num], n)<br />
else:<br />
return binary_search(sorted_L[mid_num:], n)</pre><br />
$\mathtt{binary\_search} \in \Theta(log(n))$, where $n$ is the number of elements in <code>sorted_L</code>.<br />
<br />
''Approach:''<br />
<br />
Logarithmic functions scale down the size of the problem by some constant every iteration (either with a recursive loop, a for loop, or a while loop). Also, logarithmic functions do not branch out--they generally do not make more than one call to themselves per recursion.<br />
<br />
'''Linear $\Theta(n)$'''<br />
<br />
''What it looks like:''<br />
<br />
http://www.wolframalpha.com/input/?i=plot+8n+from+0+to+10<br />
<br />
''Examples:''<br />
<br />
<pre>def sum_list(L):<br />
sum = 0<br />
for e in L:<br />
sum += e<br />
return sum</pre><br />
$\mathtt{sum\_list} \in \Theta(n)$, where $n$ is the number of elements in $L$.<br />
<br />
</pre>def countdown(n):<br />
if n > 0:<br />
print(n)<br />
countdown(n - 1)<br />
else:<br />
print("Blast off!")</pre><br />
$\mathtt{countdown} \in \Theta(n)$, where $n$ is n.<br />
<br />
''Approach:''<br />
<br />
Linear functions usually act on sequences or other collections of data. In that case, the function will go through the elements once or twice or ''k'' times, where $k<<n$. If the function acts on a number, the number usually gets smaller by a constant each iteration.<br />
<br />
Don't fall for this trap:<br />
<pre>def two_for_loops(n):<br />
for a in range(n):<br />
if n == 4:<br />
for y in range(n):<br />
print("Admiral Ackbar")<br />
else:<br />
print("It's a trap!")</pre><br />
$\mathtt{two\_for\_loops} \in \Theta(n)$, where $n$ is n. Why?<br />
<br />
'''Loglinear/Linearithmic $\Theta(n \log(n))$'''<br />
<br />
''What it looks like:''<br />
<br />
http://www.wolframalpha.com/input/?i=plot+nlog%28n%29+from+0+to+10<br />
<br />
''Example:''<br />
<br />
<pre>def merge(s1, s2):<br />
if len(s1) == 0:<br />
return s2<br />
elif len(s2) == 0:<br />
return s1<br />
elif s1[0] < s2[0]:<br />
return [s1[0]] + merge(s1[1:], s2)<br />
else:<br />
return [s2[0]] + merge(s1, s2[1:])<br />
<br />
def mergesort(lst):<br />
if len(lst) <= 1:<br />
return lst<br />
else:<br />
middle = len(lst) // 2<br />
return merge(mergesort(lst[:middle]), \<br />
mergesort(lst[middle:]))</pre><br />
$\mathtt{mergesort} \in \Theta(n \log(n))$, where $n$ is the number of elements in <code>lst</code>.<br />
<br />
Approach:<br />
These functions tend to make two recursive calls, each making the problem smaller by a half. There's a neat way to see this. For example in mergesort, start with an entire line, which represents mergesort called on the initial list. From there, the list gets split in half by the two recursive calls to mergesort in the code, so draw the another line right below the first, of the same length, but with a small gap in the middle to represent the split. Repeat until you're tired. At the end, you get a rectangle that's nwide and (n)tall!<br />
<pre><nowiki><br />
---------------<br />
------- -------<br />
--- --- --- ---<br />
- - - - - - - -<br />
</nowiki></pre><br />
The total area is the runtime, $\Theta(n \log(n))$<br />
<br />
''Don’t fall for this trap:''<br />
<br />
Don’t confuse functions that have an average running time of n(n)(like quicksort) with functions that are in (n(n))<br />
<br />
'''Polynomial $\Theta(n^{2})$,$\Theta(n^{3})$, etc.'''<br />
<br />
''What it looks like:''<br />
<br />
http://www.wolframalpha.com/input/?i=plot+n%5E2%2B3+from+0+to+10<br />
<br />
''Example:''<br />
<br />
<pre>def print_a_grid(n):<br />
for _ in range(n):<br />
for _ in range(n):<br />
print("+", end="")<br />
print("")</pre><br />
$\mathtt{print\_a\_grid} \in \Theta(n^{2})$, where $n$ is n.<br />
<br />
''Approach:''<br />
<br />
Polynomial functions will examine each element of an input many, many times, as opposed to linear functions, which examine some constant number of times.<br />
<br />
''Don’t fall into this trap:''<br />
<br />
Don’t get polynomial confused with exponential (below).<br />
<br />
'''Exponential'''<br />
<br />
''What it looks like:''<br />
<br />
http://www.wolframalpha.com/input/?i=plot+2%5En+from+0+to+10<br />
<br />
''Example:''<br />
<br />
<pre>(define (strange-add x)<br />
(if (zero? x)<br />
1<br />
(+ (strange-add (- x 1))<br />
(strange-add (- x 1)) )))<br />
<br />
def strange_add(x):<br />
if x == 0:<br />
return 1<br />
else:<br />
return strange_add(x - 1) + strange_add(x - 1)</pre><br />
$\mathtt{strange\_add} \in \Theta(2^{n})$, where $n$ is x.<br />
<br />
''Approach:''<br />
<br />
Exponential functions tend to branch out as you get deeper and deeper into their call tree, and each call only makes the work smaller by a little bit. For example, <code>(strange-add 8)</code> calls <code>(strange-add 7)</code> and <code>(strange-add 7)</code>. Those two calls each make two calls, <code>(strange-add 6)</code>, <code>(strange-add 6)</code>, <code>(strange-add 6)</code>, and <code>(strange-add 6)</code> respectively, and so on.<br />
<br />
== Mutability ==<br />
=== Michelle Chang's guide to immutability and mutability ===<br />
[https://d1b10bmlvqabco.cloudfront.net/attach/hoxc5uu6sud761/gozdkhgdUbT/htdlpko411i0/Python__Immutable_vs_Mutable.pdf Source: What You Should Know about Immutability vs Mutability]<br />
<br />
== Mutable data-structures ==<br />
== Object-oriented programming ==<br />
=== Inheritance and class vs instance attributes ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=1413 Source: Spring 2014 Piazza (1413)]<br />
<br />
'''Student Question'''<br />
<br />
I'm confused on how Classes and Inheritance work.<br />
<br />
If there's a Parent class and a Child class, when coding in the Child class, when do you write <code>Parent.attribute</code>, when do you write <code>Child.attribute</code>, and when do you write <code>self.attribute</code>?<br />
<br />
Also, I'm also confused as to when to put <code>self</code> into the parentheses as well.<br />
<br />
'''Instructor Answer'''<br />
<br />
<code>Parent.attribute</code> and <code>Child.attribute</code> would both be ways of accessing a'''class variable'''. These are variables that can be accessed without creating new '''instances''' of the that class.<br />
<br />
<code>self.attribute</code> would be used in '''methods''' to access an '''instance variable''' (an attribute specific to an instance).<br />
<br />
So for example, <code>Insect.watersafe</code> is <code>False</code>, but <code>Bee.watersafe</code> is <code>True</code>. These are class attributes because you don't have to create an Insect object or a Bee object in order to say <code>Insect.watersafe</code> or <code>Bee.watersafe</code>.<br />
<br />
However it wouldn't make any sense to say <code>Bee.armor</code>, since armor is an '''instance variable'''. You have to first create a new Bee before you could ask it for it's armor. If you created a second Bee after that, the second Bee would also have its own armor.<br />
<br />
There's a lot of vocab (in bold) that might trip you up. Try reading Discussion 6 and posting a followup if you're still unsure!<br />
<br />
== Iterables, iterators and generators ==<br />
== Python semantics and syntax ==<br />
=== While vs. If ===<br />
[https://piazza.com/class/hktwdlzfjnw680?cid=397 Source: Fall 2013 Piazza (397)]<br />
<br />
'''Student Question'''<br />
<br />
I'm a bit confused, what is the exact function of the "while" function, and how is this different from the "if" function?<br />
<br />
'''Instructor Answer'''<br />
<br />
A while loop will keep evaluating the body of the while loop when its conditional expression is true, whereas an if statement will evaluate its body only once after evaluating its conditional expression.<br />
<br />
i.e. Consider the following two functions! <code>f()</code> will return <code>10</code>, while <code>g()</code> will return <code>1</code>.<br />
<pre>def f():<br />
i = 0<br />
while i &lt; 10:<br />
i += 1<br />
return i<br />
<br />
</pre><br />
vs<br />
<br />
<pre>def g():<br />
i = 0<br />
if i &lt; 10:<br />
i += 1<br />
return i<br />
<br />
</pre><br />
<br />
=== @property ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=3015 Source: Spring 2014 Piazza (3015)]<br />
<br />
'''Student Question'''<br />
<br />
Is it possible to call <code>@property</code> on methods that have arguments?<br />
<br />
'''Student Answer'''<br />
<br />
<code>@property</code> is used to define "setters" and "getters". It essentially gives you control of what happens when a variable is assigned or retrieved.<br />
<pre>class A:<br />
def __init__(self):<br />
self._x = 5<br />
@property<br />
def x(self):<br />
return self._x<br />
@x.setter<br />
def x(self, value):<br />
print("Hello World")<br />
self._x = value<br />
<br />
&gt;&gt;&gt; a = A()<br />
&gt;&gt;&gt; a.x = 52<br />
"Hello World"</pre><br />
As such, there isn't really room for arguments, since it is treated similar to a variable rather than a function. It is retrieved as "a.x" and set as "a.x = value". <br />
<br />
If you would like to pass arguments when assigning something, it would probably be best to just have them use that method. e.g.<br />
<pre>def set_x(self, value1, value2, value3):<br />
self._x = value1 * value2 * value3</pre><br />
Of course, you won't get the syntactic sugar of "a.x = ", but it gets the job done.<br />
<br />
The cool thing about properties is that you can also place restrictions on a variable. For example, if you leave out the "x.setter" method in the example above, then assigning a.x = 5 would throw an error since it isn't defined (you can still assign a._x though). It would only be available for reading. Sometimes that is a good sign to let any users know to use some method to set it (e.g. set_x) or to not set it at all.<br />
<br />
== Scheme ==<br />
=== Scheme semantics and syntax ===<br />
----<br />
==== Difference between <code>eq?</code>, <code>eqv?</code>, <code>equal?</code> and <code>=</code> ====<br />
[http://stackoverflow.com/questions/16299246/what-is-the-difference-between-eq-eqv-equal-and-in-scheme/17719745#17719745 What is the difference between eq?, eqv?, equal?, and = in Scheme?]<br />
<br />
Let's start with the <code>=</code> equivalence predicate. The <code>=</code> predicate is used to check whether two numbers are equal. If you supply it anything else but a number then it will raise an error:<br />
<br />
<pre>(= 2 3) => #f<br />
(= 2.5 2.5) => #t<br />
(= '() '()) => error</pre><br />
The <code>eq?</code> predicate is used to check whether its two parameters respresent the same object in memory. For example:<br />
<br />
<code>(define x '(2 3))<br />
(define y '(2 3))<br />
(eq? x y) => #f<br />
(define y x)<br />
(eq? x y) => #t</code><br />
Note however that there's only one empty list <code>'()</code> in memory (actually the empty list doesn't exist in memory, but a pointer to the memory location <code>0</code> is considered as the empty list). Hence when comparing empty lists <code>eq?</code> will always return <code>#t</code> (because they represent the same object in memory):<br />
<br />
<pre>(define x '())<br />
(define y '())<br />
(eq? x y) => #t</pre><br />
Now depending upon the implementation <code>eq?</code> may or may not return <code>#t</code> for primitive values such as numbers, strings, etc. For example:<br />
<br />
(<pre>eq? 2 2) => depends upon the implementation<br />
(eq? "a" "a") => depends upon the implementation</pre><br />
This is where the <code>eqv?</code> predicate comes into picture. The <code>eqv?</code> is exactly the same as the <code>eq?</code> predicate, except that it will always return <code>#t</code> for same primitive values. For example:<br />
<br />
<pre>(eqv? 2 2) => #t<br />
(eqv? "a" "a") => #t</pre><br />
Hence <code>eqv?</code> is a superset of <code>eq?</code> and for most cases you should use <code>eqv?</code> instead of <code>eq?</code>.<br />
<br />
Finally we come to the <code>equal?</code> predicate. The <code>equal?</code> predicate is exactly the same as the <code>eqv?</code> predicate, except that it can also be used to test whether two lists, vectors, etc. have corresponding elements which satisfy the <code>eqv?</code> predicate. For example:<br />
<br />
<pre>(define x '(2 3))<br />
(define y '(2 3))<br />
(equal? x y) => #t<br />
(eqv? x y) => #f</pre><br />
In general:<br />
# Use the <code>=</code> predicate when you wish to test whether two numbers are equivalent.<br />
# Use the <code>eqv?</code> predicate when you wish to test whether two non-numeric values are equivalent.<br />
# Use the <code>equal?</code> predicate when you wish to test whether two lists, vectors, etc. are equivalent.<br />
# Don't use the <code>eq?</code> predicate unless you know exactly what you're doing.<br />
<br />
=== Scheme lists ===<br />
----<br />
==== Using <code>cons</code> ====<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=3004 Source: Spring 2014 Piazza (3004)]<br />
<br />
'''Student Question<br />
<br />
What's the difference between the following in Scheme?<br />
<br />
(cons 1 2)<br />
(cons 1 . 2)<br />
<br />
(cons 1 (cons 2 (cons 3 nil)))<br />
(cons 1 . (cons 2 . (cons 3 . 4)))<br />
<br />
Why does putting a dot before "(cons" cause it to be a malformed list? But when you put in (cons 1 2) it returns (1 . 2)? Is the dot something that only the interpreter returns, and that the user can't use in defining a list?<br />
<br />
'''Student Answer'''<br />
<br />
I struggled with this a bit as well. It helps to know the difference between a list containing a "." and one that doesn't. <br />
<br />
First off, scheme lists love to be recursive, kind of like rlists. That is, if you did cdr on the list, you would keep getting a list until you finally get an empty list/nil.<br />
<pre>STk&gt; (define a '(1 2 3))<br />
a<br />
STk&gt; a<br />
(1 2 3)<br />
STk&gt; (cdr a)<br />
(2 3)<br />
STk&gt; (cddr a)<br />
(3)<br />
STk&gt; (cdddr a)<br />
()</pre><br />
Note how each call of cdr returns a list. Even (3) is a list. It is just a list containing one member. So what happens with something like (1 2 . 3)?<br />
<pre>STk&gt; (define a '(1 2 . 3))<br />
a<br />
STk&gt; a<br />
(1 2 . 3)<br />
STk&gt; (cdr a)<br />
(2 . 3)<br />
STk&gt; (cddr a)<br />
3</pre><br />
Notice how the last <code>cdr</code> returns a simple 3. Running <code>cdddr</code> would throw an error, because the list stops at 3. A list containing a "." is known as an <em>improper </em>list.<br />
<br />
As for the ".", you won't be using it unless you use it in combination with a quote, otherwise it will always return a malformed list and throw an error. For example:<br />
<pre>STk&gt; (define a (1 2 . 3))<br />
*** Error:<br />
eval: malformed list: (1 2 . 3)<br />
Current eval stack:<br />
__________________<br />
0 (1 2 . 3)<br />
1 (define a (1 2 . 3))<br />
STk&gt; (define a '(1 2 . 3))<br />
a</pre><br />
Basically, the "." is seen in output, but not input. The only exception is the quote. Think of scheme as having two stages. First, it interprets your commands to construct the lists etc. Next, it will simplify the expression. Think of the quote as skipping straight to the second stage.<br />
<br />
Finally, the only way to get the standard lists is to end the list with nil or to use the "list" function (or to use a quote). If the list doesn't end with nil, then it will become an improper list. That is why you <em>can't</em> do things like <code>(1 . 2 . 3 . 4)</code> to make <code>(1 2 3 4)</code>. In order to form a "proper" list, each element must be represented by a list. You <em>can</em> do <code>(1 . (2 . (3 . (4))))</code> because you are treating each element like a list. When in doubt, just test some output:<br />
<pre>STk&gt; (cons 1 2)<br />
(1 . 2) ; doesn't end in an empty list/nil<br />
STk&gt; (cons 1 (cons 2 '()))<br />
(1 2)<br />
STk&gt; (cons 1 (cons 2 (3)))<br />
; ERROR<br />
STk&gt; (cons 1 (cons 2 (list 3)))<br />
(1 2 3) ; lists are formed with an nil at the end, so this works<br />
STk&gt; '(1 . (2 . (3 . 4)))<br />
(1 2 3 . 4)<br />
STk&gt; '(1 . (2 . (3 . (4)))<br />
(1 2 3 4)<br />
STk&gt; '(1 (2 . 3) 4)<br />
(1 (2 . 3) 4) ; still works, but this has 3 elements: (1), (2 . 3), and (4)<br />
STk&gt; (cons 1 (cons (cons 2 3) (cons 4 nil))) <br />
(1 (2 . 3) 4) ; equivalent to above, without quote</pre><br />
<br />
==== <code>append</code> vs <code>cons</code> vs <code>list</code> ====<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=2067 Source: Spring 2014 Piazza (2067)]<br />
<br />
This post isn't meant to be comprehensive. '''Ask questions in lab or as a followup here if you're confused.'''<br />
One of its major flaws is that it doesn't cover box and pointers. '''LEARN BOX AND POINTERS. '''<br />
<br />
Here is a beautiful web based Scheme interpreter that will draw box and pointer diagrams for you. Run through the examples below with this thing:<br />
http://xuanji.appspot.com/js-scheme-stk/index.html<br />
<br />
In order to understand these three procedures, you first have to understand a little about Pairs and Lists.<br />
<br />
'''Pairs''' are data structures that have two slots. You can put different stuff in these slots, like numbers or words or sentences or booleans--pretty much anything. You make a pair using cons.<br />
<pre>STk> (cons 'foo 'bar)<br />
(foo . bar)<br />
<br />
STk> (cons 1 'ring)<br />
(1 . ring)<br />
<br />
STk> (cons (+ 1 2 3) (member? 3 '(the 3 stooges)))<br />
(6 . #t)</pre><br />
<br />
In order to get stuff from a pair that you have made, you use <code>car</code> and <code>cdr</code>. <code>car</code> gets the thing in the first slot. <code>cdr</code> gets the thing in the second slot.<br />
<br />
<pre>STk> (define foo (cons 'x 'y))<br />
foo<br />
<br />
STk> foo<br />
(x . y)<br />
<br />
STk> (car foo)<br />
x<br />
<br />
STk> (cdr foo)<br />
y</pre><br />
That was straightforward. Now for the trippy part:<br />
'''You can put pairs inside of pairs:'''<br />
<pre>STk> (define foo (cons (cons 3 4) 5))<br />
foo<br />
<br />
STk> foo<br />
((3 . 4) . 5)<br />
<br />
STk> (car foo)<br />
(3 . 4)<br />
<br />
STk> (car (car foo))<br />
3<br />
<br />
STk> (caar foo) ; functionally equivalent as above.<br />
3<br />
<br />
STk> (cdr foo)<br />
5<br />
<br />
STk> (cdr (car foo))<br />
4<br />
<br />
STk> (cdar foo) ; functionally equivalent as above.<br />
4</pre> <br />
<br />
There's a certain style of pair nesting that is especially useful&mdash;'''Lists.'''<br />
<br />
Each list has these properties:<br />
* Every list is a pair or the empty list (denoted by '() or nil).<br />
* The car of a nonempty list is some item.<br />
* The cdr of a nonempty list must be another list.<br />
<pre>STk> (cons 1 (cons 2 (cons 3 '()))) ; list of numbers<br />
(1 2 3)<br />
<br />
STk> (define stooges (cons 'larry (cons 'curly (cons 'moe nil))))<br />
stooges<br />
<br />
STk> stooges<br />
(larry curly moe)<br />
<br />
STk> (car stooges)<br />
larry<br />
<br />
STk> (cdr stooges) ; Calling cdr on a non-empty list gives you another list!<br />
(curly moe)<br />
<br />
STk> (cadr stooges)<br />
curly<br />
<br />
STk> (cdar stooges) ; Why does this break?<br />
*** Error:<br />
cdar: bad list: (moe larry curly)<br />
Current eval stack:<br />
__________________<br />
0 (cdar stooges)<br />
<br />
STk> (define not-a-list (cons 'foo (cons 'bar 'baz))) ; This is not a list.<br />
not-a-list<br />
<br />
STk> not-a-list ; What property does this break?</pre><br />
Notice how Scheme knew that we were making lists. Before we had <code>parens</code> and periods which organized our items. Scheme now recognizes that we're making a list and does away with the periods and some of the <code>parens</code>.<br />
<br />
If you stare a bit at the list rules above, you can notice we used a recursive definition to define lists. Recursion... '''on data'''!<br />
<br />
Let's talk about <code>list</code>. <code>list</code> takes a bunch of stuff and makes a list out of them. The stuff can be anything. Words, numbers, pairs, other lists. <code>list</code> doesn't care. [picture of a honey badger]<br />
<pre>STk> (list 'foo 'bar' 'baz) ; Lists takes anything and makes a list out of it.<br />
(foo bar baz)<br />
<br />
STk> (list 'foo ((lambda (x) (+ x 4)) 8) #f (cons 1 (cons 3 4)) (cons 1 (cons 2 nil)) (list 1 2 3)) ; ANYTHING <br />
(foo 12 #f (1 3 . 4) (1 2) (1 2 3))<br />
<br />
STk> (list 'x 'y 'z)<br />
(x y z)<br />
<br />
STk> '(x y z) ; Sometimes you can get away with using quote to make literal lists. Yes, sentences are secretly lists.<br />
(x y z)</pre><br />
Now we can talk about <code>append</code>:<br />
<pre>STk> (append '(a b c) '(d e f) '(g h i)) ; Append takes in lists and appends them together.<br />
(a b c d e f g h i)<br />
<br />
STk> (append 'foo '(1 2 3)) ; foo is not a list. Stuff will break.<br />
*** Error:<br />
append: argument is not a list: foo<br />
Current eval stack:<br />
__________________<br />
0 (append (quote foo) (quote (1 2 3)))</pre><br />
You know that <code>cons</code> makes a pair. You also know that you can make a list out of pairs. You can abuse <code>cons</code> for your own maniacal purposes.<br />
<pre>STk> (cons 'joe stooges) ; Put stuff at the beginning of a list!<br />
(joe larry curly moe)</pre><br />
'''The following only applies to the STk interpreter.'''<br />
<pre>STk> (append '(1 2 3) 'foo) ; Wait... what?<br />
(1 2 3 . foo)<br />
<br />
STk> (append '(1 2 3) (cons 4 5)) ; The plot thickens!<br />
(1 2 3 4 . 5)<br />
<br />
STk> (append stooges 'shemp) ; You should really figure out why this works.<br />
(larry curly moe . shemp)</pre><br />
<br />
To summarize:<br />
* append takes in lists and outputs a big list.<br />
* cons takes in things and makes a pair out of them. However, we know that lists are made of pairs, so we can throw together a list if we use cons a certain way<br />
* list takes in things and makes a list out of those things, regardless of what they are.<br />
<br />
=== Tail recursion ===<br />
----<br />
==== Mark Miyashita's guide on tail recursion ====<br />
[http://markmiyashita.com/cs61a/sp14/tail_recursion/tail_recursion_and_tail_optimized_calls/ Source: Tail Recursion and Tail Optimized Calls]<br />
<br />
First off, I think this is an excellent article to read about tail recursion and tail calls in Python: [http://paulbutler.org/archives/tail-recursion-in-python/ here]<br />
<br />
Basically, you can write tail recursive functions in any language. Tail recursion, in one sentence, is where you return the answer in the final frame instead of following the frames back up to the original frame. For example, we have factorial which is normally not tail recursive:<br />
<br />
<pre>def factorial(n):<br />
if n == 1:<br />
return 1<br />
return n * factorial(n - 1)</pre><br />
<br />
because it needs to keep track of the <code>n *</code> at each level of recursion.<br />
<br />
The following implementation of <code>factorial</code>, is tail recursive because at the end of the last frame, it can return the answer, instead of going back up through all the frames to multiply and compute the answer:<br />
<br />
<pre>def factorial(n):<br />
def helper(n, total):<br />
if n == 1:<br />
return total<br />
return helper(n - 1, total * n)<br />
return helper(n, 1)</pre><br />
<br />
You can’t have tail optimized calls in Python – at least, not like the code that we defined above. You can define your own sort of tail optimized way of evaluating the functions by using lambdas, and I believe the article linked at the top of this post goes into detail about how to implement this if you are interested. In Scheme, the language detects when you have something like the helper function in the example above where your return statement consists of only the recursive call. In the first example, we have the <code>n *</code> and the recursive call which means it cannot be tail optimized because it needs to keep track of all of the frames that it creates. In a tail optimized call, Scheme will get rid of the frames that are no longer necessary.<br />
<br />
'''tl;dr''' – Tail recursion can be done in any language where the basic idea is that you return the answer in the final frame of recursion. Tail optimized calls are a Scheme (and some other languages, not including Python) feature where it will get rid of the frames above, if certain conditions are met – such as where the return statement is only the recursive call and nothing else. The cases in which Scheme uses a tail optimized call are located on the lecture slides located [http://www-inst.eecs.berkeley.edu/~cs61a/sp13/slides/35-TailCalls_6pp.pdf here].<br />
<br />
==== Tail recursion in Python ====<br />
[http://kylem.net/programming/tailcall.html Source: http://kylem.net/programming/tailcall.html (Retrieved June 16th, 2014)]<br />
<br />
In this page, we’re going to look at tail call recursion and see how to force Python to let us eliminate tail calls by using a trampoline. We will go through two iterations of the design: first to get it to work, and second to try to make the syntax seem reasonable. I would not consider this a useful technique in itself, but I do think it’s a good example which shows off some of the power of decorators.<br />
<br />
The first thing we should be clear about is the definition of a tail call. The “call” part means that we are considering function calls, and the “tail” part means that, of those, we are considering calls which are the last thing a function does before it returns. In the following example, the recursive call to f is a tail call (the use of the variable <code>ret</code> is immaterial because it just connects the result of the call to <code>f</code> to the return statement), and the call to <code>g</code> is not a tail call because the operation of adding one is done after <code>g</code> returns (so it’s not in “tail position”).<br />
<br />
<pre>def f(n) :<br />
if n > 0 :<br />
n -= 1<br />
ret = f(n)<br />
return ret<br />
else :<br />
ret = g(n)<br />
return ret + 1</pre><br />
<br />
'''1. Why tail calls matter'''<br />
<br />
Recursive tail calls can be replaced by jumps. This is called “tail call eliminination,” and is a transformation that can help limit the maximum stack depth used by a recursive function, with the benefit of reducing memory traffic by not having to allocate stack frames. Sometimes, recursive function which wouldn’t ordinarily be able to run due to stack overflow are transformed into function which can.<br />
<br />
Because of the benefits, some compilers (like <code>gcc</code>) perform tail call elimination[1], replacing recursive tail calls with jumps (and, depending on the language and circumstances, tail calls to other functions can sometimes be replaced with stack massaging and a jump). In the following example, we will eliminate the tail calls in a piece of code which does a binary search. It has two recursive tail calls.<br />
<br />
<pre>def binary_search(x, lst, low=None, high=None) :<br />
if low == None : low = 0<br />
if high == None : high = len(lst)-1<br />
mid = low + (high - low) // 2<br />
if low > high :<br />
return None<br />
elif lst[mid] == x :<br />
return mid<br />
elif lst[mid] > x :<br />
return binary_search(x, lst, low, mid-1)<br />
else :<br />
return binary_search(x, lst, mid+1, high)</pre><br />
<br />
Supposing Python had a <code>goto</code> statement, we could replace the tail calls with a jump to the beginning of the function, modifying the arguments at the call sites appropriately:<br />
<br />
<pre>def binary_search(x, lst, low=None, high=None) :<br />
start:<br />
if low == None : low = 0<br />
if high == None : high = len(lst)-1<br />
mid = low + (high - low) // 2<br />
if low > high :<br />
return None<br />
elif lst[mid] == x :<br />
return mid<br />
elif lst[mid] > x :<br />
(x, lst, low, high) = (x, lst, low, mid-1)<br />
goto start<br />
else :<br />
(x, lst, low, high) = (x, lst, mid+1, high)<br />
goto start</pre><br />
<br />
which, one can observe, can be written in actual Python as<br />
<br />
<pre>def binary_search(x, lst, low=None, high=None) :<br />
if low == None : low = 0<br />
if high == None : high = len(lst)-1<br />
while True :<br />
mid = low + (high - low) // 2<br />
if low > high :<br />
return None<br />
elif lst[mid] == x :<br />
return mid<br />
elif lst[mid] > x :<br />
high = mid - 1<br />
else :<br />
low = mid + 1</pre><br />
<br />
I haven’t tested the speed difference between this iterative version and the original recursive version, but I would expect it to be quite a bit faster because of there being much, much less memory traffic.<br />
<br />
Unfortunately, the transformation makes it harder to prove the binary search is correct in the resulting code. With the original recursive algorithm, it is almost trivial by induction.<br />
<br />
Programming languages like Scheme depend on tail calls being eliminated for control flow, and it’s also necessary for continuation passing style.[2]<br />
<br />
'''2. A first attempt'''<br />
<br />
Our running example is going to be the factorial function (a classic), written with an accumulator argument so that its recursive call is a tail call:<br />
<br />
<pre>def fact(n, r=1) :<br />
if n <= 1 :<br />
return r<br />
else :<br />
return fact(n-1, n*r)</pre><br />
<br />
If <code>n</code> is too large, then this recursive function will overflow the stack, despite the fact that Python can deal with really big integers. On my machine, it can compute <code>fact(999)</code>, but <code>fact(1000)</code> results in a sad <code>RuntimeError: Maximum recursion depth exceeded</code>.<br />
<br />
One solution is to modify fact to return objects which represent tail calls and then to build a trampoline underneath fact which executes these tail calls after fact returns. This way, the stack depth will only contain two stack frames: one for the trampoline and another for each call to fact.<br />
<br />
First, we define a tail call object which reifies the concept of a tail call:<br />
<br />
<pre>class TailCall(object) :<br />
def __init__(self, call, *args, **kwargs) :<br />
self.call = call<br />
self.args = args<br />
self.kwargs = kwargs<br />
def handle(self) :<br />
return self.call(*self.args, **self.kwargs)</pre><br />
<br />
This is basically just the thunk <code>lambda : call(*args, **kwargs)</code>, but we don’t use a thunk because we would like to be able to differentiate between a tail call and returning a function as a value.<br />
<br />
The next ingredient is a function which wraps a trampoline around an arbitrary function:<br />
<br />
<pre>def t(f) :<br />
def _f(*args, **kwargs) :<br />
ret = f(*args, **kwargs)<br />
while type(ret) is TailCall :<br />
ret = ret.handle()<br />
return ret<br />
return _f</pre><br />
<br />
Then, we modify fact to be<br />
<br />
<pre>def fact(n, r=1) :<br />
if n <= 1 :<br />
return r<br />
else :<br />
return TailCall(fact, n-1, n*r)</pre><br />
<br />
Now, instead of calling <code>fact(n)</code>, we must instead invoke <code>t(fact)(n)</code> (otherwise we’d just get a TailCall object).<br />
<br />
This isn’t that bad: we can get tail calls of arbitrary depth, and it’s Pythonic in the sense that the user must explicitly label the tail calls, limiting the amount of unexpected magic. But, can we eliminate the need to wrap t around the initial call? I myself find it unclean to have to write that <code>t</code> because it makes calling fact different from calling a normal function (which is how it was before the transformation).<br />
<br />
'''3. A second attempt'''<br />
<br />
The basic idea is that we will redefine fact to roughly be <code>t(fact)</code>. It’s tempting to just use <code>t</code> as a decorator:<br />
<br />
<pre>@t<br />
def fact(n, r=1) :<br />
if n <= 1 :<br />
return r<br />
else :<br />
return TailCall(fact, n-1, n*r)</pre><br />
<br />
(which, if you aren’t familiar with decorator syntax, is equivalent to writing <code>fact = t(fact)</code> right after the function definition). However, there is a problem with this in that the fact in the returned tail call is bound to <code>t(fact)</code>, so the trampoline will recursively call the trampoline, completely defeating the purpose of our work. In fact, the situation is now worse than before: on my machine, <code>fact(333)</code> causes a <code>RuntimeError</code>!<br />
<br />
For this solution, the first ingredient is the following class, which defines the trampoline as before, but wraps it in a new type so we can distinguish a trampolined function from a plain old function:<br />
<br />
<pre>class TailCaller(object) :<br />
def __init__(self, f) :<br />
self.f = f<br />
def __call__(self, *args, **kwargs) :<br />
ret = self.f(*args, **kwargs)<br />
while type(ret) is TailCall :<br />
ret = ret.handle()<br />
return ret</pre><br />
<br />
and then we modify <code>TailCall</code> to be aware of <code>TailCallers</code>:<br />
<br />
<pre>class TailCall(object) :<br />
def __init__(self, call, *args, **kwargs) :<br />
self.call = call<br />
self.args = args<br />
self.kwargs = kwargs<br />
def handle(self) :<br />
if type(self.call) is TailCaller :<br />
return self.call.f(*self.args, **self.kwargs)<br />
else :<br />
return self.call(*self.args, **self.kwargs)</pre><br />
<br />
Since classes are function-like and return their constructed object, we can just decorate our factorial function with <code>TailCaller</code>:<br />
<br />
<pre>@TailCaller<br />
def fact(n, r=1) :<br />
if n <= 1 :<br />
return r<br />
else :<br />
return TailCall(fact, n-1, n*r)</pre><br />
<br />
And then we can call fact directly with large numbers!<br />
<br />
Also, unlike in the first attempt, we can now have mutually recursive functions which all perform tail calls. The first-called <code>TailCall</code> object will handle all the trampolining.<br />
<br />
If we wanted, we could also define the following function to make the argument lists for tail calls be more consistent with those for normal function calls:[3]<br />
<br />
<pre>def tailcall(f) :<br />
def _f(*args, **kwargs) :<br />
return TailCall(f, *args, **kwargs)<br />
return _f</pre><br />
<br />
and then fact could be rewritten as<br />
<br />
<pre>@TailCaller<br />
def fact(n, r=1) :<br />
if n <= 1 :<br />
return r<br />
else :<br />
return tailcall(fact)(n-1, n*r)</pre><br />
<br />
One would hope that marking the tail calls manually could just be done away with, but I can’t think of any way to detect whether a call is a tail call without inspecting the source code. Perhaps an idea for further work is to convince Guido von Rossum that Python should support tail recursion (which is quite unlikely to happen).<br />
<br />
[1] This is compiler-writer speak. For some reason, “elimination” is what you do when you replace a computation with something equivalent. In this case, it’s true that the call is being eliminated, but in its place there’s a jump. The same is true for “common subexpression elimination” (known as CSE), which takes, for instance,<br />
<br />
<pre>a = b + c<br />
d = (b + c) + e<br />
and replaces it with<br />
a = b + c<br />
d = a + e</pre><br />
<br />
Sure, the <code>b+c</code> is eliminated from the second statement, but it’s not really gone...<br />
The optimization known as “dead code elimination” actually eliminates something, but that’s because dead code has no effect, and so it can be removed (that is, be replaced with nothing).<br />
<br />
[2] In Scheme, all loops are written as recursive functions since tail calls are the pure way of redefining variables (this is the same technique Haskell uses). For instance, to print the numbers from 1 to 100, you’d write<br />
<br />
<pre>(let next ((n 1))<br />
(if (<= n 100)<br />
(begin<br />
(display n)<br />
(newline)<br />
(next (+ n 1)))))</pre><br />
<br />
where next is bound to be a one-argument function which takes one argument, <code>n</code>, and which has the body of the <code>let</code> statement as its body. If that <code>100</code> were some arbitrarily large number, the tail call to next had better be handled as a jump, otherwise the stack would overflow! And there’s no other reasonable way to write such a loop!<br />
<br />
Continuation passing style is commonly used to handle exceptions and backtracking. You write functions of the form<br />
<br />
<pre>(define (f cont)<br />
(let ((cont2 (lambda ... (cont ...) ...)))<br />
(g cont2)))</pre><br />
<br />
along with functions which take multiple such f’s and combines them into another function which also takes a single cont argument. I’ll probably talk about this more in another page, but for now notice how the call to g is in the tail position.<br />
<br />
[3] This is basically a curried[4] version of <code>TailCall</code>.<br />
<br />
[4] That is, ''Schönfinkelized''.<br />
<br />
=== Miscellaneous ===<br />
----<br />
==== Useful Scheme Procedures ====<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=2226 Source: Spring 2014 Piazza (2226)]<br />
<br />
Here is a short list of Scheme procedures that you might use in writing your programs:<br />
<pre>; define - defines a variable or a procedure<br />
(define my-variable 4)<br />
(define (square x)<br />
(* x x))<br />
<br />
; if - conditional branching akin to if ... else<br />
(define (fib n)<br />
(if (< n 2)<br />
n<br />
(+ (fib (- n 1)) (fib (- n 2))) ))<br />
<br />
; conditional branching akin to if ... elif ... elif ... else<br />
(define (deep-map f lst)<br />
(cond ((null? lst) lst)<br />
((list? (car lst))<br />
(cons (deep-map f (car lst))<br />
(deep-map f (cdr lst))))<br />
(else<br />
(cons (f (car lst))<br />
(deep-map f (cdr lst)))) ))<br />
<br />
; and - outputs the the rightmost value if all of the arguments evaluate to #t. Outputs #f otherwise.<br />
STk> (and 0 1 2 3)<br />
3<br />
<br />
; or - outputs the the first value that evaluates to #t. Outputs #f otherwise.<br />
STk> (or 0 1 2 3)<br />
0<br />
<br />
; equal - tests if symbols are the same<br />
STk> (equal? 'foo 'bar)<br />
#f<br />
STk> (equal? 'foo 'foo)<br />
#t<br />
<br />
STk> (list? 'foo)<br />
#f<br />
STk> (list? '(1 2 3))<br />
#t<br />
STk> (list? '())<br />
#t<br />
<br />
STk> (null? '(1 2 3))<br />
#f<br />
STk> (null? ())<br />
#t<br />
<br />
; member? - tests if a symbol is in a list EDIT: NOT BUILT IN (BUT SUPER USEFUL SEE FOLLOWUP)<br />
STk> (member? 'quick '(the quick brown fox jumped over the lazy dog))<br />
#t<br />
<br />
; number? - checks if input is a number<br />
STk> (number? 42)<br />
#t<br />
STk> (number? #t)<br />
#f<br />
<br />
; remainder - computes the remainder of the first number divided by the second<br />
STk> (remainder 100 21)<br />
16</pre><br />
<br />
== Streams ==<br />
== Logic ==<br />
=== Quick Guide to Logic Programming ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=2524 Source: Spring 2014 Piazza (2524)]<br />
''Note: Someone should convert this from scmlog to Logic notation''<br />
<br />
'''Here's something I wrote a long time ago. The logic interpreter scmlog still exists and you should be able to access it using your cs61a-xx account. Post a followup if you have any questions. Hope this helps!'''<br />
<br />
<br />
'''Introduction:''' logic programming is a completely different way to think about telling computers to do stuff. Instead of telling the computer what to compute, you give the computer facts and ask it questions. The computer does its own thinking given the facts and the question, and then returns an answer.<br />
<br />
'''Table of Contents'''<br />
- Introduction<br />
- What is Logic Programming<br />
- - Giving Facts and Asking Questions<br />
- - More Complicated Facts and Questions<br />
- How to write Logic Programs<br />
- - Common Pitfalls<br />
- More Resources<br />
<br />
<br />
'''What is Logic Programming?'''<br />
Logic Programming is a way to ask the computer questions and get answers without telling it explicitly how to reach the conclusion. Kind of like this: http://youtu.be/tpKx7Oi0oeM<br />
<br />
Anyways, we have a logic programming interpreter called Scmlog. It interprets a simpler version of the logic programming language, Prolog. The first few things to understand about it is that '''SCMLOG IS NOT SCHEME'''. It just happens to look like it. Scmlog is its own language and so it has its own rules. You can't program in Scmlog like you would in Scheme or Python, so it might be good to forget what you know about those languages for a second.<br />
<br />
When you fire up scmlog (type scmlog into a terminal on the school computers), you get this prompt:<br />
<pre>star [501] ~ # scmlog<br />
scmlog (Prolog in Scheme), v. 0.2<br />
Type 'help' for help; 'quit' to exit.<br />
?- </pre><br />
You can interact with this prompt in two ways:<br />
# Give facts - "Let's tell the computer some things it should know!"<br />
# Ask questions - "Let's ask the computer questions about the things we told it about!"<br />
<br />
'''Giving Facts and Asking Questions'''<br />
A basic fact takes this form: <code>(fact (assertion))</code>, '''Where each assertion is simply a relation between things.'''<br />
<br />
For example: <code>(fact (likes potstickers brian))</code> relates three ideas, liking something, potstickers, and some guy name Brian. "Brian likes potstickers."<br />
Note that we put the relation first, and then the parties the relation acts upon. In this example, "likes" is the relation, it hooks up "brian" and "potstickers". Here's some more:<br />
<pre>?- (fact (likes potstickers brian)) <br />
?- (fact (likes potstickers andrew)) <br />
?- (fact (likes the_beatles brian)) <br />
?- (fact (likes the_beatles andrew)) <br />
?- (fact (likes led_zeppelin andrew)) <br />
?- (fact (dislikes led_zeppelin brian))</pre><br />
Now that we've given the computer a bunch of facts, how do we ask questions about them? Just replace "fact" with a "?", and replace any part of the relation (besides the relation itself) with a variable prefixed by an underscore. This is called "querying":<br />
<pre>?- (? (likes _what brian))<br />
_what : potstickers<br />
More? <br />
_what : the_beatles<br />
More? <br />
?- (? (likes potstickers _who))<br />
_who : brian<br />
More? <br />
_who : andrew<br />
More? <br />
?- (? (dislikes _what brian))<br />
_what : led_zeppelin<br />
More?<br />
?- (? (likes led_zeppelin andrew))<br />
Yes.<br />
?- (? (dislikes the_beatles _who))<br />
No.</pre><br />
Notice:<br />
* We can query any part of the assertion, besides the relation itself (can't replace "likes" with a variable).<br />
* All possible answers to the question show up.<br />
* If Scmlog couldn't find a fact that matched your query, it'll say "No."<br />
* Asking a question without any variables essentially asks if that fact exists. To which Scmlog will answer "Yes." or "No."<br />
<br />
'''More Complicated Facts and Questions'''<br />
Now this isn't the whole picture. We also have the ability to make more powerful assertions via variables, hypotheses, and conclusions:<br />
<br />
<code>(fact (ancestor _x _y) (parent _x _y))</code> "X is an ancestor of Y if X is a parent of Y"<br />
<br />
Here _x and _y are variables like usual. However, we have two parts to this fact, the conclusion ("X is an ancestor of Y") and the hypothesis ("X is a parent of Y"). We can have more than one hypotheses, and they can be any kind of query. Here's ancestor in action:<br />
<pre>?- (fact (parent george paul))<br />
?- (fact (parent martin george))<br />
?- (fact (parent martin martin_jr))<br />
?- (fact (parent martin donald))<br />
?- (fact (parent george ann))<br />
?- (fact (ancestor _X _Y) (parent _X _Y))<br />
?- (fact (ancestor _X _Y) (parent _X _Z) (ancestor _Z _Y))<br />
?- (? (ancestor paul george))<br />
No.<br />
?- (? (ancestor george paul))<br />
Yes.<br />
?- (? (ancestor george george))<br />
No.<br />
?- (? (ancestor martin paul))<br />
Yes.</pre><br />
Now there's one more thing you need to know about Scmlog. Scmlog knows about pairs and lists:<br />
<pre>?- (fact (lst (1 2 3)))<br />
?- (? (lst _x))<br />
_x : (1 2 3)<br />
More?<br />
?- (? (lst (1 . _x)))<br />
_x : (2 3)<br />
More? <br />
?- (? (lst (1 2 . _x)))<br />
_x : (3)<br />
More? <br />
?- (? (lst (_x . _y)))<br />
_x : 1<br />
_y : (2 3)<br />
?- (fact (my_pair (2 . 3)))<br />
?- (? (my_pair _y))<br />
_y : (2 . 3)<br />
?- (? (my_pair (2 . _x)))<br />
_x : 3</pre><br />
<br />
'''How to write Logic Programs'''<br />
<br />
The trick behind writing logic programs is to forget everything you know about programming. You instead want to focus on the relation you're trying to establish. Take append for example:<br />
<pre>?- (fact (append () _b _b))<br />
?- (fact (append (_x . _rest) _b (_x . _z)) (append _rest _b _z))<br />
?- (? (append (1 2 3) (3 2 1) _answer))<br />
_answer : (1 2 3 3 2 1)<br />
?- (append (1 2) 3 (1 2 3))<br />
Huh?<br />
?- (? (append (1 2) 3 (1 2 3))<br />
)<br />
No.<br />
?- (? (append (1 2) (3) (1 2 3))<br />
)<br />
Yes.<br />
?- (? (append (1 2) (3) (1 2 3)))<br />
Yes.<br />
?- (? (append _x (3 4 9) (1 0 3 2 3 4 9)))<br />
_x : (1 0 3 2)<br />
More? <br />
?- (? (append _x _y (2 1)))<br />
_x : ()<br />
_y : (2 1)<br />
More? <br />
_x : (2)<br />
_y : (1)<br />
More? <br />
_x : (2 1)<br />
_y : ()</pre><br />
<br />
What is append? Well the first fact establishes that if we append an empty list to something, the result is that something.<br />
The next fact is the meat of the code. You can almost think of this as a recursive relation. Any append relation between some three lists a b c must also fulfill another append relation between the rest of a, b, and the rest of c, respectively. Think about why that will always be true for all inputs. If the example above doesn't make sense to you, see the following:<br />
<br />
<br />
'''Common Pitfalls'''<br />
<br />
'''- One of the biggest issues people have with writing logic programs is that they don't realize that the variables don't work like they do in Scheme or Python. All Scmlog ever does is pattern match:'''<br />
<pre>?- (fact (x (0 1 8)))<br />
?- (fact (x (1 8 4)))<br />
?- (fact (x (9 4 4)))<br />
?- (fact (x (3 0 8)))<br />
?- (? (x _y))<br />
_y : (0 1 8)<br />
More? <br />
_y : (1 8 4)<br />
More? <br />
_y : (9 4 4)<br />
More? <br />
_y : (3 0 8)<br />
?- (? (x (0 1 . _z)))<br />
_z : (8)</pre><br />
<br />
- SCMLOG IS NOT SCHEME. SCMLOG IS NOT PYTHON.<br />
<br />
More Resources:<br />
* http://www-inst.eecs.berkeley.edu/~cs61a/sp12/lectures/prolog/ReadMe<br />
* http://www-inst.eecs.berkeley.edu/~cs61a/sp12/lectures/prolog/<br />
* http://www-inst.eecs.berkeley.edu/~cs61a/sp12/discussion/week14/<br />
* http://www-inst.eecs.berkeley.edu/~cs61a/fa13/slides/31-Logic_6pp.pdf<br />
<br />
=== Logic Mathematics ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=3050 Source: Spring 2014 Piazza (3050)]<br />
<br />
'''Student Question'''<br />
<br />
Can someone explain the intuition behind the implementation of the increments and the addition facts? The following is from Mark's website<br />
<br />
<pre>(fact (increment 0 1))<br />
(fact (increment 1 2))<br />
(fact (increment 2 3))<br />
(fact (increment 3 4))<br />
(fact (increment 4 5))<br />
(fact (increment 5 6))<br />
(fact (increment 6 7))<br />
(fact (increment 7 8))<br />
(fact (increment 8 9))<br />
(fact (increment 9 10))<br />
(fact (increment 10 11))<br />
(fact (increment 11 12))<br />
(fact (increment 12 13))<br />
<br />
(fact (add 1 ?x ?x+1)<br />
(increment ?x ?x+1))<br />
<br />
(fact (add ?x+1 ?y ?z+1)<br />
(increment ?x ?x+1)<br />
(increment ?z ?z+1)<br />
(add ?x ?y ?z))<br />
<br />
(query (add 2 4 6))<br />
; expect Success!</pre><br />
<br />
'''Student Answer'''<br />
<br />
First, we state a bunch of facts that denote relations between a number and the number that follows it (e.g. 1 and 2, 2 and 3, and so on). This is the increment fact.<br />
<br />
Then, we state a "base" fact, which is:<br />
<pre>(fact (add 1 ?x ?x+1)<br />
(increment ?x ?x+1))</pre><br />
In English, this fact states that some value <code>?x</code> added to 1 will give us some value <code>?x+1</code> (<code>x+1</code> is a valid variable name in Logic) '''if and only if''' the fact (<code>increment ?x ?x+1</code>) is true. As an example, <code>(add 1 2 3)</code> is true '''if and only if''' (<code>increment 2 3</code>) is true. This is true because of the increment facts we stated before.<br />
<br />
Now, to deal with additions that aren't just the sum of two numbers in which one number is a 1, we need:<br />
<pre>(fact (add ?x+1 ?y ?z+1)<br />
(increment ?x ?x+1)<br />
(increment ?z ?z+1)<br />
(add ?x ?y ?z))</pre><br />
I will try to give an intuition as to what is happening here.<br />
<br />
We state our fact: that two numbers, <code>?x+1</code> and <code>?y</code>, will add up to some number <code>?z+1</code> if and only if the following 3 hypotheses are true:<br />
* (<code>increment ?x ?x+1</code>) - "There exists some number <code>?x</code> that is 1 less than <code>?x+1</code>, and"<br />
* (<code>increment ?z ?z+1</code>) - "There exists some number <code>?z</code> that is 1 less than <code>?z+1</code>, and"<br />
* (<code>add ?x ?y ?z</code>) - "The numbers <code>?x</code> and <code>?y</code> will add up to <code>?z</code>."<br />
<br />
Consider the example of:<br />
<pre>(query (add 2 4 6))</pre><br />
Here's an idea of what's happening when Logic tries to match the query with the facts you've stated.<br />
* ?x+1 = 2, ?y = 4, ?z+1 = 6<br />
* It finds a match for <code>?x = 1</code>, since (<code>increment ?x ?x+1</code>) gives <code>?x = 1</code> because <code>?x+1 = 2</code><br />
* It finds a match for <code>?z = 5</code>, since (<code>increment ?z ?z+1</code>) gives <code>?z = 5</code> because <code>?z+1 = 6</code><br />
* It then checks for a match for (<code>add ?x ?y ?z</code>) which in this case is (<code>add 1 4 5</code>). This goes to our first "base" fact for add. (<code>add 1 4 5</code>) is a success because (<code>increment 4 5</code>) is a true fact [refer again to the "base fact" to see why this is the case]. This is also where we would get a "Failed.", if it turns out that (<code>increment ?x ?x+1</code>) wasn't actually true!<br />
* Hence, all 3 of our hypotheses are true, and so (<code>query (add 2 4 6)</code>) is a success!<br />
<br />
In this example, we only have to recurse once to get to our "base" fact. In other examples, where <code>?x+1</code> is not 2, but some number greater, such as 5, we will have to recurse 4 whole times to get to 1, at which point our "base" fact is reached. <br />
<br />
This recursion is similar to this idea in mathematical equations:<br />
<pre>x + y = z</pre><br />
is the same as<br />
<pre>(x - 1) + y = (z - 1)</pre><br />
is the same as<br />
<br />
<pre>(x - 2) + y = (z - 2)</pre?<br />
and so on... In Logic, we stop when we find that the first term (x) is 1, and then we use our increment facts to determine if the original statement is true, because all of these equations are equivalent.<br />
<br />
== Python syntax and semantics ==<br />
=== <code>print</code> vs <code>return</code> ===<br />
----<br />
==== Andrew's tips ====<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=779 Source: Spring 2014 Piazza (779)]<br />
<br />
Remember the differences between return and print.<br />
* <code>return</code> can only be used in a <code>def</code> statement. It returns a value from a function. Once Python evaluates a <code>return</code> statement, it immediately exits the function.<br />
* <code>print</code> is a function that displays its argument on the screen. It always returns <code>None</code>.<br />
<br />
Examples:<br />
<pre>def foo1(x):<br />
return x<br />
<br />
def foo2(x):<br />
print(x)<br />
<br />
>>> foo2(1) # In foo2, we print 1 ourselves using the print function<br />
1<br />
>>> foo1(1) # HERE, THE PYTHON INTERPRETER PRINTS THE RETURN VALUE OF FOO1. CANNOT STRESS HOW IMPORTANT TO UNDERSTAND THIS<br />
1<br />
>>> foo1(1) + 1<br />
2<br />
>>> foo2(1) + 1<br />
1<br />
Traceback (most recent call last):<br />
File "<stdin>", line 1, in <module><br />
TypeError: unsupported operand type(s) for +: 'NoneType' and 'int'</pre><br />
<br />
=== Function decorators ===<br />
<br />
----<br />
<br />
==== How function decorators work ====<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=638 Source: Spring 2014 Piazza (638)]<br />
<br />
'''Student Question'''<br />
<br />
I'm having difficulties understanding what exactly a function decorator is. Can someone elaborate and potentially provide me with an example other than the one in the readings?<br />
<br />
'''Instructor Answer'''<br />
<br />
So imagine you wanted your functions to print their arguments before they executed them. Here's one way to do this.<br />
<pre>def loud(fn):<br />
def new_fn(*args):<br />
print(args)<br />
return fn(*args)<br />
return new_fn </pre><br />
Here's a function loud that takes in a function and returns a new function that when called, prints out its arguments, and then does what the old function does.<br />
<br />
For example:<br />
<pre>def sq(x):<br />
return x * x<br />
>>> sq(4)<br />
16<br />
>>> sq = loud(sq) # replace the old square with our loud one.<br />
>>> sq(4)<br />
(4,)<br />
16</pre><br />
A function decorator does the same thing as the above. Assuming loud is defined, we can do this:<br />
<pre>@loud<br />
def sq(x):<br />
return x * x<br />
<br />
>>> sq(4)<br />
(4,)</pre><br />
<br />
== Student guides ==<br />
=== How to learn computer science ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=241 Source: Spring 2014 Piazza (241)]<br />
<br />
If you've never programmed before, or if you've never taken a class quite like 61A before, things right now might be scary. Everything is strange and new and there quite a lot to take in all at once. So if you're having a hard time so far, here are a few articles that might help.<br />
<br />
Note: these articles are pretty long, so feel free to read them in multiple sittings.<br />
<br />
'''At the beginning, everything seems a bit scary in CS'''. Michelle Bu, a Berkeley alum and a crazy good hacker, shares one of her experiences when she was a wee n00b in [http://blog.michellebu.com/2013/03/21-nested-callbacks/ 21 Nested Callbacks].<br />
<br />
'''Start here!''' [http://www.jamesmaa.com/2013/08/26/a-beginners-guide-to-computer-science/ "A Beginner's Guide to Computer Science"] Written by Berkeley's own James Maa. James is known for his killer walkthroughs (check out his Productivity guide). This article gives you some background on learning CS and then provides a practical guide on how to learn effectively.<br />
<br />
'''How do we learn?''' Mark Eichenlaub explains in this [http://www.quora.com/Learning/Do-grad-school-students-remember-everything-they-were-taught-in-college-all-the-time/answer/Mark-Eichenlaub Introduction to Learning Theory]. This is quite possibly the best introduction to Learning Theory.<br />
<br />
'''Sometimes, you're stuck and you end up really, really frustrated.''' Marcus Geduld explains [http://www.quora.com/Why-do-we-get-frustrated-when-learning-something/answer/Marcus-Geduld Why do we get frustrated when learning something?]<br />
<br />
=== Quick guide on getting unstuck ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=1264 Source: Quick Guide on Getting Unstuck (Retrieved June 16th, 2014)]<br />
<br />
A major frustration you might encounter in 61A is when you stare at a homework problem and have no idea where to start. Or you write some code and it doesn't pass the doctests, but now what? You work at it for a while, but next thing you know, you've been stuck for hours on the the same problem and have little to show about it.<br />
<br />
So here's a checklist of things you can do when you're stuck. Experienced programmers do these things almost naturally (because of how much practice they've had being stuck), and so while they get stuck just as much as your or I, they always know what to do next.<br />
<br />
# Do I understand what the problem is asking?<br />
## If not, which part of the problem is confusing me?<br />
### Identify the exact sentences/phrases/words/etc.<br />
## Check the given examples. Do they make sense to me?<br />
## Can I come up with my own examples? '''A good indicator that you understand the question is that you can come up with some nontrivial examples of how the function works.'''<br />
# What concepts should I use here?<br />
## Do I understand the concepts? '''Can I explain the concept in English to one of my friends such that they get it?'''<br />
### If not, go back and relearn the specific concepts that are unclear (through discussion, lab, lecture, etc.) Don't read the entire book in order to solve one problem..<br />
## How do I apply the concept to the given problem?<br />
# Write your code and test it.<br />
## Use doctests, '''BUT ALSO LOAD IT INTERACTIVELY (python3 -i ...)'''<br />
### '''Saying "my function works because the doctests pass" is a lot like saying "this airplane will fly because it has wings."'''<br />
## If your code breaks, ask yourself:<br />
### Does it error? Is it a....<br />
#### Syntax error? If so, find the syntax bug and fix it.<br />
#### Logic error? Is it something weird that you don't understand? (E.g. cannot add integer and tuple)<br />
### Why did it do that? Why didn't it do what I expected? Trace through the code by hand with an example (sample values) you came up with in step 0. '''Add calls to <code>print</code> in order to figure out how your function is handling the arguments.'''<br />
# Am I missing a trick?<br />
## Oftentimes you've never seen this type of problem before. This is expected on homework (and this is why homework can take a long time) because if you see it on the homework, then you will be familiar with it on the exam and when you program for fun and profit. <br />
## The key here is just to learn the trick however you need to.<br />
### Stare at it yourself<br />
### Stare at it with others (peers in the class)<br />
### Ask on PIazza what the approach is.<br />
### Stare at it with the TAs/lab Assistants<br />
## '''Once you figure it out, remember the trick so that you can use it next time.'''<br />
# At any point you identify what you're stuck on, you can begin to resolve it.<br />
## Use the tips above. Try things out on the interpreter. Review the lecture/discussion/labs/etc. Do whatever helps you get a better understanding of the problem.<br />
## Once you have something specific that you're stuck on, you can ask other people in the class.<br />
### '''Don't be afraid to ask. Everyone gets stuck and feels stupid sometimes. However, you get to choose how you react to it.'''<br />
### '''At the same time, it really helps to work with people who are on about the same level in the course.'''<br />
## Look on Piazza. Ask questions if yours hasn't come up yet. Be that awesome guy/girl who helps answer questions.<br />
## You can ask the TA if all else fails. We are here to help you learn!<br />
<br />
Here is an old algorithm for studying for tests (the final in this case), salvaged from the sands of time:<br />
<pre>For each topic on the final, find problems on them and do them.<br />
If you can solve them on your own, move on.<br />
Else if you are stuck, look at the solution and figure out if you<br />
are missing a trick or if you do not understand the concepts.<br />
If the problem is that you are stuck on some random trick,<br />
just learn the trick.<br />
Stare at the solutions, ask Piazza, your TA, etc.<br />
Questions you should ask at this stage:<br />
What is the problem asking me to do?<br />
How was I suppose to follow the instructions<br />
to solve the problem?<br />
What part of the problem do I not understand?<br />
What is the fastest way to clear up that misunderstanding?<br />
Then if you think you are still stuck conceptually, review<br />
and learn the concept, however you learn best.<br />
Suggestions for picking up concepts quickly (~1-2 hours):<br />
Discussion notes typically have a very concise recap of the<br />
thing they are going over.<br />
There are guides for particularly tricky things on Piazza,<br />
like Logic, Pairs and Lists in Scheme, etc.<br />
Find them and go over them.<br />
Ask a TA: "what is the best way to learn X?"<br />
If these do not work and you are still shaky after an hour<br />
or two, it might be worth watching a lecture or reading<br />
the notes.</pre><br />
<br />
== Composition ==<br />
=== General style guidelines from 61A website ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=149 Source: Spring 2014 Piazza (149)]<br />
<br />
'''Student Question'''<br />
<br />
Are we required to add any comments to our code to say what a function does, etc.? And does clarity of code count for this project, in which case should we write comments at the end of not-so-clear statements? Thanks.<br />
<br />
'''Student Answer'''<br />
<br />
Docstrings of each function are already provided. If you add a helper function, you should write a docstring for it.<br />
<br />
The [http://inst.eecs.berkeley.edu/~cs61a/sp14/style_guide.html#comments style guide on the course website] advises: "Your actual code should be self-documenting -- try to make it as obvious as possible what you are doing without resorting to comments. Only use comments if something is not obvious or needs to be explicitly emphasized"<br />
<br />
'''Instructor Answer'''<br />
<br />
You should always aim to make your code "self-documenting," meaning it is clear what your code is doing without the aid of comments. You should try to keep the number of comments to a minimum, but if there are lines which you think are unclear/ambiguous, feel free to add a comment.<br />
<br />
All projects in this class contain a 3 point component that is judged solely on your code "composition" -- i.e. whether your code is clear, concise, and easy to read.<br />
<br />
=== Simplifying code ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=1116 Source: Spring 2014 Piazza (1116)]<br />
<br />
Hi everyone, here's some tips about certain functions in Python that can greatly simplify your code for the Trends project.<br />
<br />
'''Sorting keys'''<br />
<br />
You should be familiar with the max and min functions in python, which can take in many arguments and return the maximum value.<br />
<br />
<pre>>>> max(1,3,2)<br />
3</pre><br />
<br />
These functions can also take in lists:<br />
<br />
<pre>>>> min([1,5,1,6])<br />
1</pre><br />
<br />
(In fact they can take in any ''iterable'' and return the maximum/minimum value)<br />
<br />
These functions work because Python knows how to compare the elements in the list (they are all integers). But what if the elements in the list are not integers? Fortunately, there is a way for you to tell Python how to turn each element of the list into a number that it can understand.<br />
<br />
Lets start with an example. Lets say you have a list of strings, and want to find the shortest string in the list. Here's what you can do:<br />
<br />
<pre>>>> min(['hihi', 'bye', 'a', 'zebra'], key=len)<br />
'a'</pre><br />
<br />
Notice the new keyword argument key we are passing into the min function. key is a function that min applies to each element of the list. In this case, the key is the len function, which returns the length of each string. Applying the key function to each element will return a cooresponding integer, which Python can easily use to find the minimum element.<br />
<br />
You can also use keys in the <code>sorted</code> function too, which returns a sorted list of its inputs, based on the key function passed in.<br />
<br />
<pre>>>> sorted(['hihi', 'bye', 'a', 'zebra'], key=len)<br />
['a', 'bye', 'hihi', 'zebra']</pre><br />
<br />
We can have more complex key functions. Here we sort a list of people by their age, which is the second element in the tuple. A key function, once defined, works for sorted, min and max:<br />
<br />
<pre>>>> names = [('Alice', 19, 'F'), ('Bob', 5, 'M'), ('Charlie', 12, 'M')]<br />
>>> get_age = lambda name: name[1]<br />
>>> sorted(names, key=get_age)<br />
[('Bob', 5, 'M'), ('Charlie', 12, 'M'), ('Alice', 19, 'F')]<br />
>>> max(names, key=get_age)<br />
('Alice', 19, 'F')</pre><br />
<br />
'''Dictionary default values'''<br />
<br />
Suppose we have a dictionary mapping names to counts:<br />
<br />
<pre>>>> d = {'apples': 1, 'pears': 9000}</pre><br />
<br />
If we want to add a new pear to the dictionary, we can use:<br />
<br />
<pre>>>> d['pears'] = d['pears'] + 1<br />
>>> d<br />
{'apples': 1, 'pears': 9001}</pre><br />
However we cannot use the same code to add a new item that is not already in the dictionary.<br />
<br />
<pre>>>> d['oranges'] = d['oranges'] + 1<br />
Traceback (most recent call last):<br />
...<br />
KeyError: 'oranges'</pre><br />
<br />
To solve this problem, we have to use <code>dict.setdefault(key, default)</code>. If <code>key</code> is in <code>dict</code>, it will return <code>dict[key]</code>. If not, it will insert <code>key</code> with a value of <code>default</code> and return <code>default</code>. Now we can write:<br />
<br />
<pre>>>> d['oranges'] = d.setdefault('oranges', 0) + 1<br />
>>> d<br />
{'oranges': 1, 'apples': 1, 'pears': 9001}<br />
>>> d['oranges'] = d.setdefault('oranges', 0) + 1<br />
>>> d<br />
{'oranges': 2, 'apples': 1, 'pears': 9001}</pre><br />
<br />
There's actually a even better way of doing this. If you are curious to find out, look up <code>collections.defaultdict</code>.<br />
<br />
'''For loops'''<br />
<br />
If you are iterating through a list and want to get both the item and the index the item is at, the built-in function enumerate is helpful here.<br />
<br />
<pre>>>> a = ["apple", "pear", "orange"]<br />
>>> for index, fruit in enumerate(a):<br />
... print(index, fruit)<br />
...<br />
0 apple<br />
1 pear<br />
2 orange</pre><br />
<br />
You can iterate through each key-value pair in a dictionary with dictionary.items. This is useful if you want to access both the key and the value at the same time.<br />
<br />
<pre>>>> prices = {"apple": 3, "pear": 5, "orange": 20}<br />
>>> for fruit, price in prices.items():<br />
... print(fruit, price)<br />
...<br />
apple 3<br />
pear 5<br />
orange 20</pre><br />
<br />
Hope this helps for the project!<br />
<br />
=== Programming style in scheme ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=2068 Source: Spring 2014 Piazza (2068)]<br />
<br />
Since Scheme has no rules on whitespace and indentation, you could technically write all your Scheme in one line like this:<br />
<pre>(define (fib n) (if (< n 1) n (+ (fib (- n 1)) (fib (- n 2)))))</pre><br />
<br />
But that would be terrible and everyone who had to read your code would hate you for it. Here is a more reasonable version:<br />
<pre>(define (fib n)<br />
(if (< n 1) ; Arguments to if are flush<br />
n ; Each argument gets a new line<br />
(+ (fib (- n 1)) ; Sometimes it makes to insert a newline<br />
(fib (- n 2))))) ; so that you can see arguments side by side</pre><br />
<br />
Remember that code is primarily for humans to read and incidentally for computers to run.<br />
<br />
Here are some more examples:<br />
<pre>(define (deep-map f lst)<br />
(cond ((null? lst) lst)<br />
((list? (car lst))<br />
(cons (deep-map f (car lst))<br />
(deep-map f (cdr lst))))<br />
(else<br />
(cons (f (car lst))<br />
(deep-map f (cdr lst)))) ))<br />
<br />
STk> (deep-map (lambda (x) (* x x)) (list 1 2 (list (list 3) 4)))<br />
(1 4 ((9) 16))</pre><br />
<br />
<pre>(define (reverse lst)<br />
(define (helper lst result)<br />
(if (null? lst)<br />
result<br />
(helper (cdr lst)<br />
(cons (car lst) result)) ))<br />
(helper lst ()) )<br />
<br />
STk> (reverse (list 1 2 3))<br />
(3 2 1)</pre><br />
<br />
=== ucb.py's <code>trace</code> method ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=3000 Source: Spring 2014 Piazza (3000)]<br />
<br />
'''Student Question'''<br />
<br />
How does the trace in ucb.py actually work?<br />
<br />
'''Student Answer'''<br />
<br />
It's actually very similar to the <code>printed</code> function that was defined in the Hog spec!<br />
<br />
<pre>def printed(fn):<br />
def print_and_return(*args):<br />
result = fn(*args)<br />
print('Result:', result)<br />
return result<br />
return print_and_return<br />
</pre><br />
<br />
The main idea is still the same in <code>trace</code> -- we want to figure out all the arguments and save the result of calling the function with those arguments (so we can print it before returning it). One (sort of) major improvement is <code>**kwds</code>. Just like how <code>*args</code> collected all the "positional arguments", <code>**kwds</code> captures all the "keyword arguments" (the ones of the form <code>param='some_val'</code>). This can be a bit confusing, but a couple of examples might help!<br />
<br />
<pre>&gt;&gt;&gt; def add_three(a, b, c):<br />
... return a + b + c<br />
&gt;&gt;&gt; add_three(1, 2, 3) # all arguments are positional (normal)<br />
&gt;&gt;&gt; add_three(1, 2, c=3) # a, b are positional arguments, c is a keyword argument<br />
<br />
&gt;&gt;&gt; def fn(*args, **kwargs):<br />
... print(args)<br />
... print(kwargs)<br />
&gt;&gt;&gt; fn(1, 2, 3)<br />
(1, 2, 3)<br />
{}<br />
&gt;&gt;&gt; fn(1, k=2)<br />
(1,)<br />
{'k' : 2}<br />
&gt;&gt;&gt; fn(a=1, b=2, c=3)<br />
()<br />
{'a':1, 'b':2, 'c':3}<br />
</pre><br />
<br />
Since there are only two types of arguments, having both <code>*args</code> and <code>**kwds</code> covers all our bases. If we passed <code>printed</code> a keyword argument, it could cause an error!<br />
<br />
Everything else in <code>trace</code> just makes the output prettier and more helpful. <code>trace</code> uses the <code>_PREFIX</code> global variable to keep track of how far to indent the next print statement. It catches exceptions and prints them out, before re-raising that exception. It also uses some Python black magic to figure out the name of the function so we can print <code>some_fn</code> instead of <code>&lt;function some_fn at 0x...&gt;</code>.<br />
<br />
If there's a particular aspect of <code>trace</code> that you're confused about, feel free to post a followup!<br />
<br />
== Debugging ==<br />
== Miscellaneous ==<br />
=== Andrew Huang's tips ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=779 Source: Spring 2014 Piazza (779)]<br />
<br />
Order of evaluation matters. The rules for evaluating call expressions are<br />
<br />
# Evaluate the operator<br />
# Evaluate the operands<br />
# Call the operator on the operands (and draw a new frame...)<br />
<br />
For example:<br />
<pre>def baz():<br />
print("this was first")<br />
def bar(x):<br />
print(x)<br />
return lambda x: x * x<br />
return bar # baz is a function that when called, returns a function named bar<br />
<br />
>>> baz() # the operator is baz, there are no operands<br />
this was first<br />
<function bar at 0x2797e20><br />
>>> baz()("this was second") # the operator is baz(), the operand is "this was second"<br />
this was first<br />
this was second<br />
<function <lambda> at 0x2120e20><br />
>>> baz()("this was second")(3) # the operator is baz()("this was second"), the operand is 3<br />
this was first<br />
this was second<br />
9<br />
>>> def bar(x):<br />
... print(x)<br />
... return 3<br />
... <br />
>>> baz()("this was second")(bar("this was third")) # the operator is baz()("this was second"), the operand is bar("this was third")<br />
this was first<br />
this was second<br />
this was third<br />
9</pre><br />
<br />
In order to solve any problem, you must first understand what the problem is asking. Often times it helps to try to explain it concisely in English. It also helps to come up with small examples. For example:<br />
<br />
<pre>def mouse(n):<br />
if n >= 10:<br />
squeak = n // 100<br />
n = frog(squeak) + n % 10<br />
return n<br />
<br />
def frog(croak):<br />
if croak == 0:<br />
return 1<br />
else:<br />
return 10 * mouse(croak+1)<br />
<br />
mouse(21023508479)</pre><br />
<br />
So the goal is to figure out what <code>mouse(21023508479)</code> evaluates to.<br />
<br />
One way is to just step-by-step evaluate this, as an interpreter would.<br />
<br />
Another way, is to understand what the functions are doing.<br />
<br />
Looking at <code>mouse</code>, we see that it takes in a number and outputs that same number if it is smaller than 10. otherwise, it'll return something weird. In order to understand that weird thing, we have to understand what <code>frog</code> is doing. <code>frog</code> takes in a number and if that number is <code>0</code>, return <code>1</code>. Otherwise, return ten times <code>mouse(croak+1)</code>. Well, this is still confusing. Let's try a small example.<br />
<pre>>>> mouse(357)<br />
47<br />
>>> mouse(123)<br />
23<br />
>>> mouse(1234)<br />
44<br />
>>> mouse(12345)<br />
245</pre><br />
There is a pattern. We notice that the resulting number is composed of every other digit of the original, plus one (except for the last one.)<br />
So <code>21023508479</code> is <code>[2+1][0+1][3+1][0+1][4+1][9] = 314159</code>. Can you see how the code reflects that?<br />
However in this particular example, the pattern is definitely tricky to find here, so it might make more sense to brute force it.<br />
<br />
<br />
Remember for recursion, you always need to find three things:<br />
* One or more base cases<br />
* One or more was to reduce the problem<br />
* A way to solve the problem given solutions to smaller problems<br />
For example, the discussion notes, we asked you to write count_stairs. This function takes in n, the number of steps, and returns all the ways you can climb up them if at each step, you can take either one or two steps.<br />
<br />
* Base cases: if we consider n to be the number of steps left to climb, then it makes sense that if there is 1 step left, then there is exactly one way. If there are two steps left, then there are exactly 2 ways (1 step, 1 step, or two steps). Why do we need two base cases here?<br />
* We can make the problem smaller by reducing the n. At each step, we can take one step (resulting in count_stairs(n-1)) or two steps (count_stairs(n-2)).<br />
* Assuming we get the solutions to the two recursive calls, we should add them together to get all the ways we can climb the stairs.<br />
<br />
Thus we end up with<br />
<pre>def count_stairs(n):<br />
if n <= 2:<br />
return n<br />
else:<br />
return count_stairs(n-1) + count_stars(n-2)</pre><br />
Notice that at each stair step, we either take one step or two steps. This is a common pattern in tree recursion. Look through Discussion 3 for more info.<br />
<br />
=== Y combinators (in Scheme) ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=2450 Source: Spring 2014 Piazza (2450)]<br />
<br />
'''Student Question'''<br />
<br />
Can someone explain this to me?<br />
<pre>scm> (((lambda (f) (lambda (x) (f f x)))<br />
(lambda (f k) (if (zero? k) 1 (* k (f f (- k 1)))))) 5)</pre><br />
I've edited the code as follows:<br />
<br />
<pre>(<br />
(<br />
(lambda (f) <br />
(lambda (x) (f f x))<br />
)<br />
(lambda (f k) <br />
(if (zero? k) 1 <br />
(* k (f f (- k 1)))<br />
)<br />
)<br />
) 5<br />
)</pre><br />
<br />
My understanding is that the second lambda function is passed as the first <code>f</code> in the first lambda function and the <code>5</code> is passed in as <code>x</code>. But does that mean <code>f f x</code> becomes the second lambda function with itself and <code>x</code> passed as the arguments to <code>(f k)</code>?<br />
<br />
'''Student Answer'''<br />
<br />
You're on the right track. The first lambda function is a higher order function that takes in a function, and then returns a function that takes one argument. It's actually the third lambda that is then passed into the first lambda (currying!) and then 5 is then passed into the resulting function.<br />
<br />
In case you're curious, this is the Python equivalent:<br />
<br />
<pre>>>> (lambda f: (lambda x: f(f, x))) (lambda f, k: 1 if k == 0 else (k * f(f, k - 1)))(5)</pre><br />
Which is then equivalent to:<br />
<br />
<pre>>>> def func1(f):<br />
def func2(x):<br />
return f(f, x)<br />
return func2<br />
>>> def func3(f, k):<br />
if k == 0:<br />
return 1<br />
else:<br />
return k * f(f, k - 1)<br />
>>> func1(func3)(5)<br />
120</pre><br />
By the way, this is just a fancy way of recursively calculating the factorial using only lambda functions. If you're still curious as to how this works, you could try this in Python tutor. Except I would recommend calculating 3! instead of 5, because it's a lot of frames.<br />
<br />
'''Instructor Answer'''<br />
<br />
Maybe it will look a little nicer in Python:<br />
<br />
<pre>(lambda f: lambda x: f(f, x))(lambda g, k: 1 if k == 0 else (k * g(g, k-1)))(5)</pre><br />
Or maybe not.<br />
<br />
So the idea is, you define a lambda function that takes a function <code>f</code>, and that returns a lambda function that takes an argument <code>x</code> and returns <code>f(f, x)</code>. Then, you call this lambda function you just defined on another lambda function (let's call this <code>func</code>) that takes a function g and another argument k, and is basically the factorial function. This first call returns the inner lambda of the first part, and when that's called with <code>5</code> you're essentially calling <code>func(func, 5)</code>. The chain of recursive calls then works as follows:<br />
<br />
<pre>func(func, 5) -> 5 * func(func, 4) -> 5 * 4 * func(func, 3) -> ... -> 120</pre><br />
In functional programming theory, this is known as a Y Combinator, and it is how you achieve recursion with just lambda functions. If you're wondering why we need <code>func</code> to take in a function as the first parameter, see what would happen if you took that part out!</div>Jeffreylu017https://www.ocf.berkeley.edu/~shidi/cs61a/wiki/GuidesGuides2014-06-21T02:44:25Z<p>Jeffreylu017: /* Recursion */</p>
<hr />
<div>== Higher-order functions ==<br />
== Environment diagrams ==<br />
=== Environment diagram Rules ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=131 Source: Spring 2014 Piazza (131)]<br />
<br />
Environment Diagrams are very important in our understanding of how the computer interprets our code.<br />
<br />
'''We will test you on this in every exam.'''<br />
<br />
'''It will never go away.'''<br />
<br />
'''Given that, master it as quickly as you can! :)'''<br />
<br />
Below are the rules I follow when drawing environment diagrams. If you understand and faithfully follow these rules when drawing them, you'll never get them wrong.<br />
<br />
One thing you haven't learned yet is nonlocal. You can skip that particular step for now (step 2 of Assignment).<br />
<br />
Post here if you have any questions!<br />
<br />
You can also take a look at this link for some examples of environment diagrams: http://albertwu.org/cs61a/notes/environments<br />
<br />
For a different perspective on the rules, check out: http://markmiyashita.com/cs61a/sp14/environment_diagrams/rules_of_environment_diagrams/<br />
<br />
A handout with detailed instructions on drawing environment diagrams is also available here (linked on the bottom of the course homepage): http://inst.eecs.berkeley.edu/~cs61a/sp14/pdfs/environment-diagrams.pdf<br />
<br />
<pre>Environment Diagram Rules<br />
=========================<br />
<br />
Creating a Function<br />
--------------------<br />
1. Draw the func <name>(<arg1>, <arg2>, ...)<br />
2. The parent of the function is wherever the function was defined<br />
(the frame we're currently in, since we're creating the function).<br />
3. If we used def, make a binding of the name to the value in the current frame.<br />
<br />
Calling User Defined Functions<br />
------------------------------<br />
1. Evaluate the operator and operands.<br />
2. Create a new frame; the parent is whatever the operator s parent is.<br />
Now this is the current frame.<br />
3. Bind the formal parameters to the argument values (the evaluated operands).<br />
4. Evaluate the body of the operator in the context of this new frame.<br />
5. After evaluating the body, go back to the frame that called the function.<br />
<br />
Assignment<br />
----------<br />
1. Evaluate the expression to the right of the assignment operator (=).<br />
2. If nonlocal, find the frame that has the variable you re looking for,<br />
starting in the parent frame and ending just before the global frame (via<br />
Lookup rules). Otherwise, use the current frame. Note: If there are multiple<br />
frames that have the same variable, pick the frame closest to the current<br />
frame.<br />
3. Bind the variable name to the value of the expression in the identified<br />
frame. Be sure you override the variable name if it had a previous binding.<br />
<br />
Lookup<br />
------<br />
1. Start at the current frame. Is the variable in this frame?<br />
If yes, that's the answer.<br />
2. If it isn't, go to the parent frame and repeat 1.<br />
3. If you run out of frames (reach the Global frame and it's not there), complain.<br />
<br />
Tips<br />
----<br />
1. You can only bind names to values.<br />
No expressions (like 3+4) allowed on environment diagrams!<br />
2. Frames and Functions both have parents.</pre><br />
<br />
== Sequences ==<br />
=== Reversing tuples ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=639 Source: Spring 2014 Piazza (639)]<br />
<br />
'''Student Question'''<br />
<br />
Why does [::-1] tuple work while the tuple [0:3:-1] doesn't?<br />
<br />
I thought the -1 after the second semicolon meant that the interpreter is going to read the indexes "backwards". <br />
<br />
'''Student Answer'''<br />
<br />
The syntax of slicing is <tt>tup[start:end:step]</tt>:<br />
<ul><li>start from index <tt>start</tt> and end just before index <tt>end</tt>, incrementing the index by <tt>step</tt> each time<br />
</li><li>if no <tt>step</tt> is provided, <tt>step</tt> = 1<br />
</li><li>if <tt>step</tt> is positive, default values if not provided: <tt>start</tt> = 0, <tt>end</tt> = <tt>len(tup)</tt><br />
</li><li>if <tt>step</tt> is negative, default values if not provided: <tt>start</tt> = -1, <tt>end</tt> = one position before the start of the string<br />
</li></ul><br />
<br />
<pre>&gt;&gt;&gt; (1, 2, 3)[::-1] # start at index -1, end one position before the start of the string<br />
(3, 2, 1)<br />
&gt;&gt;&gt; (1, 2, 3)[0:3:-1] # start at 0 and go to 3, but step is negative, so this doesn't make sense and an empty tuple is returned<br />
()<br />
</pre><br />
<br />
This is a helpful visualization from http://en.wikibooks.org/wiki/Python_Programming/Strings#Indexing_and_Slicing:<br />
<blockquote><br />
To understand slices, it's easiest not to count the elements themselves. It is a bit like counting not on your fingers, but in the spaces between them. The list is indexed like this:<br />
<pre>Element: 1 2 3 4<br />
Index: 0 1 2 3 4<br />
-4 -3 -2 -1<br />
</pre><br />
</blockquote><br />
More info about slicing at http://stackoverflow.com/a/13005464/2460890.<br />
<br />
=== Slicing with negative step ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=702 Source: Spring 2014 Piazza (702)]<br />
<br />
'''Student Question'''<br />
<br />
if the third example returns an empty tuple because you can't take negative steps from 0 to 4, shouldn't the second example also return an empty tuple?<br />
<br />
Can someone explain why each example returns the respective answers?<br />
<br />
Thanks<br />
<br />
<pre>>>> x= (1,2,3,4)<br />
>>> x[0::-1]<br />
(1,)<br />
>>> x[::-1]<br />
(4, 3, 2, 1)<br />
>>> x[0:4:-1]<br />
()<br />
>>> x[1::-1]<br />
(2, 1)</pre><br />
<br />
'''Instructor Answer'''<br />
<br />
(For reference, the notation is <tt>x[start:end:step]</tt>)<br />
<br />
Python does something a very strange when the step is negative: if you omit the arguments to start and end, Python will fill them with what makes sense for a negative step. In the simple case of <tt>x[::-1]</tt>, Python fills in the start with <tt>len(x)-1</tt> and the end with <tt>-(len(x)+1)</tt>. The end term is strange, but remember that the end term isn't included. We therefore can't use 0, but we can't use -1 either, since that clearly refers to the last element of the tuple. We need to fully wrap the negative index around, to refer to the element "before" the 0th index. This way, Python will start at the end of the tuple and proceed to the beginning of the tuple.<br />
<br />
That's why <tt>x[0:4:-1]</tt> doesn't make sense: how can we start at 0 and end at 4, if we're proceeding backwards?<br />
<br />
And that's why <tt>x[0::-1]</tt> makes sense (albeit, in a strange way): Python is proceeding from the 0 index to the beginning of the list. It includes the start index, which is why you see a 1 pop up.<br />
<br />
Let me know if that was confusing!<br />
<br />
== Recursion ==<br />
=== Recursion visualizer ===<br />
[https://zvzzt.wordpress.com/2014/05/03/python-recursion-visualization-with-rcviz/ Source: Python Recursion Visualization with rcviz]<br />
<br />
== Data abstraction ==<br />
== Time complexity ==<br />
=== Andrew Huang's guide to order of growth and function runtime ===<br />
[https://docs.google.com/document/d/1TxfKmM3MlH032hjSUh92I0kQDVcvmitTSzYObGMr8Bk Source: Guide to Order of Growth and Function Runtime (Retrieved June 16th, 2014)]<br />
<br />
'''Introduction'''<br />
<br />
Confused by $O$, $\Omega$, and $\Theta$?<br />
<br />
Want to figure out the runtime of that tricky function?<br />
<br />
Read this.<br />
<br />
'''NOTE THAT THIS GUIDE STARTS WITH BIG O, WHICH IS DIFFERENT FROM THETA. IF YOU UNDERSTAND BIG O, THETA IS EASY (IN FACT, IT DEFINES THETA IN TERMS OF BIG O BELOW).'''<br />
<br />
First some math.<br />
<br />
Formal definition of O(Big O):<br />
<br />
Let $f(n)$ and $g(n)$ be functions from positive integers to positive reals. We say $f \in O(g)$ (“f grows no faster than g”) if there is a constant $0 < c < \inf$ <such that $f(n) \leq c \cdot g(n)$.<br />
<br />
(Paraphrased from Dasgupta, Papadimitriou, & Vazirani)<br />
<br />
(You'll see this again in CS 170)<br />
<br />
What the heck does that mean?<br />
<br />
Let’s look at math functions for a second (just a second).<br />
<br />
Say $f(n)=5n$ and $g(n)=n^{2}$<br />
<br />
What does that look like on a graph?<br />
<br />
http://www.wolframalpha.com/input/?i=plot+5n+and+n%5E2+from+0+to+10<br />
<br />
There’s a section where $n$ dominates $n^{2}$, from 0 to 5, but we don’t really care, because after that point, $n^{2}$ is larger, all the way to infinity! By the definition, we could scale $n^{2}$ by 5 and we would span that initial gap.<br />
<br />
Thus we can say $5n \in O(n^{2})$ or $f \in O(g)$.<br />
<br />
Can we say the converse? That is, is $n^{2} \in O(5n)$?<br />
<br />
Not at all! From the graph we see that $n^{2}$ grows too quickly for $n$ to catch up, no matter what constant we scale $n$ by.<br />
<br />
So what if $f(n)=n+1000$ and $g(n)=n^{2}$?<br />
<br />
It turns out $n+1000 \in O(n^{2})$ still, because according to the definition, as long as we can multiply $n^{2}$ by some $c$, such that the gap of 1000 is spanned, we’re good. In the case, $c=1001$.<br />
<br />
'''What about and $\Omega$ and $\Theta$?'''<br />
<br />
If you digested all of the above, the rest isn’t scary! (Note, $a \equiv b$ means $a$ is equivalent to $b$)<br />
<br />
$f \in \Omega(g) \equiv g \in O(f)$ (You'll see this again briefly in CS 170)<br />
<br />
$f \in \Theta(g) (f \in O(g) and g \in O(f))$<br />
<br />
'''This means that if $f$ is Theta of $g$, then there exist some $c_{1}$ and $c_{2}$ such that'''<br />
<br />
'''$c_{1}g > f$ and'''<br />
<br />
'''$c_{2}g < f$'''<br />
<br />
'''for all positive integers.'''<br />
<br />
'''What does that mean for Python functions?'''<br />
<br />
Given a function $f$, we want to find out how fast that function runs. One way of doing this is to take out a stopwatch, and clock the amount of time it takes for $f$ to run on some input. However, there are tons of problems with that (different computers => different speeds; only one fixed input? Maybe $f$ is really fast for that input but slow for everything else; next year, all the measurements need to be redone on new computers; etc.) Instead, we'll count the steps that a function needs to perform as a function of its input. For example, here are some of the functions that take one step regardless of their input:<br />
<br />
<code>mul</code><br />
<br />
<code>add</code><br />
<br />
<code>sub</code><br />
<br />
<code>print</code><br />
<br />
<code>return</code><br />
<br />
<code>...</code><br />
<br />
So for example, <code>(3 + 3 * 8) % 3</code> would be 3 steps--one for the multiply, one of the add, and one for the mod.<br />
<br />
Let's take a simple example:<br />
<pre>def square(x):<br />
return x * x</pre><br />
<code>square</code> is a function that for any input, always takes two steps, one of the multiplication, and one for returning. Using the notation, we can say square ∈ Θ(1).<br />
<br />
Functions with iteration (for loops, recursion, etc.), usually multiply the steps by some factor. For example, consider factorial:<br />
<pre>def factorial(n):<br />
if n == 0:<br />
return 1<br />
else:<br />
return n * factorial(n-1)</pre><br />
factorial ∈ Θ(n). Why? Well given some input <code>n</code>, we do <code>n</code> recursive calls. At each recursive call, we carry out 4 steps, one for if <code>n == 0</code>, one for subtraction, one for multiply, one for return. Plus, we have the base case, which is another 2 steps, one for if and one for return. So <code>factorial(n)</code> takes $4n+2$ steps => ∈ Θ(n).<br />
<br />
As mentioned, we care about how the running time (how long the function takes to run) of the function changes, '''as we increase the size of the argument'''. So if we imagine a graph, then the x-axis represents the size of our input, and the y-axis represents how long the function took to run for each x. As the size of the input increases, the function’s runtime does something on the graph. So when we say something like “$O(n^{2})$ where $n$ is the length of the list”, we are saying as we double the size of the list, the function is expected to run at most four times as long. '''NOTE ALSO THAT I SAID WHAT $n$ IS! ALWAYS GIVE YOUR UNITS.'''<br />
<br />
This means that when we compare two functions A and B, A may be overall slower than B as we increase the size of their arguments. However, it’s possible at some specific arguments, the A may run faster (like the $f(n)=5n$ and $g(n)=n^{2}$ example above.)<br />
<br />
'''This also means we do not care about the time taken of any particular input! This implies that all those constant-time base cases all those functions don’t really matter, because they don’t scale. That is, only one specific input causes the base case to be reached, and if we increased the size of the argument, $O(1)$ doesn't necessarily hold.'''<br />
<br />
'''Brief “What runs faster than what”'''<br />
<br />
Sorted from fastest to slowest. This is by no means comprehensive.<br />
* $\Theta(1)$<br />
* $\Theta(\log(n))$<br />
* $\Theta(n)$<br />
* $\Theta(n \log(n))$<br />
* $\Theta(n^{2})$<br />
* $\Theta(n^{3})$<br />
* $\Theta(2^{n})$<br />
* (Anything past this point is kind of ridiculous)<br />
* $\Theta(n!)$<br />
* $\Theta(n^{n})$<br />
<br />
'''So we know about the math and the motivation, now how do we actually assign runtimes to real Python functions?'''<br />
<br />
What you must understand, is that there is no one method for finding the runtime. You MUST look at a function holistically or you won’t get the right answer. What does this mean? '''In order to get the correct runtime, you first must understand what the function is doing! You cannot pattern-match your way to becoming good at this.'''<br />
<br />
'''This cannot be stressed enough: UNITS MATTER, if you say O((n)), you must tell us what $n$ is.'''<br />
<br />
General tips<br />
# UNDERSTAND WHAT THE FUNCTION IS DOING!!!<br />
# Try some sample input. That is, pretend you’re the interpreter and execute the code with some small inputs. What is the function doing with the input? Having concrete examples lets you do tip 1 better. You can also graph how the runtime increases as the argument size increases.<br />
# If applicable, draw a picture of the tree of function calls. This shows you the "growth" of the function or how the function is getting "bigger", which will help you do tip 1 better.<br />
# If applicable, draw a picture of how the input is being modified through the function calls. For example, if your input is a list and your function recursively does something to that list, draw out a list, then draw out parts of the list underneath it that are called during the recursion. Helps with tip 1.<br />
# See tip 1.<br />
<br />
Anyways, let's examine some common runtimes (keep scrolling). Remember, '''this is in no way a comprehensive list, NOR IS IT TRYING TO TEACH YOU HOW TO FIND THEM.''' This post is just to give you a starting point into orders of growth by showing you some examples and basic details about each runtime.<br />
<br />
'''Constant $\Theta(1)$'''<br />
<br />
''What it looks like:''<br />
<br />
http://www.wolframalpha.com/input/?i=plot+5<br />
<br />
''Example:''<br />
<br />
<pre>def add(x, y):<br />
return x + y</pre><br />
$add \in \Theta(1)$, where 1 is.. well a constant...<br />
<br />
''Approach:''<br />
<br />
The key behind constant time functions is that regardless of the size of the input, they always run the same number of instructions.<br />
<br />
''Don’t fall for this Trap:''<br />
<br />
<pre>def bar(n):<br />
if n % 7 == 0:<br />
return "Bzzst"<br />
else:<br />
return bar(n -1)</pre><br />
$\mathtt{bar} \in \Theta(1)$. Why?<br />
<br />
'''Logarithmic $\Theta(\log(n))$'''<br />
<br />
''What it looks like:''<br />
<br />
http://www.wolframalpha.com/input/?i=plot+4log3n+from+0+to+10<br />
<br />
''Example:''<br />
<br />
<pre>def binary_search(sorted_L, n):<br />
""" sorted_L is a list of numbers sorted from<br />
smallest to largest<br />
"""<br />
if sorted_L == []:<br />
return False<br />
mid_num = sorted_L[len(sorted_L) // 2]<br />
if n == mid_num:<br />
return True<br />
elif n < mid_num:<br />
return binary_search(sorted_L[:mid_num], n)<br />
else:<br />
return binary_search(sorted_L[mid_num:], n)</pre><br />
$\mathtt{binary\_search} \in \Theta(log(n))$, where $n$ is the number of elements in <code>sorted_L</code>.<br />
<br />
''Approach:''<br />
<br />
Logarithmic functions scale down the size of the problem by some constant every iteration (either with a recursive loop, a for loop, or a while loop). Also, logarithmic functions do not branch out--they generally do not make more than one call to themselves per recursion.<br />
<br />
'''Linear $\Theta(n)$'''<br />
<br />
''What it looks like:''<br />
<br />
http://www.wolframalpha.com/input/?i=plot+8n+from+0+to+10<br />
<br />
''Examples:''<br />
<br />
<pre>def sum_list(L):<br />
sum = 0<br />
for e in L:<br />
sum += e<br />
return sum</pre><br />
$\mathtt{sum\_list} \in \Theta(n)$, where $n$ is the number of elements in $L$.<br />
<br />
</pre>def countdown(n):<br />
if n > 0:<br />
print(n)<br />
countdown(n - 1)<br />
else:<br />
print("Blast off!")</pre><br />
$\mathtt{countdown} \in \Theta(n)$, where $n$ is n.<br />
<br />
''Approach:''<br />
<br />
Linear functions usually act on sequences or other collections of data. In that case, the function will go through the elements once or twice or ''k'' times, where $k<<n$. If the function acts on a number, the number usually gets smaller by a constant each iteration.<br />
<br />
Don't fall for this trap:<br />
<pre>def two_for_loops(n):<br />
for a in range(n):<br />
if n == 4:<br />
for y in range(n):<br />
print("Admiral Ackbar")<br />
else:<br />
print("It's a trap!")</pre><br />
$\mathtt{two\_for\_loops} \in \Theta(n)$, where $n$ is n. Why?<br />
<br />
'''Loglinear/Linearithmic $\Theta(n \log(n))$'''<br />
<br />
''What it looks like:''<br />
<br />
http://www.wolframalpha.com/input/?i=plot+nlog%28n%29+from+0+to+10<br />
<br />
''Example:''<br />
<br />
<pre>def merge(s1, s2):<br />
if len(s1) == 0:<br />
return s2<br />
elif len(s2) == 0:<br />
return s1<br />
elif s1[0] < s2[0]:<br />
return [s1[0]] + merge(s1[1:], s2)<br />
else:<br />
return [s2[0]] + merge(s1, s2[1:])<br />
<br />
def mergesort(lst):<br />
if len(lst) <= 1:<br />
return lst<br />
else:<br />
middle = len(lst) // 2<br />
return merge(mergesort(lst[:middle]), \<br />
mergesort(lst[middle:]))</pre><br />
$\mathtt{mergesort} \in \Theta(n \log(n))$, where $n$ is the number of elements in <code>lst</code>.<br />
<br />
Approach:<br />
These functions tend to make two recursive calls, each making the problem smaller by a half. There's a neat way to see this. For example in mergesort, start with an entire line, which represents mergesort called on the initial list. From there, the list gets split in half by the two recursive calls to mergesort in the code, so draw the another line right below the first, of the same length, but with a small gap in the middle to represent the split. Repeat until you're tired. At the end, you get a rectangle that's nwide and (n)tall!<br />
<pre><nowiki><br />
---------------<br />
------- -------<br />
--- --- --- ---<br />
- - - - - - - -<br />
</nowiki></pre><br />
The total area is the runtime, $\Theta(n \log(n))$<br />
<br />
''Don’t fall for this trap:''<br />
<br />
Don’t confuse functions that have an average running time of n(n)(like quicksort) with functions that are in (n(n))<br />
<br />
'''Polynomial $\Theta(n^{2})$,$\Theta(n^{3})$, etc.'''<br />
<br />
''What it looks like:''<br />
<br />
http://www.wolframalpha.com/input/?i=plot+n%5E2%2B3+from+0+to+10<br />
<br />
''Example:''<br />
<br />
<pre>def print_a_grid(n):<br />
for _ in range(n):<br />
for _ in range(n):<br />
print("+", end="")<br />
print("")</pre><br />
$\mathtt{print\_a\_grid} \in \Theta(n^{2})$, where $n$ is n.<br />
<br />
''Approach:''<br />
<br />
Polynomial functions will examine each element of an input many, many times, as opposed to linear functions, which examine some constant number of times.<br />
<br />
''Don’t fall into this trap:''<br />
<br />
Don’t get polynomial confused with exponential (below).<br />
<br />
'''Exponential'''<br />
<br />
''What it looks like:''<br />
<br />
http://www.wolframalpha.com/input/?i=plot+2%5En+from+0+to+10<br />
<br />
''Example:''<br />
<br />
<pre>(define (strange-add x)<br />
(if (zero? x)<br />
1<br />
(+ (strange-add (- x 1))<br />
(strange-add (- x 1)) )))<br />
<br />
def strange_add(x):<br />
if x == 0:<br />
return 1<br />
else:<br />
return strange_add(x - 1) + strange_add(x - 1)</pre><br />
$\mathtt{strange\_add} \in \Theta(2^{n})$, where $n$ is x.<br />
<br />
''Approach:''<br />
<br />
Exponential functions tend to branch out as you get deeper and deeper into their call tree, and each call only makes the work smaller by a little bit. For example, <code>(strange-add 8)</code> calls <code>(strange-add 7)</code> and <code>(strange-add 7)</code>. Those two calls each make two calls, <code>(strange-add 6)</code>, <code>(strange-add 6)</code>, <code>(strange-add 6)</code>, and <code>(strange-add 6)</code> respectively, and so on.<br />
<br />
== Mutability ==<br />
=== Michelle Chang's guide to immutability and mutability ===<br />
[https://d1b10bmlvqabco.cloudfront.net/attach/hoxc5uu6sud761/gozdkhgdUbT/htdlpko411i0/Python__Immutable_vs_Mutable.pdf Source: What You Should Know about Immutability vs Mutability]<br />
<br />
== Mutable data-structures ==<br />
== Object-oriented programming ==<br />
=== Inheritance and class vs instance attributes ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=1413 Source: Spring 2014 Piazza (1413)]<br />
<br />
'''Student Question'''<br />
<br />
I'm confused on how Classes and Inheritance work.<br />
<br />
If there's a Parent class and a Child class, when coding in the Child class, when do you write <code>Parent.attribute</code>, when do you write <code>Child.attribute</code>, and when do you write <code>self.attribute</code>?<br />
<br />
Also, I'm also confused as to when to put <code>self</code> into the parentheses as well.<br />
<br />
'''Instructor Answer'''<br />
<br />
<code>Parent.attribute</code> and <code>Child.attribute</code> would both be ways of accessing a'''class variable'''. These are variables that can be accessed without creating new '''instances''' of the that class.<br />
<br />
<code>self.attribute</code> would be used in '''methods''' to access an '''instance variable''' (an attribute specific to an instance).<br />
<br />
So for example, <code>Insect.watersafe</code> is <code>False</code>, but <code>Bee.watersafe</code> is <code>True</code>. These are class attributes because you don't have to create an Insect object or a Bee object in order to say <code>Insect.watersafe</code> or <code>Bee.watersafe</code>.<br />
<br />
However it wouldn't make any sense to say <code>Bee.armor</code>, since armor is an '''instance variable'''. You have to first create a new Bee before you could ask it for it's armor. If you created a second Bee after that, the second Bee would also have its own armor.<br />
<br />
There's a lot of vocab (in bold) that might trip you up. Try reading Discussion 6 and posting a followup if you're still unsure!<br />
<br />
== Iterables, iterators and generators ==<br />
== Python semantics and syntax ==<br />
=== @property ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=3015 Source: Spring 2014 Piazza (3015)]<br />
<br />
'''Student Question'''<br />
<br />
Is it possible to call <code>@property</code> on methods that have arguments?<br />
<br />
'''Student Answer'''<br />
<br />
<code>@property</code> is used to define "setters" and "getters". It essentially gives you control of what happens when a variable is assigned or retrieved.<br />
<pre>class A:<br />
def __init__(self):<br />
self._x = 5<br />
@property<br />
def x(self):<br />
return self._x<br />
@x.setter<br />
def x(self, value):<br />
print("Hello World")<br />
self._x = value<br />
<br />
&gt;&gt;&gt; a = A()<br />
&gt;&gt;&gt; a.x = 52<br />
"Hello World"</pre><br />
As such, there isn't really room for arguments, since it is treated similar to a variable rather than a function. It is retrieved as "a.x" and set as "a.x = value". <br />
<br />
If you would like to pass arguments when assigning something, it would probably be best to just have them use that method. e.g.<br />
<pre>def set_x(self, value1, value2, value3):<br />
self._x = value1 * value2 * value3</pre><br />
Of course, you won't get the syntactic sugar of "a.x = ", but it gets the job done.<br />
<br />
The cool thing about properties is that you can also place restrictions on a variable. For example, if you leave out the "x.setter" method in the example above, then assigning a.x = 5 would throw an error since it isn't defined (you can still assign a._x though). It would only be available for reading. Sometimes that is a good sign to let any users know to use some method to set it (e.g. set_x) or to not set it at all.<br />
<br />
== Scheme ==<br />
=== Scheme semantics and syntax ===<br />
----<br />
==== Difference between <code>eq?</code>, <code>eqv?</code>, <code>equal?</code> and <code>=</code> ====<br />
[http://stackoverflow.com/questions/16299246/what-is-the-difference-between-eq-eqv-equal-and-in-scheme/17719745#17719745 What is the difference between eq?, eqv?, equal?, and = in Scheme?]<br />
<br />
Let's start with the <code>=</code> equivalence predicate. The <code>=</code> predicate is used to check whether two numbers are equal. If you supply it anything else but a number then it will raise an error:<br />
<br />
<pre>(= 2 3) => #f<br />
(= 2.5 2.5) => #t<br />
(= '() '()) => error</pre><br />
The <code>eq?</code> predicate is used to check whether its two parameters respresent the same object in memory. For example:<br />
<br />
<code>(define x '(2 3))<br />
(define y '(2 3))<br />
(eq? x y) => #f<br />
(define y x)<br />
(eq? x y) => #t</code><br />
Note however that there's only one empty list <code>'()</code> in memory (actually the empty list doesn't exist in memory, but a pointer to the memory location <code>0</code> is considered as the empty list). Hence when comparing empty lists <code>eq?</code> will always return <code>#t</code> (because they represent the same object in memory):<br />
<br />
<pre>(define x '())<br />
(define y '())<br />
(eq? x y) => #t</pre><br />
Now depending upon the implementation <code>eq?</code> may or may not return <code>#t</code> for primitive values such as numbers, strings, etc. For example:<br />
<br />
(<pre>eq? 2 2) => depends upon the implementation<br />
(eq? "a" "a") => depends upon the implementation</pre><br />
This is where the <code>eqv?</code> predicate comes into picture. The <code>eqv?</code> is exactly the same as the <code>eq?</code> predicate, except that it will always return <code>#t</code> for same primitive values. For example:<br />
<br />
<pre>(eqv? 2 2) => #t<br />
(eqv? "a" "a") => #t</pre><br />
Hence <code>eqv?</code> is a superset of <code>eq?</code> and for most cases you should use <code>eqv?</code> instead of <code>eq?</code>.<br />
<br />
Finally we come to the <code>equal?</code> predicate. The <code>equal?</code> predicate is exactly the same as the <code>eqv?</code> predicate, except that it can also be used to test whether two lists, vectors, etc. have corresponding elements which satisfy the <code>eqv?</code> predicate. For example:<br />
<br />
<pre>(define x '(2 3))<br />
(define y '(2 3))<br />
(equal? x y) => #t<br />
(eqv? x y) => #f</pre><br />
In general:<br />
# Use the <code>=</code> predicate when you wish to test whether two numbers are equivalent.<br />
# Use the <code>eqv?</code> predicate when you wish to test whether two non-numeric values are equivalent.<br />
# Use the <code>equal?</code> predicate when you wish to test whether two lists, vectors, etc. are equivalent.<br />
# Don't use the <code>eq?</code> predicate unless you know exactly what you're doing.<br />
<br />
=== Scheme lists ===<br />
----<br />
==== Using <code>cons</code> ====<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=3004 Source: Spring 2014 Piazza (3004)]<br />
<br />
'''Student Question<br />
<br />
What's the difference between the following in Scheme?<br />
<br />
(cons 1 2)<br />
(cons 1 . 2)<br />
<br />
(cons 1 (cons 2 (cons 3 nil)))<br />
(cons 1 . (cons 2 . (cons 3 . 4)))<br />
<br />
Why does putting a dot before "(cons" cause it to be a malformed list? But when you put in (cons 1 2) it returns (1 . 2)? Is the dot something that only the interpreter returns, and that the user can't use in defining a list?<br />
<br />
'''Student Answer'''<br />
<br />
I struggled with this a bit as well. It helps to know the difference between a list containing a "." and one that doesn't. <br />
<br />
First off, scheme lists love to be recursive, kind of like rlists. That is, if you did cdr on the list, you would keep getting a list until you finally get an empty list/nil.<br />
<pre>STk&gt; (define a '(1 2 3))<br />
a<br />
STk&gt; a<br />
(1 2 3)<br />
STk&gt; (cdr a)<br />
(2 3)<br />
STk&gt; (cddr a)<br />
(3)<br />
STk&gt; (cdddr a)<br />
()</pre><br />
Note how each call of cdr returns a list. Even (3) is a list. It is just a list containing one member. So what happens with something like (1 2 . 3)?<br />
<pre>STk&gt; (define a '(1 2 . 3))<br />
a<br />
STk&gt; a<br />
(1 2 . 3)<br />
STk&gt; (cdr a)<br />
(2 . 3)<br />
STk&gt; (cddr a)<br />
3</pre><br />
Notice how the last <code>cdr</code> returns a simple 3. Running <code>cdddr</code> would throw an error, because the list stops at 3. A list containing a "." is known as an <em>improper </em>list.<br />
<br />
As for the ".", you won't be using it unless you use it in combination with a quote, otherwise it will always return a malformed list and throw an error. For example:<br />
<pre>STk&gt; (define a (1 2 . 3))<br />
*** Error:<br />
eval: malformed list: (1 2 . 3)<br />
Current eval stack:<br />
__________________<br />
0 (1 2 . 3)<br />
1 (define a (1 2 . 3))<br />
STk&gt; (define a '(1 2 . 3))<br />
a</pre><br />
Basically, the "." is seen in output, but not input. The only exception is the quote. Think of scheme as having two stages. First, it interprets your commands to construct the lists etc. Next, it will simplify the expression. Think of the quote as skipping straight to the second stage.<br />
<br />
Finally, the only way to get the standard lists is to end the list with nil or to use the "list" function (or to use a quote). If the list doesn't end with nil, then it will become an improper list. That is why you <em>can't</em> do things like <code>(1 . 2 . 3 . 4)</code> to make <code>(1 2 3 4)</code>. In order to form a "proper" list, each element must be represented by a list. You <em>can</em> do <code>(1 . (2 . (3 . (4))))</code> because you are treating each element like a list. When in doubt, just test some output:<br />
<pre>STk&gt; (cons 1 2)<br />
(1 . 2) ; doesn't end in an empty list/nil<br />
STk&gt; (cons 1 (cons 2 '()))<br />
(1 2)<br />
STk&gt; (cons 1 (cons 2 (3)))<br />
; ERROR<br />
STk&gt; (cons 1 (cons 2 (list 3)))<br />
(1 2 3) ; lists are formed with an nil at the end, so this works<br />
STk&gt; '(1 . (2 . (3 . 4)))<br />
(1 2 3 . 4)<br />
STk&gt; '(1 . (2 . (3 . (4)))<br />
(1 2 3 4)<br />
STk&gt; '(1 (2 . 3) 4)<br />
(1 (2 . 3) 4) ; still works, but this has 3 elements: (1), (2 . 3), and (4)<br />
STk&gt; (cons 1 (cons (cons 2 3) (cons 4 nil))) <br />
(1 (2 . 3) 4) ; equivalent to above, without quote</pre><br />
<br />
==== <code>append</code> vs <code>cons</code> vs <code>list</code> ====<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=2067 Source: Spring 2014 Piazza (2067)]<br />
<br />
This post isn't meant to be comprehensive. '''Ask questions in lab or as a followup here if you're confused.'''<br />
One of its major flaws is that it doesn't cover box and pointers. '''LEARN BOX AND POINTERS. '''<br />
<br />
Here is a beautiful web based Scheme interpreter that will draw box and pointer diagrams for you. Run through the examples below with this thing:<br />
http://xuanji.appspot.com/js-scheme-stk/index.html<br />
<br />
In order to understand these three procedures, you first have to understand a little about Pairs and Lists.<br />
<br />
'''Pairs''' are data structures that have two slots. You can put different stuff in these slots, like numbers or words or sentences or booleans--pretty much anything. You make a pair using cons.<br />
<pre>STk> (cons 'foo 'bar)<br />
(foo . bar)<br />
<br />
STk> (cons 1 'ring)<br />
(1 . ring)<br />
<br />
STk> (cons (+ 1 2 3) (member? 3 '(the 3 stooges)))<br />
(6 . #t)</pre><br />
<br />
In order to get stuff from a pair that you have made, you use <code>car</code> and <code>cdr</code>. <code>car</code> gets the thing in the first slot. <code>cdr</code> gets the thing in the second slot.<br />
<br />
<pre>STk> (define foo (cons 'x 'y))<br />
foo<br />
<br />
STk> foo<br />
(x . y)<br />
<br />
STk> (car foo)<br />
x<br />
<br />
STk> (cdr foo)<br />
y</pre><br />
That was straightforward. Now for the trippy part:<br />
'''You can put pairs inside of pairs:'''<br />
<pre>STk> (define foo (cons (cons 3 4) 5))<br />
foo<br />
<br />
STk> foo<br />
((3 . 4) . 5)<br />
<br />
STk> (car foo)<br />
(3 . 4)<br />
<br />
STk> (car (car foo))<br />
3<br />
<br />
STk> (caar foo) ; functionally equivalent as above.<br />
3<br />
<br />
STk> (cdr foo)<br />
5<br />
<br />
STk> (cdr (car foo))<br />
4<br />
<br />
STk> (cdar foo) ; functionally equivalent as above.<br />
4</pre> <br />
<br />
There's a certain style of pair nesting that is especially useful&mdash;'''Lists.'''<br />
<br />
Each list has these properties:<br />
* Every list is a pair or the empty list (denoted by '() or nil).<br />
* The car of a nonempty list is some item.<br />
* The cdr of a nonempty list must be another list.<br />
<pre>STk> (cons 1 (cons 2 (cons 3 '()))) ; list of numbers<br />
(1 2 3)<br />
<br />
STk> (define stooges (cons 'larry (cons 'curly (cons 'moe nil))))<br />
stooges<br />
<br />
STk> stooges<br />
(larry curly moe)<br />
<br />
STk> (car stooges)<br />
larry<br />
<br />
STk> (cdr stooges) ; Calling cdr on a non-empty list gives you another list!<br />
(curly moe)<br />
<br />
STk> (cadr stooges)<br />
curly<br />
<br />
STk> (cdar stooges) ; Why does this break?<br />
*** Error:<br />
cdar: bad list: (moe larry curly)<br />
Current eval stack:<br />
__________________<br />
0 (cdar stooges)<br />
<br />
STk> (define not-a-list (cons 'foo (cons 'bar 'baz))) ; This is not a list.<br />
not-a-list<br />
<br />
STk> not-a-list ; What property does this break?</pre><br />
Notice how Scheme knew that we were making lists. Before we had <code>parens</code> and periods which organized our items. Scheme now recognizes that we're making a list and does away with the periods and some of the <code>parens</code>.<br />
<br />
If you stare a bit at the list rules above, you can notice we used a recursive definition to define lists. Recursion... '''on data'''!<br />
<br />
Let's talk about <code>list</code>. <code>list</code> takes a bunch of stuff and makes a list out of them. The stuff can be anything. Words, numbers, pairs, other lists. <code>list</code> doesn't care. [picture of a honey badger]<br />
<pre>STk> (list 'foo 'bar' 'baz) ; Lists takes anything and makes a list out of it.<br />
(foo bar baz)<br />
<br />
STk> (list 'foo ((lambda (x) (+ x 4)) 8) #f (cons 1 (cons 3 4)) (cons 1 (cons 2 nil)) (list 1 2 3)) ; ANYTHING <br />
(foo 12 #f (1 3 . 4) (1 2) (1 2 3))<br />
<br />
STk> (list 'x 'y 'z)<br />
(x y z)<br />
<br />
STk> '(x y z) ; Sometimes you can get away with using quote to make literal lists. Yes, sentences are secretly lists.<br />
(x y z)</pre><br />
Now we can talk about <code>append</code>:<br />
<pre>STk> (append '(a b c) '(d e f) '(g h i)) ; Append takes in lists and appends them together.<br />
(a b c d e f g h i)<br />
<br />
STk> (append 'foo '(1 2 3)) ; foo is not a list. Stuff will break.<br />
*** Error:<br />
append: argument is not a list: foo<br />
Current eval stack:<br />
__________________<br />
0 (append (quote foo) (quote (1 2 3)))</pre><br />
You know that <code>cons</code> makes a pair. You also know that you can make a list out of pairs. You can abuse <code>cons</code> for your own maniacal purposes.<br />
<pre>STk> (cons 'joe stooges) ; Put stuff at the beginning of a list!<br />
(joe larry curly moe)</pre><br />
'''The following only applies to the STk interpreter.'''<br />
<pre>STk> (append '(1 2 3) 'foo) ; Wait... what?<br />
(1 2 3 . foo)<br />
<br />
STk> (append '(1 2 3) (cons 4 5)) ; The plot thickens!<br />
(1 2 3 4 . 5)<br />
<br />
STk> (append stooges 'shemp) ; You should really figure out why this works.<br />
(larry curly moe . shemp)</pre><br />
<br />
To summarize:<br />
* append takes in lists and outputs a big list.<br />
* cons takes in things and makes a pair out of them. However, we know that lists are made of pairs, so we can throw together a list if we use cons a certain way<br />
* list takes in things and makes a list out of those things, regardless of what they are.<br />
<br />
=== Tail recursion ===<br />
----<br />
==== Mark Miyashita's guide on tail recursion ====<br />
[http://markmiyashita.com/cs61a/sp14/tail_recursion/tail_recursion_and_tail_optimized_calls/ Source: Tail Recursion and Tail Optimized Calls]<br />
<br />
First off, I think this is an excellent article to read about tail recursion and tail calls in Python: [http://paulbutler.org/archives/tail-recursion-in-python/ here]<br />
<br />
Basically, you can write tail recursive functions in any language. Tail recursion, in one sentence, is where you return the answer in the final frame instead of following the frames back up to the original frame. For example, we have factorial which is normally not tail recursive:<br />
<br />
<pre>def factorial(n):<br />
if n == 1:<br />
return 1<br />
return n * factorial(n - 1)</pre><br />
<br />
because it needs to keep track of the <code>n *</code> at each level of recursion.<br />
<br />
The following implementation of <code>factorial</code>, is tail recursive because at the end of the last frame, it can return the answer, instead of going back up through all the frames to multiply and compute the answer:<br />
<br />
<pre>def factorial(n):<br />
def helper(n, total):<br />
if n == 1:<br />
return total<br />
return helper(n - 1, total * n)<br />
return helper(n, 1)</pre><br />
<br />
You can’t have tail optimized calls in Python – at least, not like the code that we defined above. You can define your own sort of tail optimized way of evaluating the functions by using lambdas, and I believe the article linked at the top of this post goes into detail about how to implement this if you are interested. In Scheme, the language detects when you have something like the helper function in the example above where your return statement consists of only the recursive call. In the first example, we have the <code>n *</code> and the recursive call which means it cannot be tail optimized because it needs to keep track of all of the frames that it creates. In a tail optimized call, Scheme will get rid of the frames that are no longer necessary.<br />
<br />
'''tl;dr''' – Tail recursion can be done in any language where the basic idea is that you return the answer in the final frame of recursion. Tail optimized calls are a Scheme (and some other languages, not including Python) feature where it will get rid of the frames above, if certain conditions are met – such as where the return statement is only the recursive call and nothing else. The cases in which Scheme uses a tail optimized call are located on the lecture slides located [http://www-inst.eecs.berkeley.edu/~cs61a/sp13/slides/35-TailCalls_6pp.pdf here].<br />
<br />
==== Tail recursion in Python ====<br />
[http://kylem.net/programming/tailcall.html Source: http://kylem.net/programming/tailcall.html (Retrieved June 16th, 2014)]<br />
<br />
In this page, we’re going to look at tail call recursion and see how to force Python to let us eliminate tail calls by using a trampoline. We will go through two iterations of the design: first to get it to work, and second to try to make the syntax seem reasonable. I would not consider this a useful technique in itself, but I do think it’s a good example which shows off some of the power of decorators.<br />
<br />
The first thing we should be clear about is the definition of a tail call. The “call” part means that we are considering function calls, and the “tail” part means that, of those, we are considering calls which are the last thing a function does before it returns. In the following example, the recursive call to f is a tail call (the use of the variable <code>ret</code> is immaterial because it just connects the result of the call to <code>f</code> to the return statement), and the call to <code>g</code> is not a tail call because the operation of adding one is done after <code>g</code> returns (so it’s not in “tail position”).<br />
<br />
<pre>def f(n) :<br />
if n > 0 :<br />
n -= 1<br />
ret = f(n)<br />
return ret<br />
else :<br />
ret = g(n)<br />
return ret + 1</pre><br />
<br />
'''1. Why tail calls matter'''<br />
<br />
Recursive tail calls can be replaced by jumps. This is called “tail call eliminination,” and is a transformation that can help limit the maximum stack depth used by a recursive function, with the benefit of reducing memory traffic by not having to allocate stack frames. Sometimes, recursive function which wouldn’t ordinarily be able to run due to stack overflow are transformed into function which can.<br />
<br />
Because of the benefits, some compilers (like <code>gcc</code>) perform tail call elimination[1], replacing recursive tail calls with jumps (and, depending on the language and circumstances, tail calls to other functions can sometimes be replaced with stack massaging and a jump). In the following example, we will eliminate the tail calls in a piece of code which does a binary search. It has two recursive tail calls.<br />
<br />
<pre>def binary_search(x, lst, low=None, high=None) :<br />
if low == None : low = 0<br />
if high == None : high = len(lst)-1<br />
mid = low + (high - low) // 2<br />
if low > high :<br />
return None<br />
elif lst[mid] == x :<br />
return mid<br />
elif lst[mid] > x :<br />
return binary_search(x, lst, low, mid-1)<br />
else :<br />
return binary_search(x, lst, mid+1, high)</pre><br />
<br />
Supposing Python had a <code>goto</code> statement, we could replace the tail calls with a jump to the beginning of the function, modifying the arguments at the call sites appropriately:<br />
<br />
<pre>def binary_search(x, lst, low=None, high=None) :<br />
start:<br />
if low == None : low = 0<br />
if high == None : high = len(lst)-1<br />
mid = low + (high - low) // 2<br />
if low > high :<br />
return None<br />
elif lst[mid] == x :<br />
return mid<br />
elif lst[mid] > x :<br />
(x, lst, low, high) = (x, lst, low, mid-1)<br />
goto start<br />
else :<br />
(x, lst, low, high) = (x, lst, mid+1, high)<br />
goto start</pre><br />
<br />
which, one can observe, can be written in actual Python as<br />
<br />
<pre>def binary_search(x, lst, low=None, high=None) :<br />
if low == None : low = 0<br />
if high == None : high = len(lst)-1<br />
while True :<br />
mid = low + (high - low) // 2<br />
if low > high :<br />
return None<br />
elif lst[mid] == x :<br />
return mid<br />
elif lst[mid] > x :<br />
high = mid - 1<br />
else :<br />
low = mid + 1</pre><br />
<br />
I haven’t tested the speed difference between this iterative version and the original recursive version, but I would expect it to be quite a bit faster because of there being much, much less memory traffic.<br />
<br />
Unfortunately, the transformation makes it harder to prove the binary search is correct in the resulting code. With the original recursive algorithm, it is almost trivial by induction.<br />
<br />
Programming languages like Scheme depend on tail calls being eliminated for control flow, and it’s also necessary for continuation passing style.[2]<br />
<br />
'''2. A first attempt'''<br />
<br />
Our running example is going to be the factorial function (a classic), written with an accumulator argument so that its recursive call is a tail call:<br />
<br />
<pre>def fact(n, r=1) :<br />
if n <= 1 :<br />
return r<br />
else :<br />
return fact(n-1, n*r)</pre><br />
<br />
If <code>n</code> is too large, then this recursive function will overflow the stack, despite the fact that Python can deal with really big integers. On my machine, it can compute <code>fact(999)</code>, but <code>fact(1000)</code> results in a sad <code>RuntimeError: Maximum recursion depth exceeded</code>.<br />
<br />
One solution is to modify fact to return objects which represent tail calls and then to build a trampoline underneath fact which executes these tail calls after fact returns. This way, the stack depth will only contain two stack frames: one for the trampoline and another for each call to fact.<br />
<br />
First, we define a tail call object which reifies the concept of a tail call:<br />
<br />
<pre>class TailCall(object) :<br />
def __init__(self, call, *args, **kwargs) :<br />
self.call = call<br />
self.args = args<br />
self.kwargs = kwargs<br />
def handle(self) :<br />
return self.call(*self.args, **self.kwargs)</pre><br />
<br />
This is basically just the thunk <code>lambda : call(*args, **kwargs)</code>, but we don’t use a thunk because we would like to be able to differentiate between a tail call and returning a function as a value.<br />
<br />
The next ingredient is a function which wraps a trampoline around an arbitrary function:<br />
<br />
<pre>def t(f) :<br />
def _f(*args, **kwargs) :<br />
ret = f(*args, **kwargs)<br />
while type(ret) is TailCall :<br />
ret = ret.handle()<br />
return ret<br />
return _f</pre><br />
<br />
Then, we modify fact to be<br />
<br />
<pre>def fact(n, r=1) :<br />
if n <= 1 :<br />
return r<br />
else :<br />
return TailCall(fact, n-1, n*r)</pre><br />
<br />
Now, instead of calling <code>fact(n)</code>, we must instead invoke <code>t(fact)(n)</code> (otherwise we’d just get a TailCall object).<br />
<br />
This isn’t that bad: we can get tail calls of arbitrary depth, and it’s Pythonic in the sense that the user must explicitly label the tail calls, limiting the amount of unexpected magic. But, can we eliminate the need to wrap t around the initial call? I myself find it unclean to have to write that <code>t</code> because it makes calling fact different from calling a normal function (which is how it was before the transformation).<br />
<br />
'''3. A second attempt'''<br />
<br />
The basic idea is that we will redefine fact to roughly be <code>t(fact)</code>. It’s tempting to just use <code>t</code> as a decorator:<br />
<br />
<pre>@t<br />
def fact(n, r=1) :<br />
if n <= 1 :<br />
return r<br />
else :<br />
return TailCall(fact, n-1, n*r)</pre><br />
<br />
(which, if you aren’t familiar with decorator syntax, is equivalent to writing <code>fact = t(fact)</code> right after the function definition). However, there is a problem with this in that the fact in the returned tail call is bound to <code>t(fact)</code>, so the trampoline will recursively call the trampoline, completely defeating the purpose of our work. In fact, the situation is now worse than before: on my machine, <code>fact(333)</code> causes a <code>RuntimeError</code>!<br />
<br />
For this solution, the first ingredient is the following class, which defines the trampoline as before, but wraps it in a new type so we can distinguish a trampolined function from a plain old function:<br />
<br />
<pre>class TailCaller(object) :<br />
def __init__(self, f) :<br />
self.f = f<br />
def __call__(self, *args, **kwargs) :<br />
ret = self.f(*args, **kwargs)<br />
while type(ret) is TailCall :<br />
ret = ret.handle()<br />
return ret</pre><br />
<br />
and then we modify <code>TailCall</code> to be aware of <code>TailCallers</code>:<br />
<br />
<pre>class TailCall(object) :<br />
def __init__(self, call, *args, **kwargs) :<br />
self.call = call<br />
self.args = args<br />
self.kwargs = kwargs<br />
def handle(self) :<br />
if type(self.call) is TailCaller :<br />
return self.call.f(*self.args, **self.kwargs)<br />
else :<br />
return self.call(*self.args, **self.kwargs)</pre><br />
<br />
Since classes are function-like and return their constructed object, we can just decorate our factorial function with <code>TailCaller</code>:<br />
<br />
<pre>@TailCaller<br />
def fact(n, r=1) :<br />
if n <= 1 :<br />
return r<br />
else :<br />
return TailCall(fact, n-1, n*r)</pre><br />
<br />
And then we can call fact directly with large numbers!<br />
<br />
Also, unlike in the first attempt, we can now have mutually recursive functions which all perform tail calls. The first-called <code>TailCall</code> object will handle all the trampolining.<br />
<br />
If we wanted, we could also define the following function to make the argument lists for tail calls be more consistent with those for normal function calls:[3]<br />
<br />
<pre>def tailcall(f) :<br />
def _f(*args, **kwargs) :<br />
return TailCall(f, *args, **kwargs)<br />
return _f</pre><br />
<br />
and then fact could be rewritten as<br />
<br />
<pre>@TailCaller<br />
def fact(n, r=1) :<br />
if n <= 1 :<br />
return r<br />
else :<br />
return tailcall(fact)(n-1, n*r)</pre><br />
<br />
One would hope that marking the tail calls manually could just be done away with, but I can’t think of any way to detect whether a call is a tail call without inspecting the source code. Perhaps an idea for further work is to convince Guido von Rossum that Python should support tail recursion (which is quite unlikely to happen).<br />
<br />
[1] This is compiler-writer speak. For some reason, “elimination” is what you do when you replace a computation with something equivalent. In this case, it’s true that the call is being eliminated, but in its place there’s a jump. The same is true for “common subexpression elimination” (known as CSE), which takes, for instance,<br />
<br />
<pre>a = b + c<br />
d = (b + c) + e<br />
and replaces it with<br />
a = b + c<br />
d = a + e</pre><br />
<br />
Sure, the <code>b+c</code> is eliminated from the second statement, but it’s not really gone...<br />
The optimization known as “dead code elimination” actually eliminates something, but that’s because dead code has no effect, and so it can be removed (that is, be replaced with nothing).<br />
<br />
[2] In Scheme, all loops are written as recursive functions since tail calls are the pure way of redefining variables (this is the same technique Haskell uses). For instance, to print the numbers from 1 to 100, you’d write<br />
<br />
<pre>(let next ((n 1))<br />
(if (<= n 100)<br />
(begin<br />
(display n)<br />
(newline)<br />
(next (+ n 1)))))</pre><br />
<br />
where next is bound to be a one-argument function which takes one argument, <code>n</code>, and which has the body of the <code>let</code> statement as its body. If that <code>100</code> were some arbitrarily large number, the tail call to next had better be handled as a jump, otherwise the stack would overflow! And there’s no other reasonable way to write such a loop!<br />
<br />
Continuation passing style is commonly used to handle exceptions and backtracking. You write functions of the form<br />
<br />
<pre>(define (f cont)<br />
(let ((cont2 (lambda ... (cont ...) ...)))<br />
(g cont2)))</pre><br />
<br />
along with functions which take multiple such f’s and combines them into another function which also takes a single cont argument. I’ll probably talk about this more in another page, but for now notice how the call to g is in the tail position.<br />
<br />
[3] This is basically a curried[4] version of <code>TailCall</code>.<br />
<br />
[4] That is, ''Schönfinkelized''.<br />
<br />
=== Miscellaneous ===<br />
----<br />
==== Useful Scheme Procedures ====<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=2226 Source: Spring 2014 Piazza (2226)]<br />
<br />
Here is a short list of Scheme procedures that you might use in writing your programs:<br />
<pre>; define - defines a variable or a procedure<br />
(define my-variable 4)<br />
(define (square x)<br />
(* x x))<br />
<br />
; if - conditional branching akin to if ... else<br />
(define (fib n)<br />
(if (< n 2)<br />
n<br />
(+ (fib (- n 1)) (fib (- n 2))) ))<br />
<br />
; conditional branching akin to if ... elif ... elif ... else<br />
(define (deep-map f lst)<br />
(cond ((null? lst) lst)<br />
((list? (car lst))<br />
(cons (deep-map f (car lst))<br />
(deep-map f (cdr lst))))<br />
(else<br />
(cons (f (car lst))<br />
(deep-map f (cdr lst)))) ))<br />
<br />
; and - outputs the the rightmost value if all of the arguments evaluate to #t. Outputs #f otherwise.<br />
STk> (and 0 1 2 3)<br />
3<br />
<br />
; or - outputs the the first value that evaluates to #t. Outputs #f otherwise.<br />
STk> (or 0 1 2 3)<br />
0<br />
<br />
; equal - tests if symbols are the same<br />
STk> (equal? 'foo 'bar)<br />
#f<br />
STk> (equal? 'foo 'foo)<br />
#t<br />
<br />
STk> (list? 'foo)<br />
#f<br />
STk> (list? '(1 2 3))<br />
#t<br />
STk> (list? '())<br />
#t<br />
<br />
STk> (null? '(1 2 3))<br />
#f<br />
STk> (null? ())<br />
#t<br />
<br />
; member? - tests if a symbol is in a list EDIT: NOT BUILT IN (BUT SUPER USEFUL SEE FOLLOWUP)<br />
STk> (member? 'quick '(the quick brown fox jumped over the lazy dog))<br />
#t<br />
<br />
; number? - checks if input is a number<br />
STk> (number? 42)<br />
#t<br />
STk> (number? #t)<br />
#f<br />
<br />
; remainder - computes the remainder of the first number divided by the second<br />
STk> (remainder 100 21)<br />
16</pre><br />
<br />
== Streams ==<br />
== Logic ==<br />
=== Quick Guide to Logic Programming ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=2524 Source: Spring 2014 Piazza (2524)]<br />
''Note: Someone should convert this from scmlog to Logic notation''<br />
<br />
'''Here's something I wrote a long time ago. The logic interpreter scmlog still exists and you should be able to access it using your cs61a-xx account. Post a followup if you have any questions. Hope this helps!'''<br />
<br />
<br />
'''Introduction:''' logic programming is a completely different way to think about telling computers to do stuff. Instead of telling the computer what to compute, you give the computer facts and ask it questions. The computer does its own thinking given the facts and the question, and then returns an answer.<br />
<br />
'''Table of Contents'''<br />
- Introduction<br />
- What is Logic Programming<br />
- - Giving Facts and Asking Questions<br />
- - More Complicated Facts and Questions<br />
- How to write Logic Programs<br />
- - Common Pitfalls<br />
- More Resources<br />
<br />
<br />
'''What is Logic Programming?'''<br />
Logic Programming is a way to ask the computer questions and get answers without telling it explicitly how to reach the conclusion. Kind of like this: http://youtu.be/tpKx7Oi0oeM<br />
<br />
Anyways, we have a logic programming interpreter called Scmlog. It interprets a simpler version of the logic programming language, Prolog. The first few things to understand about it is that '''SCMLOG IS NOT SCHEME'''. It just happens to look like it. Scmlog is its own language and so it has its own rules. You can't program in Scmlog like you would in Scheme or Python, so it might be good to forget what you know about those languages for a second.<br />
<br />
When you fire up scmlog (type scmlog into a terminal on the school computers), you get this prompt:<br />
<pre>star [501] ~ # scmlog<br />
scmlog (Prolog in Scheme), v. 0.2<br />
Type 'help' for help; 'quit' to exit.<br />
?- </pre><br />
You can interact with this prompt in two ways:<br />
# Give facts - "Let's tell the computer some things it should know!"<br />
# Ask questions - "Let's ask the computer questions about the things we told it about!"<br />
<br />
'''Giving Facts and Asking Questions'''<br />
A basic fact takes this form: <code>(fact (assertion))</code>, '''Where each assertion is simply a relation between things.'''<br />
<br />
For example: <code>(fact (likes potstickers brian))</code> relates three ideas, liking something, potstickers, and some guy name Brian. "Brian likes potstickers."<br />
Note that we put the relation first, and then the parties the relation acts upon. In this example, "likes" is the relation, it hooks up "brian" and "potstickers". Here's some more:<br />
<pre>?- (fact (likes potstickers brian)) <br />
?- (fact (likes potstickers andrew)) <br />
?- (fact (likes the_beatles brian)) <br />
?- (fact (likes the_beatles andrew)) <br />
?- (fact (likes led_zeppelin andrew)) <br />
?- (fact (dislikes led_zeppelin brian))</pre><br />
Now that we've given the computer a bunch of facts, how do we ask questions about them? Just replace "fact" with a "?", and replace any part of the relation (besides the relation itself) with a variable prefixed by an underscore. This is called "querying":<br />
<pre>?- (? (likes _what brian))<br />
_what : potstickers<br />
More? <br />
_what : the_beatles<br />
More? <br />
?- (? (likes potstickers _who))<br />
_who : brian<br />
More? <br />
_who : andrew<br />
More? <br />
?- (? (dislikes _what brian))<br />
_what : led_zeppelin<br />
More?<br />
?- (? (likes led_zeppelin andrew))<br />
Yes.<br />
?- (? (dislikes the_beatles _who))<br />
No.</pre><br />
Notice:<br />
* We can query any part of the assertion, besides the relation itself (can't replace "likes" with a variable).<br />
* All possible answers to the question show up.<br />
* If Scmlog couldn't find a fact that matched your query, it'll say "No."<br />
* Asking a question without any variables essentially asks if that fact exists. To which Scmlog will answer "Yes." or "No."<br />
<br />
'''More Complicated Facts and Questions'''<br />
Now this isn't the whole picture. We also have the ability to make more powerful assertions via variables, hypotheses, and conclusions:<br />
<br />
<code>(fact (ancestor _x _y) (parent _x _y))</code> "X is an ancestor of Y if X is a parent of Y"<br />
<br />
Here _x and _y are variables like usual. However, we have two parts to this fact, the conclusion ("X is an ancestor of Y") and the hypothesis ("X is a parent of Y"). We can have more than one hypotheses, and they can be any kind of query. Here's ancestor in action:<br />
<pre>?- (fact (parent george paul))<br />
?- (fact (parent martin george))<br />
?- (fact (parent martin martin_jr))<br />
?- (fact (parent martin donald))<br />
?- (fact (parent george ann))<br />
?- (fact (ancestor _X _Y) (parent _X _Y))<br />
?- (fact (ancestor _X _Y) (parent _X _Z) (ancestor _Z _Y))<br />
?- (? (ancestor paul george))<br />
No.<br />
?- (? (ancestor george paul))<br />
Yes.<br />
?- (? (ancestor george george))<br />
No.<br />
?- (? (ancestor martin paul))<br />
Yes.</pre><br />
Now there's one more thing you need to know about Scmlog. Scmlog knows about pairs and lists:<br />
<pre>?- (fact (lst (1 2 3)))<br />
?- (? (lst _x))<br />
_x : (1 2 3)<br />
More?<br />
?- (? (lst (1 . _x)))<br />
_x : (2 3)<br />
More? <br />
?- (? (lst (1 2 . _x)))<br />
_x : (3)<br />
More? <br />
?- (? (lst (_x . _y)))<br />
_x : 1<br />
_y : (2 3)<br />
?- (fact (my_pair (2 . 3)))<br />
?- (? (my_pair _y))<br />
_y : (2 . 3)<br />
?- (? (my_pair (2 . _x)))<br />
_x : 3</pre><br />
<br />
'''How to write Logic Programs'''<br />
<br />
The trick behind writing logic programs is to forget everything you know about programming. You instead want to focus on the relation you're trying to establish. Take append for example:<br />
<pre>?- (fact (append () _b _b))<br />
?- (fact (append (_x . _rest) _b (_x . _z)) (append _rest _b _z))<br />
?- (? (append (1 2 3) (3 2 1) _answer))<br />
_answer : (1 2 3 3 2 1)<br />
?- (append (1 2) 3 (1 2 3))<br />
Huh?<br />
?- (? (append (1 2) 3 (1 2 3))<br />
)<br />
No.<br />
?- (? (append (1 2) (3) (1 2 3))<br />
)<br />
Yes.<br />
?- (? (append (1 2) (3) (1 2 3)))<br />
Yes.<br />
?- (? (append _x (3 4 9) (1 0 3 2 3 4 9)))<br />
_x : (1 0 3 2)<br />
More? <br />
?- (? (append _x _y (2 1)))<br />
_x : ()<br />
_y : (2 1)<br />
More? <br />
_x : (2)<br />
_y : (1)<br />
More? <br />
_x : (2 1)<br />
_y : ()</pre><br />
<br />
What is append? Well the first fact establishes that if we append an empty list to something, the result is that something.<br />
The next fact is the meat of the code. You can almost think of this as a recursive relation. Any append relation between some three lists a b c must also fulfill another append relation between the rest of a, b, and the rest of c, respectively. Think about why that will always be true for all inputs. If the example above doesn't make sense to you, see the following:<br />
<br />
<br />
'''Common Pitfalls'''<br />
<br />
'''- One of the biggest issues people have with writing logic programs is that they don't realize that the variables don't work like they do in Scheme or Python. All Scmlog ever does is pattern match:'''<br />
<pre>?- (fact (x (0 1 8)))<br />
?- (fact (x (1 8 4)))<br />
?- (fact (x (9 4 4)))<br />
?- (fact (x (3 0 8)))<br />
?- (? (x _y))<br />
_y : (0 1 8)<br />
More? <br />
_y : (1 8 4)<br />
More? <br />
_y : (9 4 4)<br />
More? <br />
_y : (3 0 8)<br />
?- (? (x (0 1 . _z)))<br />
_z : (8)</pre><br />
<br />
- SCMLOG IS NOT SCHEME. SCMLOG IS NOT PYTHON.<br />
<br />
More Resources:<br />
* http://www-inst.eecs.berkeley.edu/~cs61a/sp12/lectures/prolog/ReadMe<br />
* http://www-inst.eecs.berkeley.edu/~cs61a/sp12/lectures/prolog/<br />
* http://www-inst.eecs.berkeley.edu/~cs61a/sp12/discussion/week14/<br />
* http://www-inst.eecs.berkeley.edu/~cs61a/fa13/slides/31-Logic_6pp.pdf<br />
<br />
=== Logic Mathematics ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=3050 Source: Spring 2014 Piazza (3050)]<br />
<br />
'''Student Question'''<br />
<br />
Can someone explain the intuition behind the implementation of the increments and the addition facts? The following is from Mark's website<br />
<br />
<pre>(fact (increment 0 1))<br />
(fact (increment 1 2))<br />
(fact (increment 2 3))<br />
(fact (increment 3 4))<br />
(fact (increment 4 5))<br />
(fact (increment 5 6))<br />
(fact (increment 6 7))<br />
(fact (increment 7 8))<br />
(fact (increment 8 9))<br />
(fact (increment 9 10))<br />
(fact (increment 10 11))<br />
(fact (increment 11 12))<br />
(fact (increment 12 13))<br />
<br />
(fact (add 1 ?x ?x+1)<br />
(increment ?x ?x+1))<br />
<br />
(fact (add ?x+1 ?y ?z+1)<br />
(increment ?x ?x+1)<br />
(increment ?z ?z+1)<br />
(add ?x ?y ?z))<br />
<br />
(query (add 2 4 6))<br />
; expect Success!</pre><br />
<br />
'''Student Answer'''<br />
<br />
First, we state a bunch of facts that denote relations between a number and the number that follows it (e.g. 1 and 2, 2 and 3, and so on). This is the increment fact.<br />
<br />
Then, we state a "base" fact, which is:<br />
<pre>(fact (add 1 ?x ?x+1)<br />
(increment ?x ?x+1))</pre><br />
In English, this fact states that some value <code>?x</code> added to 1 will give us some value <code>?x+1</code> (<code>x+1</code> is a valid variable name in Logic) '''if and only if''' the fact (<code>increment ?x ?x+1</code>) is true. As an example, <code>(add 1 2 3)</code> is true '''if and only if''' (<code>increment 2 3</code>) is true. This is true because of the increment facts we stated before.<br />
<br />
Now, to deal with additions that aren't just the sum of two numbers in which one number is a 1, we need:<br />
<pre>(fact (add ?x+1 ?y ?z+1)<br />
(increment ?x ?x+1)<br />
(increment ?z ?z+1)<br />
(add ?x ?y ?z))</pre><br />
I will try to give an intuition as to what is happening here.<br />
<br />
We state our fact: that two numbers, <code>?x+1</code> and <code>?y</code>, will add up to some number <code>?z+1</code> if and only if the following 3 hypotheses are true:<br />
* (<code>increment ?x ?x+1</code>) - "There exists some number <code>?x</code> that is 1 less than <code>?x+1</code>, and"<br />
* (<code>increment ?z ?z+1</code>) - "There exists some number <code>?z</code> that is 1 less than <code>?z+1</code>, and"<br />
* (<code>add ?x ?y ?z</code>) - "The numbers <code>?x</code> and <code>?y</code> will add up to <code>?z</code>."<br />
<br />
Consider the example of:<br />
<pre>(query (add 2 4 6))</pre><br />
Here's an idea of what's happening when Logic tries to match the query with the facts you've stated.<br />
* ?x+1 = 2, ?y = 4, ?z+1 = 6<br />
* It finds a match for <code>?x = 1</code>, since (<code>increment ?x ?x+1</code>) gives <code>?x = 1</code> because <code>?x+1 = 2</code><br />
* It finds a match for <code>?z = 5</code>, since (<code>increment ?z ?z+1</code>) gives <code>?z = 5</code> because <code>?z+1 = 6</code><br />
* It then checks for a match for (<code>add ?x ?y ?z</code>) which in this case is (<code>add 1 4 5</code>). This goes to our first "base" fact for add. (<code>add 1 4 5</code>) is a success because (<code>increment 4 5</code>) is a true fact [refer again to the "base fact" to see why this is the case]. This is also where we would get a "Failed.", if it turns out that (<code>increment ?x ?x+1</code>) wasn't actually true!<br />
* Hence, all 3 of our hypotheses are true, and so (<code>query (add 2 4 6)</code>) is a success!<br />
<br />
In this example, we only have to recurse once to get to our "base" fact. In other examples, where <code>?x+1</code> is not 2, but some number greater, such as 5, we will have to recurse 4 whole times to get to 1, at which point our "base" fact is reached. <br />
<br />
This recursion is similar to this idea in mathematical equations:<br />
<pre>x + y = z</pre><br />
is the same as<br />
<pre>(x - 1) + y = (z - 1)</pre><br />
is the same as<br />
<br />
<pre>(x - 2) + y = (z - 2)</pre?<br />
and so on... In Logic, we stop when we find that the first term (x) is 1, and then we use our increment facts to determine if the original statement is true, because all of these equations are equivalent.<br />
<br />
== Python syntax and semantics ==<br />
=== <code>print</code> vs <code>return</code> ===<br />
----<br />
==== Andrew's tips ====<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=779 Source: Spring 2014 Piazza (779)]<br />
<br />
Remember the differences between return and print.<br />
* <code>return</code> can only be used in a <code>def</code> statement. It returns a value from a function. Once Python evaluates a <code>return</code> statement, it immediately exits the function.<br />
* <code>print</code> is a function that displays its argument on the screen. It always returns <code>None</code>.<br />
<br />
Examples:<br />
<pre>def foo1(x):<br />
return x<br />
<br />
def foo2(x):<br />
print(x)<br />
<br />
>>> foo2(1) # In foo2, we print 1 ourselves using the print function<br />
1<br />
>>> foo1(1) # HERE, THE PYTHON INTERPRETER PRINTS THE RETURN VALUE OF FOO1. CANNOT STRESS HOW IMPORTANT TO UNDERSTAND THIS<br />
1<br />
>>> foo1(1) + 1<br />
2<br />
>>> foo2(1) + 1<br />
1<br />
Traceback (most recent call last):<br />
File "<stdin>", line 1, in <module><br />
TypeError: unsupported operand type(s) for +: 'NoneType' and 'int'</pre><br />
<br />
=== Function decorators ===<br />
<br />
----<br />
<br />
==== How function decorators work ====<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=638 Source: Spring 2014 Piazza (638)]<br />
<br />
'''Student Question'''<br />
<br />
I'm having difficulties understanding what exactly a function decorator is. Can someone elaborate and potentially provide me with an example other than the one in the readings?<br />
<br />
'''Instructor Answer'''<br />
<br />
So imagine you wanted your functions to print their arguments before they executed them. Here's one way to do this.<br />
<pre>def loud(fn):<br />
def new_fn(*args):<br />
print(args)<br />
return fn(*args)<br />
return new_fn </pre><br />
Here's a function loud that takes in a function and returns a new function that when called, prints out its arguments, and then does what the old function does.<br />
<br />
For example:<br />
<pre>def sq(x):<br />
return x * x<br />
>>> sq(4)<br />
16<br />
>>> sq = loud(sq) # replace the old square with our loud one.<br />
>>> sq(4)<br />
(4,)<br />
16</pre><br />
A function decorator does the same thing as the above. Assuming loud is defined, we can do this:<br />
<pre>@loud<br />
def sq(x):<br />
return x * x<br />
<br />
>>> sq(4)<br />
(4,)</pre><br />
<br />
== Student guides ==<br />
=== How to learn computer science ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=241 Source: Spring 2014 Piazza (241)]<br />
<br />
If you've never programmed before, or if you've never taken a class quite like 61A before, things right now might be scary. Everything is strange and new and there quite a lot to take in all at once. So if you're having a hard time so far, here are a few articles that might help.<br />
<br />
Note: these articles are pretty long, so feel free to read them in multiple sittings.<br />
<br />
'''At the beginning, everything seems a bit scary in CS'''. Michelle Bu, a Berkeley alum and a crazy good hacker, shares one of her experiences when she was a wee n00b in [http://blog.michellebu.com/2013/03/21-nested-callbacks/ 21 Nested Callbacks].<br />
<br />
'''Start here!''' [http://www.jamesmaa.com/2013/08/26/a-beginners-guide-to-computer-science/ "A Beginner's Guide to Computer Science"] Written by Berkeley's own James Maa. James is known for his killer walkthroughs (check out his Productivity guide). This article gives you some background on learning CS and then provides a practical guide on how to learn effectively.<br />
<br />
'''How do we learn?''' Mark Eichenlaub explains in this [http://www.quora.com/Learning/Do-grad-school-students-remember-everything-they-were-taught-in-college-all-the-time/answer/Mark-Eichenlaub Introduction to Learning Theory]. This is quite possibly the best introduction to Learning Theory.<br />
<br />
'''Sometimes, you're stuck and you end up really, really frustrated.''' Marcus Geduld explains [http://www.quora.com/Why-do-we-get-frustrated-when-learning-something/answer/Marcus-Geduld Why do we get frustrated when learning something?]<br />
<br />
=== Quick guide on getting unstuck ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=1264 Source: Quick Guide on Getting Unstuck (Retrieved June 16th, 2014)]<br />
<br />
A major frustration you might encounter in 61A is when you stare at a homework problem and have no idea where to start. Or you write some code and it doesn't pass the doctests, but now what? You work at it for a while, but next thing you know, you've been stuck for hours on the the same problem and have little to show about it.<br />
<br />
So here's a checklist of things you can do when you're stuck. Experienced programmers do these things almost naturally (because of how much practice they've had being stuck), and so while they get stuck just as much as your or I, they always know what to do next.<br />
<br />
# Do I understand what the problem is asking?<br />
## If not, which part of the problem is confusing me?<br />
### Identify the exact sentences/phrases/words/etc.<br />
## Check the given examples. Do they make sense to me?<br />
## Can I come up with my own examples? '''A good indicator that you understand the question is that you can come up with some nontrivial examples of how the function works.'''<br />
# What concepts should I use here?<br />
## Do I understand the concepts? '''Can I explain the concept in English to one of my friends such that they get it?'''<br />
### If not, go back and relearn the specific concepts that are unclear (through discussion, lab, lecture, etc.) Don't read the entire book in order to solve one problem..<br />
## How do I apply the concept to the given problem?<br />
# Write your code and test it.<br />
## Use doctests, '''BUT ALSO LOAD IT INTERACTIVELY (python3 -i ...)'''<br />
### '''Saying "my function works because the doctests pass" is a lot like saying "this airplane will fly because it has wings."'''<br />
## If your code breaks, ask yourself:<br />
### Does it error? Is it a....<br />
#### Syntax error? If so, find the syntax bug and fix it.<br />
#### Logic error? Is it something weird that you don't understand? (E.g. cannot add integer and tuple)<br />
### Why did it do that? Why didn't it do what I expected? Trace through the code by hand with an example (sample values) you came up with in step 0. '''Add calls to <code>print</code> in order to figure out how your function is handling the arguments.'''<br />
# Am I missing a trick?<br />
## Oftentimes you've never seen this type of problem before. This is expected on homework (and this is why homework can take a long time) because if you see it on the homework, then you will be familiar with it on the exam and when you program for fun and profit. <br />
## The key here is just to learn the trick however you need to.<br />
### Stare at it yourself<br />
### Stare at it with others (peers in the class)<br />
### Ask on PIazza what the approach is.<br />
### Stare at it with the TAs/lab Assistants<br />
## '''Once you figure it out, remember the trick so that you can use it next time.'''<br />
# At any point you identify what you're stuck on, you can begin to resolve it.<br />
## Use the tips above. Try things out on the interpreter. Review the lecture/discussion/labs/etc. Do whatever helps you get a better understanding of the problem.<br />
## Once you have something specific that you're stuck on, you can ask other people in the class.<br />
### '''Don't be afraid to ask. Everyone gets stuck and feels stupid sometimes. However, you get to choose how you react to it.'''<br />
### '''At the same time, it really helps to work with people who are on about the same level in the course.'''<br />
## Look on Piazza. Ask questions if yours hasn't come up yet. Be that awesome guy/girl who helps answer questions.<br />
## You can ask the TA if all else fails. We are here to help you learn!<br />
<br />
Here is an old algorithm for studying for tests (the final in this case), salvaged from the sands of time:<br />
<pre>For each topic on the final, find problems on them and do them.<br />
If you can solve them on your own, move on.<br />
Else if you are stuck, look at the solution and figure out if you<br />
are missing a trick or if you do not understand the concepts.<br />
If the problem is that you are stuck on some random trick,<br />
just learn the trick.<br />
Stare at the solutions, ask Piazza, your TA, etc.<br />
Questions you should ask at this stage:<br />
What is the problem asking me to do?<br />
How was I suppose to follow the instructions<br />
to solve the problem?<br />
What part of the problem do I not understand?<br />
What is the fastest way to clear up that misunderstanding?<br />
Then if you think you are still stuck conceptually, review<br />
and learn the concept, however you learn best.<br />
Suggestions for picking up concepts quickly (~1-2 hours):<br />
Discussion notes typically have a very concise recap of the<br />
thing they are going over.<br />
There are guides for particularly tricky things on Piazza,<br />
like Logic, Pairs and Lists in Scheme, etc.<br />
Find them and go over them.<br />
Ask a TA: "what is the best way to learn X?"<br />
If these do not work and you are still shaky after an hour<br />
or two, it might be worth watching a lecture or reading<br />
the notes.</pre><br />
<br />
== Composition ==<br />
=== General style guidelines from 61A website ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=149 Source: Spring 2014 Piazza (149)]<br />
<br />
'''Student Question'''<br />
<br />
Are we required to add any comments to our code to say what a function does, etc.? And does clarity of code count for this project, in which case should we write comments at the end of not-so-clear statements? Thanks.<br />
<br />
'''Student Answer'''<br />
<br />
Docstrings of each function are already provided. If you add a helper function, you should write a docstring for it.<br />
<br />
The [http://inst.eecs.berkeley.edu/~cs61a/sp14/style_guide.html#comments style guide on the course website] advises: "Your actual code should be self-documenting -- try to make it as obvious as possible what you are doing without resorting to comments. Only use comments if something is not obvious or needs to be explicitly emphasized"<br />
<br />
'''Instructor Answer'''<br />
<br />
You should always aim to make your code "self-documenting," meaning it is clear what your code is doing without the aid of comments. You should try to keep the number of comments to a minimum, but if there are lines which you think are unclear/ambiguous, feel free to add a comment.<br />
<br />
All projects in this class contain a 3 point component that is judged solely on your code "composition" -- i.e. whether your code is clear, concise, and easy to read.<br />
<br />
=== Simplifying code ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=1116 Source: Spring 2014 Piazza (1116)]<br />
<br />
Hi everyone, here's some tips about certain functions in Python that can greatly simplify your code for the Trends project.<br />
<br />
'''Sorting keys'''<br />
<br />
You should be familiar with the max and min functions in python, which can take in many arguments and return the maximum value.<br />
<br />
<pre>>>> max(1,3,2)<br />
3</pre><br />
<br />
These functions can also take in lists:<br />
<br />
<pre>>>> min([1,5,1,6])<br />
1</pre><br />
<br />
(In fact they can take in any ''iterable'' and return the maximum/minimum value)<br />
<br />
These functions work because Python knows how to compare the elements in the list (they are all integers). But what if the elements in the list are not integers? Fortunately, there is a way for you to tell Python how to turn each element of the list into a number that it can understand.<br />
<br />
Lets start with an example. Lets say you have a list of strings, and want to find the shortest string in the list. Here's what you can do:<br />
<br />
<pre>>>> min(['hihi', 'bye', 'a', 'zebra'], key=len)<br />
'a'</pre><br />
<br />
Notice the new keyword argument key we are passing into the min function. key is a function that min applies to each element of the list. In this case, the key is the len function, which returns the length of each string. Applying the key function to each element will return a cooresponding integer, which Python can easily use to find the minimum element.<br />
<br />
You can also use keys in the <code>sorted</code> function too, which returns a sorted list of its inputs, based on the key function passed in.<br />
<br />
<pre>>>> sorted(['hihi', 'bye', 'a', 'zebra'], key=len)<br />
['a', 'bye', 'hihi', 'zebra']</pre><br />
<br />
We can have more complex key functions. Here we sort a list of people by their age, which is the second element in the tuple. A key function, once defined, works for sorted, min and max:<br />
<br />
<pre>>>> names = [('Alice', 19, 'F'), ('Bob', 5, 'M'), ('Charlie', 12, 'M')]<br />
>>> get_age = lambda name: name[1]<br />
>>> sorted(names, key=get_age)<br />
[('Bob', 5, 'M'), ('Charlie', 12, 'M'), ('Alice', 19, 'F')]<br />
>>> max(names, key=get_age)<br />
('Alice', 19, 'F')</pre><br />
<br />
'''Dictionary default values'''<br />
<br />
Suppose we have a dictionary mapping names to counts:<br />
<br />
<pre>>>> d = {'apples': 1, 'pears': 9000}</pre><br />
<br />
If we want to add a new pear to the dictionary, we can use:<br />
<br />
<pre>>>> d['pears'] = d['pears'] + 1<br />
>>> d<br />
{'apples': 1, 'pears': 9001}</pre><br />
However we cannot use the same code to add a new item that is not already in the dictionary.<br />
<br />
<pre>>>> d['oranges'] = d['oranges'] + 1<br />
Traceback (most recent call last):<br />
...<br />
KeyError: 'oranges'</pre><br />
<br />
To solve this problem, we have to use <code>dict.setdefault(key, default)</code>. If <code>key</code> is in <code>dict</code>, it will return <code>dict[key]</code>. If not, it will insert <code>key</code> with a value of <code>default</code> and return <code>default</code>. Now we can write:<br />
<br />
<pre>>>> d['oranges'] = d.setdefault('oranges', 0) + 1<br />
>>> d<br />
{'oranges': 1, 'apples': 1, 'pears': 9001}<br />
>>> d['oranges'] = d.setdefault('oranges', 0) + 1<br />
>>> d<br />
{'oranges': 2, 'apples': 1, 'pears': 9001}</pre><br />
<br />
There's actually a even better way of doing this. If you are curious to find out, look up <code>collections.defaultdict</code>.<br />
<br />
'''For loops'''<br />
<br />
If you are iterating through a list and want to get both the item and the index the item is at, the built-in function enumerate is helpful here.<br />
<br />
<pre>>>> a = ["apple", "pear", "orange"]<br />
>>> for index, fruit in enumerate(a):<br />
... print(index, fruit)<br />
...<br />
0 apple<br />
1 pear<br />
2 orange</pre><br />
<br />
You can iterate through each key-value pair in a dictionary with dictionary.items. This is useful if you want to access both the key and the value at the same time.<br />
<br />
<pre>>>> prices = {"apple": 3, "pear": 5, "orange": 20}<br />
>>> for fruit, price in prices.items():<br />
... print(fruit, price)<br />
...<br />
apple 3<br />
pear 5<br />
orange 20</pre><br />
<br />
Hope this helps for the project!<br />
<br />
=== Programming style in scheme ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=2068 Source: Spring 2014 Piazza (2068)]<br />
<br />
Since Scheme has no rules on whitespace and indentation, you could technically write all your Scheme in one line like this:<br />
<pre>(define (fib n) (if (< n 1) n (+ (fib (- n 1)) (fib (- n 2)))))</pre><br />
<br />
But that would be terrible and everyone who had to read your code would hate you for it. Here is a more reasonable version:<br />
<pre>(define (fib n)<br />
(if (< n 1) ; Arguments to if are flush<br />
n ; Each argument gets a new line<br />
(+ (fib (- n 1)) ; Sometimes it makes to insert a newline<br />
(fib (- n 2))))) ; so that you can see arguments side by side</pre><br />
<br />
Remember that code is primarily for humans to read and incidentally for computers to run.<br />
<br />
Here are some more examples:<br />
<pre>(define (deep-map f lst)<br />
(cond ((null? lst) lst)<br />
((list? (car lst))<br />
(cons (deep-map f (car lst))<br />
(deep-map f (cdr lst))))<br />
(else<br />
(cons (f (car lst))<br />
(deep-map f (cdr lst)))) ))<br />
<br />
STk> (deep-map (lambda (x) (* x x)) (list 1 2 (list (list 3) 4)))<br />
(1 4 ((9) 16))</pre><br />
<br />
<pre>(define (reverse lst)<br />
(define (helper lst result)<br />
(if (null? lst)<br />
result<br />
(helper (cdr lst)<br />
(cons (car lst) result)) ))<br />
(helper lst ()) )<br />
<br />
STk> (reverse (list 1 2 3))<br />
(3 2 1)</pre><br />
<br />
=== ucb.py's <code>trace</code> method ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=3000 Source: Spring 2014 Piazza (3000)]<br />
<br />
'''Student Question'''<br />
<br />
How does the trace in ucb.py actually work?<br />
<br />
'''Student Answer'''<br />
<br />
It's actually very similar to the <code>printed</code> function that was defined in the Hog spec!<br />
<br />
<pre>def printed(fn):<br />
def print_and_return(*args):<br />
result = fn(*args)<br />
print('Result:', result)<br />
return result<br />
return print_and_return<br />
</pre><br />
<br />
The main idea is still the same in <code>trace</code> -- we want to figure out all the arguments and save the result of calling the function with those arguments (so we can print it before returning it). One (sort of) major improvement is <code>**kwds</code>. Just like how <code>*args</code> collected all the "positional arguments", <code>**kwds</code> captures all the "keyword arguments" (the ones of the form <code>param='some_val'</code>). This can be a bit confusing, but a couple of examples might help!<br />
<br />
<pre>&gt;&gt;&gt; def add_three(a, b, c):<br />
... return a + b + c<br />
&gt;&gt;&gt; add_three(1, 2, 3) # all arguments are positional (normal)<br />
&gt;&gt;&gt; add_three(1, 2, c=3) # a, b are positional arguments, c is a keyword argument<br />
<br />
&gt;&gt;&gt; def fn(*args, **kwargs):<br />
... print(args)<br />
... print(kwargs)<br />
&gt;&gt;&gt; fn(1, 2, 3)<br />
(1, 2, 3)<br />
{}<br />
&gt;&gt;&gt; fn(1, k=2)<br />
(1,)<br />
{'k' : 2}<br />
&gt;&gt;&gt; fn(a=1, b=2, c=3)<br />
()<br />
{'a':1, 'b':2, 'c':3}<br />
</pre><br />
<br />
Since there are only two types of arguments, having both <code>*args</code> and <code>**kwds</code> covers all our bases. If we passed <code>printed</code> a keyword argument, it could cause an error!<br />
<br />
Everything else in <code>trace</code> just makes the output prettier and more helpful. <code>trace</code> uses the <code>_PREFIX</code> global variable to keep track of how far to indent the next print statement. It catches exceptions and prints them out, before re-raising that exception. It also uses some Python black magic to figure out the name of the function so we can print <code>some_fn</code> instead of <code>&lt;function some_fn at 0x...&gt;</code>.<br />
<br />
If there's a particular aspect of <code>trace</code> that you're confused about, feel free to post a followup!<br />
<br />
== Debugging ==<br />
== Miscellaneous ==<br />
=== Andrew Huang's tips ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=779 Source: Spring 2014 Piazza (779)]<br />
<br />
Order of evaluation matters. The rules for evaluating call expressions are<br />
<br />
# Evaluate the operator<br />
# Evaluate the operands<br />
# Call the operator on the operands (and draw a new frame...)<br />
<br />
For example:<br />
<pre>def baz():<br />
print("this was first")<br />
def bar(x):<br />
print(x)<br />
return lambda x: x * x<br />
return bar # baz is a function that when called, returns a function named bar<br />
<br />
>>> baz() # the operator is baz, there are no operands<br />
this was first<br />
<function bar at 0x2797e20><br />
>>> baz()("this was second") # the operator is baz(), the operand is "this was second"<br />
this was first<br />
this was second<br />
<function <lambda> at 0x2120e20><br />
>>> baz()("this was second")(3) # the operator is baz()("this was second"), the operand is 3<br />
this was first<br />
this was second<br />
9<br />
>>> def bar(x):<br />
... print(x)<br />
... return 3<br />
... <br />
>>> baz()("this was second")(bar("this was third")) # the operator is baz()("this was second"), the operand is bar("this was third")<br />
this was first<br />
this was second<br />
this was third<br />
9</pre><br />
<br />
In order to solve any problem, you must first understand what the problem is asking. Often times it helps to try to explain it concisely in English. It also helps to come up with small examples. For example:<br />
<br />
<pre>def mouse(n):<br />
if n >= 10:<br />
squeak = n // 100<br />
n = frog(squeak) + n % 10<br />
return n<br />
<br />
def frog(croak):<br />
if croak == 0:<br />
return 1<br />
else:<br />
return 10 * mouse(croak+1)<br />
<br />
mouse(21023508479)</pre><br />
<br />
So the goal is to figure out what <code>mouse(21023508479)</code> evaluates to.<br />
<br />
One way is to just step-by-step evaluate this, as an interpreter would.<br />
<br />
Another way, is to understand what the functions are doing.<br />
<br />
Looking at <code>mouse</code>, we see that it takes in a number and outputs that same number if it is smaller than 10. otherwise, it'll return something weird. In order to understand that weird thing, we have to understand what <code>frog</code> is doing. <code>frog</code> takes in a number and if that number is <code>0</code>, return <code>1</code>. Otherwise, return ten times <code>mouse(croak+1)</code>. Well, this is still confusing. Let's try a small example.<br />
<pre>>>> mouse(357)<br />
47<br />
>>> mouse(123)<br />
23<br />
>>> mouse(1234)<br />
44<br />
>>> mouse(12345)<br />
245</pre><br />
There is a pattern. We notice that the resulting number is composed of every other digit of the original, plus one (except for the last one.)<br />
So <code>21023508479</code> is <code>[2+1][0+1][3+1][0+1][4+1][9] = 314159</code>. Can you see how the code reflects that?<br />
However in this particular example, the pattern is definitely tricky to find here, so it might make more sense to brute force it.<br />
<br />
<br />
Remember for recursion, you always need to find three things:<br />
* One or more base cases<br />
* One or more was to reduce the problem<br />
* A way to solve the problem given solutions to smaller problems<br />
For example, the discussion notes, we asked you to write count_stairs. This function takes in n, the number of steps, and returns all the ways you can climb up them if at each step, you can take either one or two steps.<br />
<br />
* Base cases: if we consider n to be the number of steps left to climb, then it makes sense that if there is 1 step left, then there is exactly one way. If there are two steps left, then there are exactly 2 ways (1 step, 1 step, or two steps). Why do we need two base cases here?<br />
* We can make the problem smaller by reducing the n. At each step, we can take one step (resulting in count_stairs(n-1)) or two steps (count_stairs(n-2)).<br />
* Assuming we get the solutions to the two recursive calls, we should add them together to get all the ways we can climb the stairs.<br />
<br />
Thus we end up with<br />
<pre>def count_stairs(n):<br />
if n <= 2:<br />
return n<br />
else:<br />
return count_stairs(n-1) + count_stars(n-2)</pre><br />
Notice that at each stair step, we either take one step or two steps. This is a common pattern in tree recursion. Look through Discussion 3 for more info.<br />
<br />
=== Y combinators (in Scheme) ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=2450 Source: Spring 2014 Piazza (2450)]<br />
<br />
'''Student Question'''<br />
<br />
Can someone explain this to me?<br />
<pre>scm> (((lambda (f) (lambda (x) (f f x)))<br />
(lambda (f k) (if (zero? k) 1 (* k (f f (- k 1)))))) 5)</pre><br />
I've edited the code as follows:<br />
<br />
<pre>(<br />
(<br />
(lambda (f) <br />
(lambda (x) (f f x))<br />
)<br />
(lambda (f k) <br />
(if (zero? k) 1 <br />
(* k (f f (- k 1)))<br />
)<br />
)<br />
) 5<br />
)</pre><br />
<br />
My understanding is that the second lambda function is passed as the first <code>f</code> in the first lambda function and the <code>5</code> is passed in as <code>x</code>. But does that mean <code>f f x</code> becomes the second lambda function with itself and <code>x</code> passed as the arguments to <code>(f k)</code>?<br />
<br />
'''Student Answer'''<br />
<br />
You're on the right track. The first lambda function is a higher order function that takes in a function, and then returns a function that takes one argument. It's actually the third lambda that is then passed into the first lambda (currying!) and then 5 is then passed into the resulting function.<br />
<br />
In case you're curious, this is the Python equivalent:<br />
<br />
<pre>>>> (lambda f: (lambda x: f(f, x))) (lambda f, k: 1 if k == 0 else (k * f(f, k - 1)))(5)</pre><br />
Which is then equivalent to:<br />
<br />
<pre>>>> def func1(f):<br />
def func2(x):<br />
return f(f, x)<br />
return func2<br />
>>> def func3(f, k):<br />
if k == 0:<br />
return 1<br />
else:<br />
return k * f(f, k - 1)<br />
>>> func1(func3)(5)<br />
120</pre><br />
By the way, this is just a fancy way of recursively calculating the factorial using only lambda functions. If you're still curious as to how this works, you could try this in Python tutor. Except I would recommend calculating 3! instead of 5, because it's a lot of frames.<br />
<br />
'''Instructor Answer'''<br />
<br />
Maybe it will look a little nicer in Python:<br />
<br />
<pre>(lambda f: lambda x: f(f, x))(lambda g, k: 1 if k == 0 else (k * g(g, k-1)))(5)</pre><br />
Or maybe not.<br />
<br />
So the idea is, you define a lambda function that takes a function <code>f</code>, and that returns a lambda function that takes an argument <code>x</code> and returns <code>f(f, x)</code>. Then, you call this lambda function you just defined on another lambda function (let's call this <code>func</code>) that takes a function g and another argument k, and is basically the factorial function. This first call returns the inner lambda of the first part, and when that's called with <code>5</code> you're essentially calling <code>func(func, 5)</code>. The chain of recursive calls then works as follows:<br />
<br />
<pre>func(func, 5) -> 5 * func(func, 4) -> 5 * 4 * func(func, 3) -> ... -> 120</pre><br />
In functional programming theory, this is known as a Y Combinator, and it is how you achieve recursion with just lambda functions. If you're wondering why we need <code>func</code> to take in a function as the first parameter, see what would happen if you took that part out!</div>Jeffreylu017https://www.ocf.berkeley.edu/~shidi/cs61a/wiki/Practice_problemsPractice problems2014-06-21T02:44:20Z<p>Jeffreylu017: /* General Review */</p>
<hr />
<div>The following is a list of links to practice problems.<br />
<br />
== Lists, Dictionaries, and Comprehensions ==<br />
* [https://docs.google.com/document/d/1fC_7h1FppyRP3qy9tZPa9rjan4Abg2hedcJHvPOVask Final Review from Sp14]<br />
<br />
== Higher Order Functions ==<br />
* [https://docs.google.com/document/d/1vmlz0wKSYoKZTbhuL8HoE6VgmVWC3Pmm-LbTEHYRsDQ Worksheet from Sp14]<br />
* [https://docs.google.com/document/d/1nLYTTjwoa0HnOGZFPXKwoqZrl4e_QBZQPiC8yCM18iE Higher order functions and environment diagrams from Sp14] ([https://docs.google.com/document/d/1bvKGxLoEmmUJxZtsNnRgFH8YNNKWtpKK7B9ZMwh1ljg Solutions])<br />
<br />
== Lambda Expressions ==<br />
* [http://www.ocf.berkeley.edu/~joy/fa13/review/lambda.py Python exercise from Fa13]<br />
* [http://www.ocf.berkeley.edu/~joy/fa13/review/lambda_sol.py Solutions to python exercise from Fa13]<br />
<br />
== Environment Diagram ==<br />
* [https://www.dropbox.com/s/z6nlsgp0chfd6fc/environment_review.pdf Review worksheet from Fa13]<br />
* [http://www.michellerubyhwang.com/handouts/sp14/EnvDiagramSp14.pdf Michelle Hwang's Environment Diagram Rules]<br />
* [https://www.youtube.com/watch?v=ia60GQNKChI Andrew Huang draws an environment diagram] ([https://docs.google.com/document/d/1RPUfcOggSXdEWeYptXgCVwDQF08JNbuOhd9qmDfEPhc Original question and solution)]<br />
<br />
== Sequences and Tuples ==<br />
* [https://docs.google.com/document/d/1kHzSd8q8V9WhsFLneaXzNXI9Cp-KZryBxJvSACvQQ2E Final Review]<br />
<br />
== General Review ==<br />
Please note that topics covered by each exam sometimes vary between semesters, so previous exams and reviews do not necessarily reflect the contents or format of the exam of the current semester. Consult instructors for a list of topics that you will be responsible for on each exam.<br />
=== Midterm 1 Review ===<br />
* [http://albertwu.org/cs61a/review/mt1 Albert Wu's Midterm 1 Practice Problems]<br />
* HKN Midterm 1 Review ([https://docs.google.com/presentation/d/1aAVpvCEROitc3H-RKRL5zF5mAuH6PvfnN4obnulmg68 Spring 2014])<br />
* [http://www.michellerubyhwang.com/cs61a/ Michelle Hwang's Midterm Review Problems]<br />
<br />
=== Midterm 2 Review ===<br />
* [http://albertwu.org/cs61a/review/mt2 Albert Wu's Midterm 2 Practice Problems]<br />
* [https://docs.google.com/document/d/1RlXy9xpyRE8JPbEDrU4YSoxR5f4N2QAgFCwPodzOPYc Andrew Huang's Midterm 2 Review Questions]<br />
* [https://d1b10bmlvqabco.cloudfront.net/attach/hoxc5uu6sud761/gru0m9962oa/hsnl3drfb0d5/PS1.pdf Soumya Basu's Midterm 2 Review Problem Set (Hard)]<br />
* [https://docs.google.com/document/d/1JKX0WhlYuEkYE9Ep7x5Prf22bH7QcznJ_f3ebKvOOos Marvin Zhang's Midterm 2 Review Problems] ([https://docs.google.com/document/d/1W1CF9OSa9GQeevu_mnQFQa-gOIF4xCGJcX93hDSaF5c Solutions])<br />
<br />
=== Final Review ===<br />
* CS 61A Review Materials ([http://www-inst.eecs.berkeley.edu/~cs61a/sp14/practice-materials/practice.html Spring 2014])<br />
* [http://albertwu.org/cs61a/review/final Albert Wu's Final Review Problems]<br />
* Mark Miyashita's Review Problems ([http://markmiyashita.com/cs61a/sp14/practice/ Spring 2014], [http://markmiyashita.com/cs61a/fa13/practice/ Fall 2013], [http://markmiyashita.com/cs61a/su13/practice/ Summer 2013], [http://markmiyashita.com/cs61a/sp13/practice/ Spring 2013])</div>Jeffreylu017https://www.ocf.berkeley.edu/~shidi/cs61a/wiki/GuidesGuides2014-06-21T02:24:16Z<p>Jeffreylu017: /* Python semantics and syntax */</p>
<hr />
<div>== Higher-order functions ==<br />
== Environment diagrams ==<br />
=== Environment diagram Rules ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=131 Source: Spring 2014 Piazza (131)]<br />
<br />
Environment Diagrams are very important in our understanding of how the computer interprets our code.<br />
<br />
'''We will test you on this in every exam.'''<br />
<br />
'''It will never go away.'''<br />
<br />
'''Given that, master it as quickly as you can! :)'''<br />
<br />
Below are the rules I follow when drawing environment diagrams. If you understand and faithfully follow these rules when drawing them, you'll never get them wrong.<br />
<br />
One thing you haven't learned yet is nonlocal. You can skip that particular step for now (step 2 of Assignment).<br />
<br />
Post here if you have any questions!<br />
<br />
You can also take a look at this link for some examples of environment diagrams: http://albertwu.org/cs61a/notes/environments<br />
<br />
For a different perspective on the rules, check out: http://markmiyashita.com/cs61a/sp14/environment_diagrams/rules_of_environment_diagrams/<br />
<br />
A handout with detailed instructions on drawing environment diagrams is also available here (linked on the bottom of the course homepage): http://inst.eecs.berkeley.edu/~cs61a/sp14/pdfs/environment-diagrams.pdf<br />
<br />
<pre>Environment Diagram Rules<br />
=========================<br />
<br />
Creating a Function<br />
--------------------<br />
1. Draw the func <name>(<arg1>, <arg2>, ...)<br />
2. The parent of the function is wherever the function was defined<br />
(the frame we're currently in, since we're creating the function).<br />
3. If we used def, make a binding of the name to the value in the current frame.<br />
<br />
Calling User Defined Functions<br />
------------------------------<br />
1. Evaluate the operator and operands.<br />
2. Create a new frame; the parent is whatever the operator s parent is.<br />
Now this is the current frame.<br />
3. Bind the formal parameters to the argument values (the evaluated operands).<br />
4. Evaluate the body of the operator in the context of this new frame.<br />
5. After evaluating the body, go back to the frame that called the function.<br />
<br />
Assignment<br />
----------<br />
1. Evaluate the expression to the right of the assignment operator (=).<br />
2. If nonlocal, find the frame that has the variable you re looking for,<br />
starting in the parent frame and ending just before the global frame (via<br />
Lookup rules). Otherwise, use the current frame. Note: If there are multiple<br />
frames that have the same variable, pick the frame closest to the current<br />
frame.<br />
3. Bind the variable name to the value of the expression in the identified<br />
frame. Be sure you override the variable name if it had a previous binding.<br />
<br />
Lookup<br />
------<br />
1. Start at the current frame. Is the variable in this frame?<br />
If yes, that's the answer.<br />
2. If it isn't, go to the parent frame and repeat 1.<br />
3. If you run out of frames (reach the Global frame and it's not there), complain.<br />
<br />
Tips<br />
----<br />
1. You can only bind names to values.<br />
No expressions (like 3+4) allowed on environment diagrams!<br />
2. Frames and Functions both have parents.</pre><br />
<br />
== Sequences ==<br />
=== Reversing tuples ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=639 Source: Spring 2014 Piazza (639)]<br />
<br />
'''Student Question'''<br />
<br />
Why does [::-1] tuple work while the tuple [0:3:-1] doesn't?<br />
<br />
I thought the -1 after the second semicolon meant that the interpreter is going to read the indexes "backwards". <br />
<br />
'''Student Answer'''<br />
<br />
The syntax of slicing is <tt>tup[start:end:step]</tt>:<br />
<ul><li>start from index <tt>start</tt> and end just before index <tt>end</tt>, incrementing the index by <tt>step</tt> each time<br />
</li><li>if no <tt>step</tt> is provided, <tt>step</tt> = 1<br />
</li><li>if <tt>step</tt> is positive, default values if not provided: <tt>start</tt> = 0, <tt>end</tt> = <tt>len(tup)</tt><br />
</li><li>if <tt>step</tt> is negative, default values if not provided: <tt>start</tt> = -1, <tt>end</tt> = one position before the start of the string<br />
</li></ul><br />
<br />
<pre>&gt;&gt;&gt; (1, 2, 3)[::-1] # start at index -1, end one position before the start of the string<br />
(3, 2, 1)<br />
&gt;&gt;&gt; (1, 2, 3)[0:3:-1] # start at 0 and go to 3, but step is negative, so this doesn't make sense and an empty tuple is returned<br />
()<br />
</pre><br />
<br />
This is a helpful visualization from http://en.wikibooks.org/wiki/Python_Programming/Strings#Indexing_and_Slicing:<br />
<blockquote><br />
To understand slices, it's easiest not to count the elements themselves. It is a bit like counting not on your fingers, but in the spaces between them. The list is indexed like this:<br />
<pre>Element: 1 2 3 4<br />
Index: 0 1 2 3 4<br />
-4 -3 -2 -1<br />
</pre><br />
</blockquote><br />
More info about slicing at http://stackoverflow.com/a/13005464/2460890.<br />
<br />
=== Slicing with negative step ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=702 Source: Spring 2014 Piazza (702)]<br />
<br />
'''Student Question'''<br />
<br />
if the third example returns an empty tuple because you can't take negative steps from 0 to 4, shouldn't the second example also return an empty tuple?<br />
<br />
Can someone explain why each example returns the respective answers?<br />
<br />
Thanks<br />
<br />
<pre>>>> x= (1,2,3,4)<br />
>>> x[0::-1]<br />
(1,)<br />
>>> x[::-1]<br />
(4, 3, 2, 1)<br />
>>> x[0:4:-1]<br />
()<br />
>>> x[1::-1]<br />
(2, 1)</pre><br />
<br />
'''Instructor Answer'''<br />
<br />
(For reference, the notation is <tt>x[start:end:step]</tt>)<br />
<br />
Python does something a very strange when the step is negative: if you omit the arguments to start and end, Python will fill them with what makes sense for a negative step. In the simple case of <tt>x[::-1]</tt>, Python fills in the start with <tt>len(x)-1</tt> and the end with <tt>-(len(x)+1)</tt>. The end term is strange, but remember that the end term isn't included. We therefore can't use 0, but we can't use -1 either, since that clearly refers to the last element of the tuple. We need to fully wrap the negative index around, to refer to the element "before" the 0th index. This way, Python will start at the end of the tuple and proceed to the beginning of the tuple.<br />
<br />
That's why <tt>x[0:4:-1]</tt> doesn't make sense: how can we start at 0 and end at 4, if we're proceeding backwards?<br />
<br />
And that's why <tt>x[0::-1]</tt> makes sense (albeit, in a strange way): Python is proceeding from the 0 index to the beginning of the list. It includes the start index, which is why you see a 1 pop up.<br />
<br />
Let me know if that was confusing!<br />
<br />
== Recursion ==<br />
== Data abstraction ==<br />
== Time complexity ==<br />
=== Andrew Huang's guide to order of growth and function runtime ===<br />
[https://docs.google.com/document/d/1TxfKmM3MlH032hjSUh92I0kQDVcvmitTSzYObGMr8Bk Source: Guide to Order of Growth and Function Runtime (Retrieved June 16th, 2014)]<br />
<br />
'''Introduction'''<br />
<br />
Confused by $O$, $\Omega$, and $\Theta$?<br />
<br />
Want to figure out the runtime of that tricky function?<br />
<br />
Read this.<br />
<br />
'''NOTE THAT THIS GUIDE STARTS WITH BIG O, WHICH IS DIFFERENT FROM THETA. IF YOU UNDERSTAND BIG O, THETA IS EASY (IN FACT, IT DEFINES THETA IN TERMS OF BIG O BELOW).'''<br />
<br />
First some math.<br />
<br />
Formal definition of O(Big O):<br />
<br />
Let $f(n)$ and $g(n)$ be functions from positive integers to positive reals. We say $f \in O(g)$ (“f grows no faster than g”) if there is a constant $0 < c < \inf$ <such that $f(n) \leq c \cdot g(n)$.<br />
<br />
(Paraphrased from Dasgupta, Papadimitriou, & Vazirani)<br />
<br />
(You'll see this again in CS 170)<br />
<br />
What the heck does that mean?<br />
<br />
Let’s look at math functions for a second (just a second).<br />
<br />
Say $f(n)=5n$ and $g(n)=n^{2}$<br />
<br />
What does that look like on a graph?<br />
<br />
http://www.wolframalpha.com/input/?i=plot+5n+and+n%5E2+from+0+to+10<br />
<br />
There’s a section where $n$ dominates $n^{2}$, from 0 to 5, but we don’t really care, because after that point, $n^{2}$ is larger, all the way to infinity! By the definition, we could scale $n^{2}$ by 5 and we would span that initial gap.<br />
<br />
Thus we can say $5n \in O(n^{2})$ or $f \in O(g)$.<br />
<br />
Can we say the converse? That is, is $n^{2} \in O(5n)$?<br />
<br />
Not at all! From the graph we see that $n^{2}$ grows too quickly for $n$ to catch up, no matter what constant we scale $n$ by.<br />
<br />
So what if $f(n)=n+1000$ and $g(n)=n^{2}$?<br />
<br />
It turns out $n+1000 \in O(n^{2})$ still, because according to the definition, as long as we can multiply $n^{2}$ by some $c$, such that the gap of 1000 is spanned, we’re good. In the case, $c=1001$.<br />
<br />
'''What about and $\Omega$ and $\Theta$?'''<br />
<br />
If you digested all of the above, the rest isn’t scary! (Note, $a \equiv b$ means $a$ is equivalent to $b$)<br />
<br />
$f \in \Omega(g) \equiv g \in O(f)$ (You'll see this again briefly in CS 170)<br />
<br />
$f \in \Theta(g) (f \in O(g) and g \in O(f))$<br />
<br />
'''This means that if $f$ is Theta of $g$, then there exist some $c_{1}$ and $c_{2}$ such that'''<br />
<br />
'''$c_{1}g > f$ and'''<br />
<br />
'''$c_{2}g < f$'''<br />
<br />
'''for all positive integers.'''<br />
<br />
'''What does that mean for Python functions?'''<br />
<br />
Given a function $f$, we want to find out how fast that function runs. One way of doing this is to take out a stopwatch, and clock the amount of time it takes for $f$ to run on some input. However, there are tons of problems with that (different computers => different speeds; only one fixed input? Maybe $f$ is really fast for that input but slow for everything else; next year, all the measurements need to be redone on new computers; etc.) Instead, we'll count the steps that a function needs to perform as a function of its input. For example, here are some of the functions that take one step regardless of their input:<br />
<br />
<code>mul</code><br />
<br />
<code>add</code><br />
<br />
<code>sub</code><br />
<br />
<code>print</code><br />
<br />
<code>return</code><br />
<br />
<code>...</code><br />
<br />
So for example, <code>(3 + 3 * 8) % 3</code> would be 3 steps--one for the multiply, one of the add, and one for the mod.<br />
<br />
Let's take a simple example:<br />
<pre>def square(x):<br />
return x * x</pre><br />
<code>square</code> is a function that for any input, always takes two steps, one of the multiplication, and one for returning. Using the notation, we can say square ∈ Θ(1).<br />
<br />
Functions with iteration (for loops, recursion, etc.), usually multiply the steps by some factor. For example, consider factorial:<br />
<pre>def factorial(n):<br />
if n == 0:<br />
return 1<br />
else:<br />
return n * factorial(n-1)</pre><br />
factorial ∈ Θ(n). Why? Well given some input <code>n</code>, we do <code>n</code> recursive calls. At each recursive call, we carry out 4 steps, one for if <code>n == 0</code>, one for subtraction, one for multiply, one for return. Plus, we have the base case, which is another 2 steps, one for if and one for return. So <code>factorial(n)</code> takes $4n+2$ steps => ∈ Θ(n).<br />
<br />
As mentioned, we care about how the running time (how long the function takes to run) of the function changes, '''as we increase the size of the argument'''. So if we imagine a graph, then the x-axis represents the size of our input, and the y-axis represents how long the function took to run for each x. As the size of the input increases, the function’s runtime does something on the graph. So when we say something like “$O(n^{2})$ where $n$ is the length of the list”, we are saying as we double the size of the list, the function is expected to run at most four times as long. '''NOTE ALSO THAT I SAID WHAT $n$ IS! ALWAYS GIVE YOUR UNITS.'''<br />
<br />
This means that when we compare two functions A and B, A may be overall slower than B as we increase the size of their arguments. However, it’s possible at some specific arguments, the A may run faster (like the $f(n)=5n$ and $g(n)=n^{2}$ example above.)<br />
<br />
'''This also means we do not care about the time taken of any particular input! This implies that all those constant-time base cases all those functions don’t really matter, because they don’t scale. That is, only one specific input causes the base case to be reached, and if we increased the size of the argument, $O(1)$ doesn't necessarily hold.'''<br />
<br />
'''Brief “What runs faster than what”'''<br />
<br />
Sorted from fastest to slowest. This is by no means comprehensive.<br />
* $\Theta(1)$<br />
* $\Theta(\log(n))$<br />
* $\Theta(n)$<br />
* $\Theta(n \log(n))$<br />
* $\Theta(n^{2})$<br />
* $\Theta(n^{3})$<br />
* $\Theta(2^{n})$<br />
* (Anything past this point is kind of ridiculous)<br />
* $\Theta(n!)$<br />
* $\Theta(n^{n})$<br />
<br />
'''So we know about the math and the motivation, now how do we actually assign runtimes to real Python functions?'''<br />
<br />
What you must understand, is that there is no one method for finding the runtime. You MUST look at a function holistically or you won’t get the right answer. What does this mean? '''In order to get the correct runtime, you first must understand what the function is doing! You cannot pattern-match your way to becoming good at this.'''<br />
<br />
'''This cannot be stressed enough: UNITS MATTER, if you say O((n)), you must tell us what $n$ is.'''<br />
<br />
General tips<br />
# UNDERSTAND WHAT THE FUNCTION IS DOING!!!<br />
# Try some sample input. That is, pretend you’re the interpreter and execute the code with some small inputs. What is the function doing with the input? Having concrete examples lets you do tip 1 better. You can also graph how the runtime increases as the argument size increases.<br />
# If applicable, draw a picture of the tree of function calls. This shows you the "growth" of the function or how the function is getting "bigger", which will help you do tip 1 better.<br />
# If applicable, draw a picture of how the input is being modified through the function calls. For example, if your input is a list and your function recursively does something to that list, draw out a list, then draw out parts of the list underneath it that are called during the recursion. Helps with tip 1.<br />
# See tip 1.<br />
<br />
Anyways, let's examine some common runtimes (keep scrolling). Remember, '''this is in no way a comprehensive list, NOR IS IT TRYING TO TEACH YOU HOW TO FIND THEM.''' This post is just to give you a starting point into orders of growth by showing you some examples and basic details about each runtime.<br />
<br />
'''Constant $\Theta(1)$'''<br />
<br />
''What it looks like:''<br />
<br />
http://www.wolframalpha.com/input/?i=plot+5<br />
<br />
''Example:''<br />
<br />
<pre>def add(x, y):<br />
return x + y</pre><br />
$add \in \Theta(1)$, where 1 is.. well a constant...<br />
<br />
''Approach:''<br />
<br />
The key behind constant time functions is that regardless of the size of the input, they always run the same number of instructions.<br />
<br />
''Don’t fall for this Trap:''<br />
<br />
<pre>def bar(n):<br />
if n % 7 == 0:<br />
return "Bzzst"<br />
else:<br />
return bar(n -1)</pre><br />
$\mathtt{bar} \in \Theta(1)$. Why?<br />
<br />
'''Logarithmic $\Theta(\log(n))$'''<br />
<br />
''What it looks like:''<br />
<br />
http://www.wolframalpha.com/input/?i=plot+4log3n+from+0+to+10<br />
<br />
''Example:''<br />
<br />
<pre>def binary_search(sorted_L, n):<br />
""" sorted_L is a list of numbers sorted from<br />
smallest to largest<br />
"""<br />
if sorted_L == []:<br />
return False<br />
mid_num = sorted_L[len(sorted_L) // 2]<br />
if n == mid_num:<br />
return True<br />
elif n < mid_num:<br />
return binary_search(sorted_L[:mid_num], n)<br />
else:<br />
return binary_search(sorted_L[mid_num:], n)</pre><br />
$\mathtt{binary\_search} \in \Theta(log(n))$, where $n$ is the number of elements in <code>sorted_L</code>.<br />
<br />
''Approach:''<br />
<br />
Logarithmic functions scale down the size of the problem by some constant every iteration (either with a recursive loop, a for loop, or a while loop). Also, logarithmic functions do not branch out--they generally do not make more than one call to themselves per recursion.<br />
<br />
'''Linear $\Theta(n)$'''<br />
<br />
''What it looks like:''<br />
<br />
http://www.wolframalpha.com/input/?i=plot+8n+from+0+to+10<br />
<br />
''Examples:''<br />
<br />
<pre>def sum_list(L):<br />
sum = 0<br />
for e in L:<br />
sum += e<br />
return sum</pre><br />
$\mathtt{sum\_list} \in \Theta(n)$, where $n$ is the number of elements in $L$.<br />
<br />
</pre>def countdown(n):<br />
if n > 0:<br />
print(n)<br />
countdown(n - 1)<br />
else:<br />
print("Blast off!")</pre><br />
$\mathtt{countdown} \in \Theta(n)$, where $n$ is n.<br />
<br />
''Approach:''<br />
<br />
Linear functions usually act on sequences or other collections of data. In that case, the function will go through the elements once or twice or ''k'' times, where $k<<n$. If the function acts on a number, the number usually gets smaller by a constant each iteration.<br />
<br />
Don't fall for this trap:<br />
<pre>def two_for_loops(n):<br />
for a in range(n):<br />
if n == 4:<br />
for y in range(n):<br />
print("Admiral Ackbar")<br />
else:<br />
print("It's a trap!")</pre><br />
$\mathtt{two\_for\_loops} \in \Theta(n)$, where $n$ is n. Why?<br />
<br />
'''Loglinear/Linearithmic $\Theta(n \log(n))$'''<br />
<br />
''What it looks like:''<br />
<br />
http://www.wolframalpha.com/input/?i=plot+nlog%28n%29+from+0+to+10<br />
<br />
''Example:''<br />
<br />
<pre>def merge(s1, s2):<br />
if len(s1) == 0:<br />
return s2<br />
elif len(s2) == 0:<br />
return s1<br />
elif s1[0] < s2[0]:<br />
return [s1[0]] + merge(s1[1:], s2)<br />
else:<br />
return [s2[0]] + merge(s1, s2[1:])<br />
<br />
def mergesort(lst):<br />
if len(lst) <= 1:<br />
return lst<br />
else:<br />
middle = len(lst) // 2<br />
return merge(mergesort(lst[:middle]), \<br />
mergesort(lst[middle:]))</pre><br />
$\mathtt{mergesort} \in \Theta(n \log(n))$, where $n$ is the number of elements in <code>lst</code>.<br />
<br />
Approach:<br />
These functions tend to make two recursive calls, each making the problem smaller by a half. There's a neat way to see this. For example in mergesort, start with an entire line, which represents mergesort called on the initial list. From there, the list gets split in half by the two recursive calls to mergesort in the code, so draw the another line right below the first, of the same length, but with a small gap in the middle to represent the split. Repeat until you're tired. At the end, you get a rectangle that's nwide and (n)tall!<br />
<pre><nowiki><br />
---------------<br />
------- -------<br />
--- --- --- ---<br />
- - - - - - - -<br />
</nowiki></pre><br />
The total area is the runtime, $\Theta(n \log(n))$<br />
<br />
''Don’t fall for this trap:''<br />
<br />
Don’t confuse functions that have an average running time of n(n)(like quicksort) with functions that are in (n(n))<br />
<br />
'''Polynomial $\Theta(n^{2})$,$\Theta(n^{3})$, etc.'''<br />
<br />
''What it looks like:''<br />
<br />
http://www.wolframalpha.com/input/?i=plot+n%5E2%2B3+from+0+to+10<br />
<br />
''Example:''<br />
<br />
<pre>def print_a_grid(n):<br />
for _ in range(n):<br />
for _ in range(n):<br />
print("+", end="")<br />
print("")</pre><br />
$\mathtt{print\_a\_grid} \in \Theta(n^{2})$, where $n$ is n.<br />
<br />
''Approach:''<br />
<br />
Polynomial functions will examine each element of an input many, many times, as opposed to linear functions, which examine some constant number of times.<br />
<br />
''Don’t fall into this trap:''<br />
<br />
Don’t get polynomial confused with exponential (below).<br />
<br />
'''Exponential'''<br />
<br />
''What it looks like:''<br />
<br />
http://www.wolframalpha.com/input/?i=plot+2%5En+from+0+to+10<br />
<br />
''Example:''<br />
<br />
<pre>(define (strange-add x)<br />
(if (zero? x)<br />
1<br />
(+ (strange-add (- x 1))<br />
(strange-add (- x 1)) )))<br />
<br />
def strange_add(x):<br />
if x == 0:<br />
return 1<br />
else:<br />
return strange_add(x - 1) + strange_add(x - 1)</pre><br />
$\mathtt{strange\_add} \in \Theta(2^{n})$, where $n$ is x.<br />
<br />
''Approach:''<br />
<br />
Exponential functions tend to branch out as you get deeper and deeper into their call tree, and each call only makes the work smaller by a little bit. For example, <code>(strange-add 8)</code> calls <code>(strange-add 7)</code> and <code>(strange-add 7)</code>. Those two calls each make two calls, <code>(strange-add 6)</code>, <code>(strange-add 6)</code>, <code>(strange-add 6)</code>, and <code>(strange-add 6)</code> respectively, and so on.<br />
<br />
== Mutability ==<br />
=== Michelle Chang's guide to immutability and mutability ===<br />
[https://d1b10bmlvqabco.cloudfront.net/attach/hoxc5uu6sud761/gozdkhgdUbT/htdlpko411i0/Python__Immutable_vs_Mutable.pdf Source: What You Should Know about Immutability vs Mutability]<br />
<br />
== Mutable data-structures ==<br />
== Object-oriented programming ==<br />
=== Inheritance and class vs instance attributes ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=1413 Source: Spring 2014 Piazza (1413)]<br />
<br />
'''Student Question'''<br />
<br />
I'm confused on how Classes and Inheritance work.<br />
<br />
If there's a Parent class and a Child class, when coding in the Child class, when do you write <code>Parent.attribute</code>, when do you write <code>Child.attribute</code>, and when do you write <code>self.attribute</code>?<br />
<br />
Also, I'm also confused as to when to put <code>self</code> into the parentheses as well.<br />
<br />
'''Instructor Answer'''<br />
<br />
<code>Parent.attribute</code> and <code>Child.attribute</code> would both be ways of accessing a'''class variable'''. These are variables that can be accessed without creating new '''instances''' of the that class.<br />
<br />
<code>self.attribute</code> would be used in '''methods''' to access an '''instance variable''' (an attribute specific to an instance).<br />
<br />
So for example, <code>Insect.watersafe</code> is <code>False</code>, but <code>Bee.watersafe</code> is <code>True</code>. These are class attributes because you don't have to create an Insect object or a Bee object in order to say <code>Insect.watersafe</code> or <code>Bee.watersafe</code>.<br />
<br />
However it wouldn't make any sense to say <code>Bee.armor</code>, since armor is an '''instance variable'''. You have to first create a new Bee before you could ask it for it's armor. If you created a second Bee after that, the second Bee would also have its own armor.<br />
<br />
There's a lot of vocab (in bold) that might trip you up. Try reading Discussion 6 and posting a followup if you're still unsure!<br />
<br />
== Iterables, iterators and generators ==<br />
== Python semantics and syntax ==<br />
=== @property ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=3015 Source: Spring 2014 Piazza (3015)]<br />
<br />
'''Student Question'''<br />
<br />
Is it possible to call <code>@property</code> on methods that have arguments?<br />
<br />
'''Student Answer'''<br />
<br />
<code>@property</code> is used to define "setters" and "getters". It essentially gives you control of what happens when a variable is assigned or retrieved.<br />
<pre>class A:<br />
def __init__(self):<br />
self._x = 5<br />
@property<br />
def x(self):<br />
return self._x<br />
@x.setter<br />
def x(self, value):<br />
print("Hello World")<br />
self._x = value<br />
<br />
&gt;&gt;&gt; a = A()<br />
&gt;&gt;&gt; a.x = 52<br />
"Hello World"</pre><br />
As such, there isn't really room for arguments, since it is treated similar to a variable rather than a function. It is retrieved as "a.x" and set as "a.x = value". <br />
<br />
If you would like to pass arguments when assigning something, it would probably be best to just have them use that method. e.g.<br />
<pre>def set_x(self, value1, value2, value3):<br />
self._x = value1 * value2 * value3</pre><br />
Of course, you won't get the syntactic sugar of "a.x = ", but it gets the job done.<br />
<br />
The cool thing about properties is that you can also place restrictions on a variable. For example, if you leave out the "x.setter" method in the example above, then assigning a.x = 5 would throw an error since it isn't defined (you can still assign a._x though). It would only be available for reading. Sometimes that is a good sign to let any users know to use some method to set it (e.g. set_x) or to not set it at all.<br />
<br />
== Scheme ==<br />
=== Scheme semantics and syntax ===<br />
----<br />
==== Difference between <code>eq?</code>, <code>eqv?</code>, <code>equal?</code> and <code>=</code> ====<br />
[http://stackoverflow.com/questions/16299246/what-is-the-difference-between-eq-eqv-equal-and-in-scheme/17719745#17719745 What is the difference between eq?, eqv?, equal?, and = in Scheme?]<br />
<br />
Let's start with the <code>=</code> equivalence predicate. The <code>=</code> predicate is used to check whether two numbers are equal. If you supply it anything else but a number then it will raise an error:<br />
<br />
<pre>(= 2 3) => #f<br />
(= 2.5 2.5) => #t<br />
(= '() '()) => error</pre><br />
The <code>eq?</code> predicate is used to check whether its two parameters respresent the same object in memory. For example:<br />
<br />
<code>(define x '(2 3))<br />
(define y '(2 3))<br />
(eq? x y) => #f<br />
(define y x)<br />
(eq? x y) => #t</code><br />
Note however that there's only one empty list <code>'()</code> in memory (actually the empty list doesn't exist in memory, but a pointer to the memory location <code>0</code> is considered as the empty list). Hence when comparing empty lists <code>eq?</code> will always return <code>#t</code> (because they represent the same object in memory):<br />
<br />
<pre>(define x '())<br />
(define y '())<br />
(eq? x y) => #t</pre><br />
Now depending upon the implementation <code>eq?</code> may or may not return <code>#t</code> for primitive values such as numbers, strings, etc. For example:<br />
<br />
(<pre>eq? 2 2) => depends upon the implementation<br />
(eq? "a" "a") => depends upon the implementation</pre><br />
This is where the <code>eqv?</code> predicate comes into picture. The <code>eqv?</code> is exactly the same as the <code>eq?</code> predicate, except that it will always return <code>#t</code> for same primitive values. For example:<br />
<br />
<pre>(eqv? 2 2) => #t<br />
(eqv? "a" "a") => #t</pre><br />
Hence <code>eqv?</code> is a superset of <code>eq?</code> and for most cases you should use <code>eqv?</code> instead of <code>eq?</code>.<br />
<br />
Finally we come to the <code>equal?</code> predicate. The <code>equal?</code> predicate is exactly the same as the <code>eqv?</code> predicate, except that it can also be used to test whether two lists, vectors, etc. have corresponding elements which satisfy the <code>eqv?</code> predicate. For example:<br />
<br />
<pre>(define x '(2 3))<br />
(define y '(2 3))<br />
(equal? x y) => #t<br />
(eqv? x y) => #f</pre><br />
In general:<br />
# Use the <code>=</code> predicate when you wish to test whether two numbers are equivalent.<br />
# Use the <code>eqv?</code> predicate when you wish to test whether two non-numeric values are equivalent.<br />
# Use the <code>equal?</code> predicate when you wish to test whether two lists, vectors, etc. are equivalent.<br />
# Don't use the <code>eq?</code> predicate unless you know exactly what you're doing.<br />
<br />
=== Scheme lists ===<br />
----<br />
==== Using <code>cons</code> ====<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=3004 Source: Spring 2014 Piazza (3004)]<br />
<br />
'''Student Question<br />
<br />
What's the difference between the following in Scheme?<br />
<br />
(cons 1 2)<br />
(cons 1 . 2)<br />
<br />
(cons 1 (cons 2 (cons 3 nil)))<br />
(cons 1 . (cons 2 . (cons 3 . 4)))<br />
<br />
Why does putting a dot before "(cons" cause it to be a malformed list? But when you put in (cons 1 2) it returns (1 . 2)? Is the dot something that only the interpreter returns, and that the user can't use in defining a list?<br />
<br />
'''Student Answer'''<br />
<br />
I struggled with this a bit as well. It helps to know the difference between a list containing a "." and one that doesn't. <br />
<br />
First off, scheme lists love to be recursive, kind of like rlists. That is, if you did cdr on the list, you would keep getting a list until you finally get an empty list/nil.<br />
<pre>STk&gt; (define a '(1 2 3))<br />
a<br />
STk&gt; a<br />
(1 2 3)<br />
STk&gt; (cdr a)<br />
(2 3)<br />
STk&gt; (cddr a)<br />
(3)<br />
STk&gt; (cdddr a)<br />
()</pre><br />
Note how each call of cdr returns a list. Even (3) is a list. It is just a list containing one member. So what happens with something like (1 2 . 3)?<br />
<pre>STk&gt; (define a '(1 2 . 3))<br />
a<br />
STk&gt; a<br />
(1 2 . 3)<br />
STk&gt; (cdr a)<br />
(2 . 3)<br />
STk&gt; (cddr a)<br />
3</pre><br />
Notice how the last <code>cdr</code> returns a simple 3. Running <code>cdddr</code> would throw an error, because the list stops at 3. A list containing a "." is known as an <em>improper </em>list.<br />
<br />
As for the ".", you won't be using it unless you use it in combination with a quote, otherwise it will always return a malformed list and throw an error. For example:<br />
<pre>STk&gt; (define a (1 2 . 3))<br />
*** Error:<br />
eval: malformed list: (1 2 . 3)<br />
Current eval stack:<br />
__________________<br />
0 (1 2 . 3)<br />
1 (define a (1 2 . 3))<br />
STk&gt; (define a '(1 2 . 3))<br />
a</pre><br />
Basically, the "." is seen in output, but not input. The only exception is the quote. Think of scheme as having two stages. First, it interprets your commands to construct the lists etc. Next, it will simplify the expression. Think of the quote as skipping straight to the second stage.<br />
<br />
Finally, the only way to get the standard lists is to end the list with nil or to use the "list" function (or to use a quote). If the list doesn't end with nil, then it will become an improper list. That is why you <em>can't</em> do things like <code>(1 . 2 . 3 . 4)</code> to make <code>(1 2 3 4)</code>. In order to form a "proper" list, each element must be represented by a list. You <em>can</em> do <code>(1 . (2 . (3 . (4))))</code> because you are treating each element like a list. When in doubt, just test some output:<br />
<pre>STk&gt; (cons 1 2)<br />
(1 . 2) ; doesn't end in an empty list/nil<br />
STk&gt; (cons 1 (cons 2 '()))<br />
(1 2)<br />
STk&gt; (cons 1 (cons 2 (3)))<br />
; ERROR<br />
STk&gt; (cons 1 (cons 2 (list 3)))<br />
(1 2 3) ; lists are formed with an nil at the end, so this works<br />
STk&gt; '(1 . (2 . (3 . 4)))<br />
(1 2 3 . 4)<br />
STk&gt; '(1 . (2 . (3 . (4)))<br />
(1 2 3 4)<br />
STk&gt; '(1 (2 . 3) 4)<br />
(1 (2 . 3) 4) ; still works, but this has 3 elements: (1), (2 . 3), and (4)<br />
STk&gt; (cons 1 (cons (cons 2 3) (cons 4 nil))) <br />
(1 (2 . 3) 4) ; equivalent to above, without quote</pre><br />
<br />
==== <code>append</code> vs <code>cons</code> vs <code>list</code> ====<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=2067 Source: Spring 2014 Piazza (2067)]<br />
<br />
This post isn't meant to be comprehensive. '''Ask questions in lab or as a followup here if you're confused.'''<br />
One of its major flaws is that it doesn't cover box and pointers. '''LEARN BOX AND POINTERS. '''<br />
<br />
Here is a beautiful web based Scheme interpreter that will draw box and pointer diagrams for you. Run through the examples below with this thing:<br />
http://xuanji.appspot.com/js-scheme-stk/index.html<br />
<br />
In order to understand these three procedures, you first have to understand a little about Pairs and Lists.<br />
<br />
'''Pairs''' are data structures that have two slots. You can put different stuff in these slots, like numbers or words or sentences or booleans--pretty much anything. You make a pair using cons.<br />
<pre>STk> (cons 'foo 'bar)<br />
(foo . bar)<br />
<br />
STk> (cons 1 'ring)<br />
(1 . ring)<br />
<br />
STk> (cons (+ 1 2 3) (member? 3 '(the 3 stooges)))<br />
(6 . #t)</pre><br />
<br />
In order to get stuff from a pair that you have made, you use <code>car</code> and <code>cdr</code>. <code>car</code> gets the thing in the first slot. <code>cdr</code> gets the thing in the second slot.<br />
<br />
<pre>STk> (define foo (cons 'x 'y))<br />
foo<br />
<br />
STk> foo<br />
(x . y)<br />
<br />
STk> (car foo)<br />
x<br />
<br />
STk> (cdr foo)<br />
y</pre><br />
That was straightforward. Now for the trippy part:<br />
'''You can put pairs inside of pairs:'''<br />
<pre>STk> (define foo (cons (cons 3 4) 5))<br />
foo<br />
<br />
STk> foo<br />
((3 . 4) . 5)<br />
<br />
STk> (car foo)<br />
(3 . 4)<br />
<br />
STk> (car (car foo))<br />
3<br />
<br />
STk> (caar foo) ; functionally equivalent as above.<br />
3<br />
<br />
STk> (cdr foo)<br />
5<br />
<br />
STk> (cdr (car foo))<br />
4<br />
<br />
STk> (cdar foo) ; functionally equivalent as above.<br />
4</pre> <br />
<br />
There's a certain style of pair nesting that is especially useful&mdash;'''Lists.'''<br />
<br />
Each list has these properties:<br />
* Every list is a pair or the empty list (denoted by '() or nil).<br />
* The car of a nonempty list is some item.<br />
* The cdr of a nonempty list must be another list.<br />
<pre>STk> (cons 1 (cons 2 (cons 3 '()))) ; list of numbers<br />
(1 2 3)<br />
<br />
STk> (define stooges (cons 'larry (cons 'curly (cons 'moe nil))))<br />
stooges<br />
<br />
STk> stooges<br />
(larry curly moe)<br />
<br />
STk> (car stooges)<br />
larry<br />
<br />
STk> (cdr stooges) ; Calling cdr on a non-empty list gives you another list!<br />
(curly moe)<br />
<br />
STk> (cadr stooges)<br />
curly<br />
<br />
STk> (cdar stooges) ; Why does this break?<br />
*** Error:<br />
cdar: bad list: (moe larry curly)<br />
Current eval stack:<br />
__________________<br />
0 (cdar stooges)<br />
<br />
STk> (define not-a-list (cons 'foo (cons 'bar 'baz))) ; This is not a list.<br />
not-a-list<br />
<br />
STk> not-a-list ; What property does this break?</pre><br />
Notice how Scheme knew that we were making lists. Before we had <code>parens</code> and periods which organized our items. Scheme now recognizes that we're making a list and does away with the periods and some of the <code>parens</code>.<br />
<br />
If you stare a bit at the list rules above, you can notice we used a recursive definition to define lists. Recursion... '''on data'''!<br />
<br />
Let's talk about <code>list</code>. <code>list</code> takes a bunch of stuff and makes a list out of them. The stuff can be anything. Words, numbers, pairs, other lists. <code>list</code> doesn't care. [picture of a honey badger]<br />
<pre>STk> (list 'foo 'bar' 'baz) ; Lists takes anything and makes a list out of it.<br />
(foo bar baz)<br />
<br />
STk> (list 'foo ((lambda (x) (+ x 4)) 8) #f (cons 1 (cons 3 4)) (cons 1 (cons 2 nil)) (list 1 2 3)) ; ANYTHING <br />
(foo 12 #f (1 3 . 4) (1 2) (1 2 3))<br />
<br />
STk> (list 'x 'y 'z)<br />
(x y z)<br />
<br />
STk> '(x y z) ; Sometimes you can get away with using quote to make literal lists. Yes, sentences are secretly lists.<br />
(x y z)</pre><br />
Now we can talk about <code>append</code>:<br />
<pre>STk> (append '(a b c) '(d e f) '(g h i)) ; Append takes in lists and appends them together.<br />
(a b c d e f g h i)<br />
<br />
STk> (append 'foo '(1 2 3)) ; foo is not a list. Stuff will break.<br />
*** Error:<br />
append: argument is not a list: foo<br />
Current eval stack:<br />
__________________<br />
0 (append (quote foo) (quote (1 2 3)))</pre><br />
You know that <code>cons</code> makes a pair. You also know that you can make a list out of pairs. You can abuse <code>cons</code> for your own maniacal purposes.<br />
<pre>STk> (cons 'joe stooges) ; Put stuff at the beginning of a list!<br />
(joe larry curly moe)</pre><br />
'''The following only applies to the STk interpreter.'''<br />
<pre>STk> (append '(1 2 3) 'foo) ; Wait... what?<br />
(1 2 3 . foo)<br />
<br />
STk> (append '(1 2 3) (cons 4 5)) ; The plot thickens!<br />
(1 2 3 4 . 5)<br />
<br />
STk> (append stooges 'shemp) ; You should really figure out why this works.<br />
(larry curly moe . shemp)</pre><br />
<br />
To summarize:<br />
* append takes in lists and outputs a big list.<br />
* cons takes in things and makes a pair out of them. However, we know that lists are made of pairs, so we can throw together a list if we use cons a certain way<br />
* list takes in things and makes a list out of those things, regardless of what they are.<br />
<br />
=== Tail recursion ===<br />
----<br />
==== Mark Miyashita's guide on tail recursion ====<br />
[http://markmiyashita.com/cs61a/sp14/tail_recursion/tail_recursion_and_tail_optimized_calls/ Source: Tail Recursion and Tail Optimized Calls]<br />
<br />
First off, I think this is an excellent article to read about tail recursion and tail calls in Python: [http://paulbutler.org/archives/tail-recursion-in-python/ here]<br />
<br />
Basically, you can write tail recursive functions in any language. Tail recursion, in one sentence, is where you return the answer in the final frame instead of following the frames back up to the original frame. For example, we have factorial which is normally not tail recursive:<br />
<br />
<pre>def factorial(n):<br />
if n == 1:<br />
return 1<br />
return n * factorial(n - 1)</pre><br />
<br />
because it needs to keep track of the <code>n *</code> at each level of recursion.<br />
<br />
The following implementation of <code>factorial</code>, is tail recursive because at the end of the last frame, it can return the answer, instead of going back up through all the frames to multiply and compute the answer:<br />
<br />
<pre>def factorial(n):<br />
def helper(n, total):<br />
if n == 1:<br />
return total<br />
return helper(n - 1, total * n)<br />
return helper(n, 1)</pre><br />
<br />
You can’t have tail optimized calls in Python – at least, not like the code that we defined above. You can define your own sort of tail optimized way of evaluating the functions by using lambdas, and I believe the article linked at the top of this post goes into detail about how to implement this if you are interested. In Scheme, the language detects when you have something like the helper function in the example above where your return statement consists of only the recursive call. In the first example, we have the <code>n *</code> and the recursive call which means it cannot be tail optimized because it needs to keep track of all of the frames that it creates. In a tail optimized call, Scheme will get rid of the frames that are no longer necessary.<br />
<br />
'''tl;dr''' – Tail recursion can be done in any language where the basic idea is that you return the answer in the final frame of recursion. Tail optimized calls are a Scheme (and some other languages, not including Python) feature where it will get rid of the frames above, if certain conditions are met – such as where the return statement is only the recursive call and nothing else. The cases in which Scheme uses a tail optimized call are located on the lecture slides located [http://www-inst.eecs.berkeley.edu/~cs61a/sp13/slides/35-TailCalls_6pp.pdf here].<br />
<br />
==== Tail recursion in Python ====<br />
[http://kylem.net/programming/tailcall.html Source: http://kylem.net/programming/tailcall.html (Retrieved June 16th, 2014)]<br />
<br />
In this page, we’re going to look at tail call recursion and see how to force Python to let us eliminate tail calls by using a trampoline. We will go through two iterations of the design: first to get it to work, and second to try to make the syntax seem reasonable. I would not consider this a useful technique in itself, but I do think it’s a good example which shows off some of the power of decorators.<br />
<br />
The first thing we should be clear about is the definition of a tail call. The “call” part means that we are considering function calls, and the “tail” part means that, of those, we are considering calls which are the last thing a function does before it returns. In the following example, the recursive call to f is a tail call (the use of the variable <code>ret</code> is immaterial because it just connects the result of the call to <code>f</code> to the return statement), and the call to <code>g</code> is not a tail call because the operation of adding one is done after <code>g</code> returns (so it’s not in “tail position”).<br />
<br />
<pre>def f(n) :<br />
if n > 0 :<br />
n -= 1<br />
ret = f(n)<br />
return ret<br />
else :<br />
ret = g(n)<br />
return ret + 1</pre><br />
<br />
'''1. Why tail calls matter'''<br />
<br />
Recursive tail calls can be replaced by jumps. This is called “tail call eliminination,” and is a transformation that can help limit the maximum stack depth used by a recursive function, with the benefit of reducing memory traffic by not having to allocate stack frames. Sometimes, recursive function which wouldn’t ordinarily be able to run due to stack overflow are transformed into function which can.<br />
<br />
Because of the benefits, some compilers (like <code>gcc</code>) perform tail call elimination[1], replacing recursive tail calls with jumps (and, depending on the language and circumstances, tail calls to other functions can sometimes be replaced with stack massaging and a jump). In the following example, we will eliminate the tail calls in a piece of code which does a binary search. It has two recursive tail calls.<br />
<br />
<pre>def binary_search(x, lst, low=None, high=None) :<br />
if low == None : low = 0<br />
if high == None : high = len(lst)-1<br />
mid = low + (high - low) // 2<br />
if low > high :<br />
return None<br />
elif lst[mid] == x :<br />
return mid<br />
elif lst[mid] > x :<br />
return binary_search(x, lst, low, mid-1)<br />
else :<br />
return binary_search(x, lst, mid+1, high)</pre><br />
<br />
Supposing Python had a <code>goto</code> statement, we could replace the tail calls with a jump to the beginning of the function, modifying the arguments at the call sites appropriately:<br />
<br />
<pre>def binary_search(x, lst, low=None, high=None) :<br />
start:<br />
if low == None : low = 0<br />
if high == None : high = len(lst)-1<br />
mid = low + (high - low) // 2<br />
if low > high :<br />
return None<br />
elif lst[mid] == x :<br />
return mid<br />
elif lst[mid] > x :<br />
(x, lst, low, high) = (x, lst, low, mid-1)<br />
goto start<br />
else :<br />
(x, lst, low, high) = (x, lst, mid+1, high)<br />
goto start</pre><br />
<br />
which, one can observe, can be written in actual Python as<br />
<br />
<pre>def binary_search(x, lst, low=None, high=None) :<br />
if low == None : low = 0<br />
if high == None : high = len(lst)-1<br />
while True :<br />
mid = low + (high - low) // 2<br />
if low > high :<br />
return None<br />
elif lst[mid] == x :<br />
return mid<br />
elif lst[mid] > x :<br />
high = mid - 1<br />
else :<br />
low = mid + 1</pre><br />
<br />
I haven’t tested the speed difference between this iterative version and the original recursive version, but I would expect it to be quite a bit faster because of there being much, much less memory traffic.<br />
<br />
Unfortunately, the transformation makes it harder to prove the binary search is correct in the resulting code. With the original recursive algorithm, it is almost trivial by induction.<br />
<br />
Programming languages like Scheme depend on tail calls being eliminated for control flow, and it’s also necessary for continuation passing style.[2]<br />
<br />
'''2. A first attempt'''<br />
<br />
Our running example is going to be the factorial function (a classic), written with an accumulator argument so that its recursive call is a tail call:<br />
<br />
<pre>def fact(n, r=1) :<br />
if n <= 1 :<br />
return r<br />
else :<br />
return fact(n-1, n*r)</pre><br />
<br />
If <code>n</code> is too large, then this recursive function will overflow the stack, despite the fact that Python can deal with really big integers. On my machine, it can compute <code>fact(999)</code>, but <code>fact(1000)</code> results in a sad <code>RuntimeError: Maximum recursion depth exceeded</code>.<br />
<br />
One solution is to modify fact to return objects which represent tail calls and then to build a trampoline underneath fact which executes these tail calls after fact returns. This way, the stack depth will only contain two stack frames: one for the trampoline and another for each call to fact.<br />
<br />
First, we define a tail call object which reifies the concept of a tail call:<br />
<br />
<pre>class TailCall(object) :<br />
def __init__(self, call, *args, **kwargs) :<br />
self.call = call<br />
self.args = args<br />
self.kwargs = kwargs<br />
def handle(self) :<br />
return self.call(*self.args, **self.kwargs)</pre><br />
<br />
This is basically just the thunk <code>lambda : call(*args, **kwargs)</code>, but we don’t use a thunk because we would like to be able to differentiate between a tail call and returning a function as a value.<br />
<br />
The next ingredient is a function which wraps a trampoline around an arbitrary function:<br />
<br />
<pre>def t(f) :<br />
def _f(*args, **kwargs) :<br />
ret = f(*args, **kwargs)<br />
while type(ret) is TailCall :<br />
ret = ret.handle()<br />
return ret<br />
return _f</pre><br />
<br />
Then, we modify fact to be<br />
<br />
<pre>def fact(n, r=1) :<br />
if n <= 1 :<br />
return r<br />
else :<br />
return TailCall(fact, n-1, n*r)</pre><br />
<br />
Now, instead of calling <code>fact(n)</code>, we must instead invoke <code>t(fact)(n)</code> (otherwise we’d just get a TailCall object).<br />
<br />
This isn’t that bad: we can get tail calls of arbitrary depth, and it’s Pythonic in the sense that the user must explicitly label the tail calls, limiting the amount of unexpected magic. But, can we eliminate the need to wrap t around the initial call? I myself find it unclean to have to write that <code>t</code> because it makes calling fact different from calling a normal function (which is how it was before the transformation).<br />
<br />
'''3. A second attempt'''<br />
<br />
The basic idea is that we will redefine fact to roughly be <code>t(fact)</code>. It’s tempting to just use <code>t</code> as a decorator:<br />
<br />
<pre>@t<br />
def fact(n, r=1) :<br />
if n <= 1 :<br />
return r<br />
else :<br />
return TailCall(fact, n-1, n*r)</pre><br />
<br />
(which, if you aren’t familiar with decorator syntax, is equivalent to writing <code>fact = t(fact)</code> right after the function definition). However, there is a problem with this in that the fact in the returned tail call is bound to <code>t(fact)</code>, so the trampoline will recursively call the trampoline, completely defeating the purpose of our work. In fact, the situation is now worse than before: on my machine, <code>fact(333)</code> causes a <code>RuntimeError</code>!<br />
<br />
For this solution, the first ingredient is the following class, which defines the trampoline as before, but wraps it in a new type so we can distinguish a trampolined function from a plain old function:<br />
<br />
<pre>class TailCaller(object) :<br />
def __init__(self, f) :<br />
self.f = f<br />
def __call__(self, *args, **kwargs) :<br />
ret = self.f(*args, **kwargs)<br />
while type(ret) is TailCall :<br />
ret = ret.handle()<br />
return ret</pre><br />
<br />
and then we modify <code>TailCall</code> to be aware of <code>TailCallers</code>:<br />
<br />
<pre>class TailCall(object) :<br />
def __init__(self, call, *args, **kwargs) :<br />
self.call = call<br />
self.args = args<br />
self.kwargs = kwargs<br />
def handle(self) :<br />
if type(self.call) is TailCaller :<br />
return self.call.f(*self.args, **self.kwargs)<br />
else :<br />
return self.call(*self.args, **self.kwargs)</pre><br />
<br />
Since classes are function-like and return their constructed object, we can just decorate our factorial function with <code>TailCaller</code>:<br />
<br />
<pre>@TailCaller<br />
def fact(n, r=1) :<br />
if n <= 1 :<br />
return r<br />
else :<br />
return TailCall(fact, n-1, n*r)</pre><br />
<br />
And then we can call fact directly with large numbers!<br />
<br />
Also, unlike in the first attempt, we can now have mutually recursive functions which all perform tail calls. The first-called <code>TailCall</code> object will handle all the trampolining.<br />
<br />
If we wanted, we could also define the following function to make the argument lists for tail calls be more consistent with those for normal function calls:[3]<br />
<br />
<pre>def tailcall(f) :<br />
def _f(*args, **kwargs) :<br />
return TailCall(f, *args, **kwargs)<br />
return _f</pre><br />
<br />
and then fact could be rewritten as<br />
<br />
<pre>@TailCaller<br />
def fact(n, r=1) :<br />
if n <= 1 :<br />
return r<br />
else :<br />
return tailcall(fact)(n-1, n*r)</pre><br />
<br />
One would hope that marking the tail calls manually could just be done away with, but I can’t think of any way to detect whether a call is a tail call without inspecting the source code. Perhaps an idea for further work is to convince Guido von Rossum that Python should support tail recursion (which is quite unlikely to happen).<br />
<br />
[1] This is compiler-writer speak. For some reason, “elimination” is what you do when you replace a computation with something equivalent. In this case, it’s true that the call is being eliminated, but in its place there’s a jump. The same is true for “common subexpression elimination” (known as CSE), which takes, for instance,<br />
<br />
<pre>a = b + c<br />
d = (b + c) + e<br />
and replaces it with<br />
a = b + c<br />
d = a + e</pre><br />
<br />
Sure, the <code>b+c</code> is eliminated from the second statement, but it’s not really gone...<br />
The optimization known as “dead code elimination” actually eliminates something, but that’s because dead code has no effect, and so it can be removed (that is, be replaced with nothing).<br />
<br />
[2] In Scheme, all loops are written as recursive functions since tail calls are the pure way of redefining variables (this is the same technique Haskell uses). For instance, to print the numbers from 1 to 100, you’d write<br />
<br />
<pre>(let next ((n 1))<br />
(if (<= n 100)<br />
(begin<br />
(display n)<br />
(newline)<br />
(next (+ n 1)))))</pre><br />
<br />
where next is bound to be a one-argument function which takes one argument, <code>n</code>, and which has the body of the <code>let</code> statement as its body. If that <code>100</code> were some arbitrarily large number, the tail call to next had better be handled as a jump, otherwise the stack would overflow! And there’s no other reasonable way to write such a loop!<br />
<br />
Continuation passing style is commonly used to handle exceptions and backtracking. You write functions of the form<br />
<br />
<pre>(define (f cont)<br />
(let ((cont2 (lambda ... (cont ...) ...)))<br />
(g cont2)))</pre><br />
<br />
along with functions which take multiple such f’s and combines them into another function which also takes a single cont argument. I’ll probably talk about this more in another page, but for now notice how the call to g is in the tail position.<br />
<br />
[3] This is basically a curried[4] version of <code>TailCall</code>.<br />
<br />
[4] That is, ''Schönfinkelized''.<br />
<br />
=== Miscellaneous ===<br />
----<br />
==== Useful Scheme Procedures ====<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=2226 Source: Spring 2014 Piazza (2226)]<br />
<br />
Here is a short list of Scheme procedures that you might use in writing your programs:<br />
<pre>; define - defines a variable or a procedure<br />
(define my-variable 4)<br />
(define (square x)<br />
(* x x))<br />
<br />
; if - conditional branching akin to if ... else<br />
(define (fib n)<br />
(if (< n 2)<br />
n<br />
(+ (fib (- n 1)) (fib (- n 2))) ))<br />
<br />
; conditional branching akin to if ... elif ... elif ... else<br />
(define (deep-map f lst)<br />
(cond ((null? lst) lst)<br />
((list? (car lst))<br />
(cons (deep-map f (car lst))<br />
(deep-map f (cdr lst))))<br />
(else<br />
(cons (f (car lst))<br />
(deep-map f (cdr lst)))) ))<br />
<br />
; and - outputs the the rightmost value if all of the arguments evaluate to #t. Outputs #f otherwise.<br />
STk> (and 0 1 2 3)<br />
3<br />
<br />
; or - outputs the the first value that evaluates to #t. Outputs #f otherwise.<br />
STk> (or 0 1 2 3)<br />
0<br />
<br />
; equal - tests if symbols are the same<br />
STk> (equal? 'foo 'bar)<br />
#f<br />
STk> (equal? 'foo 'foo)<br />
#t<br />
<br />
STk> (list? 'foo)<br />
#f<br />
STk> (list? '(1 2 3))<br />
#t<br />
STk> (list? '())<br />
#t<br />
<br />
STk> (null? '(1 2 3))<br />
#f<br />
STk> (null? ())<br />
#t<br />
<br />
; member? - tests if a symbol is in a list EDIT: NOT BUILT IN (BUT SUPER USEFUL SEE FOLLOWUP)<br />
STk> (member? 'quick '(the quick brown fox jumped over the lazy dog))<br />
#t<br />
<br />
; number? - checks if input is a number<br />
STk> (number? 42)<br />
#t<br />
STk> (number? #t)<br />
#f<br />
<br />
; remainder - computes the remainder of the first number divided by the second<br />
STk> (remainder 100 21)<br />
16</pre><br />
<br />
== Streams ==<br />
== Logic ==<br />
=== Quick Guide to Logic Programming ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=2524 Source: Spring 2014 Piazza (2524)]<br />
''Note: Someone should convert this from scmlog to Logic notation''<br />
<br />
'''Here's something I wrote a long time ago. The logic interpreter scmlog still exists and you should be able to access it using your cs61a-xx account. Post a followup if you have any questions. Hope this helps!'''<br />
<br />
<br />
'''Introduction:''' logic programming is a completely different way to think about telling computers to do stuff. Instead of telling the computer what to compute, you give the computer facts and ask it questions. The computer does its own thinking given the facts and the question, and then returns an answer.<br />
<br />
'''Table of Contents'''<br />
- Introduction<br />
- What is Logic Programming<br />
- - Giving Facts and Asking Questions<br />
- - More Complicated Facts and Questions<br />
- How to write Logic Programs<br />
- - Common Pitfalls<br />
- More Resources<br />
<br />
<br />
'''What is Logic Programming?'''<br />
Logic Programming is a way to ask the computer questions and get answers without telling it explicitly how to reach the conclusion. Kind of like this: http://youtu.be/tpKx7Oi0oeM<br />
<br />
Anyways, we have a logic programming interpreter called Scmlog. It interprets a simpler version of the logic programming language, Prolog. The first few things to understand about it is that '''SCMLOG IS NOT SCHEME'''. It just happens to look like it. Scmlog is its own language and so it has its own rules. You can't program in Scmlog like you would in Scheme or Python, so it might be good to forget what you know about those languages for a second.<br />
<br />
When you fire up scmlog (type scmlog into a terminal on the school computers), you get this prompt:<br />
<pre>star [501] ~ # scmlog<br />
scmlog (Prolog in Scheme), v. 0.2<br />
Type 'help' for help; 'quit' to exit.<br />
?- </pre><br />
You can interact with this prompt in two ways:<br />
# Give facts - "Let's tell the computer some things it should know!"<br />
# Ask questions - "Let's ask the computer questions about the things we told it about!"<br />
<br />
'''Giving Facts and Asking Questions'''<br />
A basic fact takes this form: <code>(fact (assertion))</code>, '''Where each assertion is simply a relation between things.'''<br />
<br />
For example: <code>(fact (likes potstickers brian))</code> relates three ideas, liking something, potstickers, and some guy name Brian. "Brian likes potstickers."<br />
Note that we put the relation first, and then the parties the relation acts upon. In this example, "likes" is the relation, it hooks up "brian" and "potstickers". Here's some more:<br />
<pre>?- (fact (likes potstickers brian)) <br />
?- (fact (likes potstickers andrew)) <br />
?- (fact (likes the_beatles brian)) <br />
?- (fact (likes the_beatles andrew)) <br />
?- (fact (likes led_zeppelin andrew)) <br />
?- (fact (dislikes led_zeppelin brian))</pre><br />
Now that we've given the computer a bunch of facts, how do we ask questions about them? Just replace "fact" with a "?", and replace any part of the relation (besides the relation itself) with a variable prefixed by an underscore. This is called "querying":<br />
<pre>?- (? (likes _what brian))<br />
_what : potstickers<br />
More? <br />
_what : the_beatles<br />
More? <br />
?- (? (likes potstickers _who))<br />
_who : brian<br />
More? <br />
_who : andrew<br />
More? <br />
?- (? (dislikes _what brian))<br />
_what : led_zeppelin<br />
More?<br />
?- (? (likes led_zeppelin andrew))<br />
Yes.<br />
?- (? (dislikes the_beatles _who))<br />
No.</pre><br />
Notice:<br />
* We can query any part of the assertion, besides the relation itself (can't replace "likes" with a variable).<br />
* All possible answers to the question show up.<br />
* If Scmlog couldn't find a fact that matched your query, it'll say "No."<br />
* Asking a question without any variables essentially asks if that fact exists. To which Scmlog will answer "Yes." or "No."<br />
<br />
'''More Complicated Facts and Questions'''<br />
Now this isn't the whole picture. We also have the ability to make more powerful assertions via variables, hypotheses, and conclusions:<br />
<br />
<code>(fact (ancestor _x _y) (parent _x _y))</code> "X is an ancestor of Y if X is a parent of Y"<br />
<br />
Here _x and _y are variables like usual. However, we have two parts to this fact, the conclusion ("X is an ancestor of Y") and the hypothesis ("X is a parent of Y"). We can have more than one hypotheses, and they can be any kind of query. Here's ancestor in action:<br />
<pre>?- (fact (parent george paul))<br />
?- (fact (parent martin george))<br />
?- (fact (parent martin martin_jr))<br />
?- (fact (parent martin donald))<br />
?- (fact (parent george ann))<br />
?- (fact (ancestor _X _Y) (parent _X _Y))<br />
?- (fact (ancestor _X _Y) (parent _X _Z) (ancestor _Z _Y))<br />
?- (? (ancestor paul george))<br />
No.<br />
?- (? (ancestor george paul))<br />
Yes.<br />
?- (? (ancestor george george))<br />
No.<br />
?- (? (ancestor martin paul))<br />
Yes.</pre><br />
Now there's one more thing you need to know about Scmlog. Scmlog knows about pairs and lists:<br />
<pre>?- (fact (lst (1 2 3)))<br />
?- (? (lst _x))<br />
_x : (1 2 3)<br />
More?<br />
?- (? (lst (1 . _x)))<br />
_x : (2 3)<br />
More? <br />
?- (? (lst (1 2 . _x)))<br />
_x : (3)<br />
More? <br />
?- (? (lst (_x . _y)))<br />
_x : 1<br />
_y : (2 3)<br />
?- (fact (my_pair (2 . 3)))<br />
?- (? (my_pair _y))<br />
_y : (2 . 3)<br />
?- (? (my_pair (2 . _x)))<br />
_x : 3</pre><br />
<br />
'''How to write Logic Programs'''<br />
<br />
The trick behind writing logic programs is to forget everything you know about programming. You instead want to focus on the relation you're trying to establish. Take append for example:<br />
<pre>?- (fact (append () _b _b))<br />
?- (fact (append (_x . _rest) _b (_x . _z)) (append _rest _b _z))<br />
?- (? (append (1 2 3) (3 2 1) _answer))<br />
_answer : (1 2 3 3 2 1)<br />
?- (append (1 2) 3 (1 2 3))<br />
Huh?<br />
?- (? (append (1 2) 3 (1 2 3))<br />
)<br />
No.<br />
?- (? (append (1 2) (3) (1 2 3))<br />
)<br />
Yes.<br />
?- (? (append (1 2) (3) (1 2 3)))<br />
Yes.<br />
?- (? (append _x (3 4 9) (1 0 3 2 3 4 9)))<br />
_x : (1 0 3 2)<br />
More? <br />
?- (? (append _x _y (2 1)))<br />
_x : ()<br />
_y : (2 1)<br />
More? <br />
_x : (2)<br />
_y : (1)<br />
More? <br />
_x : (2 1)<br />
_y : ()</pre><br />
<br />
What is append? Well the first fact establishes that if we append an empty list to something, the result is that something.<br />
The next fact is the meat of the code. You can almost think of this as a recursive relation. Any append relation between some three lists a b c must also fulfill another append relation between the rest of a, b, and the rest of c, respectively. Think about why that will always be true for all inputs. If the example above doesn't make sense to you, see the following:<br />
<br />
<br />
'''Common Pitfalls'''<br />
<br />
'''- One of the biggest issues people have with writing logic programs is that they don't realize that the variables don't work like they do in Scheme or Python. All Scmlog ever does is pattern match:'''<br />
<pre>?- (fact (x (0 1 8)))<br />
?- (fact (x (1 8 4)))<br />
?- (fact (x (9 4 4)))<br />
?- (fact (x (3 0 8)))<br />
?- (? (x _y))<br />
_y : (0 1 8)<br />
More? <br />
_y : (1 8 4)<br />
More? <br />
_y : (9 4 4)<br />
More? <br />
_y : (3 0 8)<br />
?- (? (x (0 1 . _z)))<br />
_z : (8)</pre><br />
<br />
- SCMLOG IS NOT SCHEME. SCMLOG IS NOT PYTHON.<br />
<br />
More Resources:<br />
* http://www-inst.eecs.berkeley.edu/~cs61a/sp12/lectures/prolog/ReadMe<br />
* http://www-inst.eecs.berkeley.edu/~cs61a/sp12/lectures/prolog/<br />
* http://www-inst.eecs.berkeley.edu/~cs61a/sp12/discussion/week14/<br />
* http://www-inst.eecs.berkeley.edu/~cs61a/fa13/slides/31-Logic_6pp.pdf<br />
<br />
=== Logic Mathematics ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=3050 Source: Spring 2014 Piazza (3050)]<br />
<br />
'''Student Question'''<br />
<br />
Can someone explain the intuition behind the implementation of the increments and the addition facts? The following is from Mark's website<br />
<br />
<pre>(fact (increment 0 1))<br />
(fact (increment 1 2))<br />
(fact (increment 2 3))<br />
(fact (increment 3 4))<br />
(fact (increment 4 5))<br />
(fact (increment 5 6))<br />
(fact (increment 6 7))<br />
(fact (increment 7 8))<br />
(fact (increment 8 9))<br />
(fact (increment 9 10))<br />
(fact (increment 10 11))<br />
(fact (increment 11 12))<br />
(fact (increment 12 13))<br />
<br />
(fact (add 1 ?x ?x+1)<br />
(increment ?x ?x+1))<br />
<br />
(fact (add ?x+1 ?y ?z+1)<br />
(increment ?x ?x+1)<br />
(increment ?z ?z+1)<br />
(add ?x ?y ?z))<br />
<br />
(query (add 2 4 6))<br />
; expect Success!</pre><br />
<br />
'''Student Answer'''<br />
<br />
First, we state a bunch of facts that denote relations between a number and the number that follows it (e.g. 1 and 2, 2 and 3, and so on). This is the increment fact.<br />
<br />
Then, we state a "base" fact, which is:<br />
<pre>(fact (add 1 ?x ?x+1)<br />
(increment ?x ?x+1))</pre><br />
In English, this fact states that some value <code>?x</code> added to 1 will give us some value <code>?x+1</code> (<code>x+1</code> is a valid variable name in Logic) '''if and only if''' the fact (<code>increment ?x ?x+1</code>) is true. As an example, <code>(add 1 2 3)</code> is true '''if and only if''' (<code>increment 2 3</code>) is true. This is true because of the increment facts we stated before.<br />
<br />
Now, to deal with additions that aren't just the sum of two numbers in which one number is a 1, we need:<br />
<pre>(fact (add ?x+1 ?y ?z+1)<br />
(increment ?x ?x+1)<br />
(increment ?z ?z+1)<br />
(add ?x ?y ?z))</pre><br />
I will try to give an intuition as to what is happening here.<br />
<br />
We state our fact: that two numbers, <code>?x+1</code> and <code>?y</code>, will add up to some number <code>?z+1</code> if and only if the following 3 hypotheses are true:<br />
* (<code>increment ?x ?x+1</code>) - "There exists some number <code>?x</code> that is 1 less than <code>?x+1</code>, and"<br />
* (<code>increment ?z ?z+1</code>) - "There exists some number <code>?z</code> that is 1 less than <code>?z+1</code>, and"<br />
* (<code>add ?x ?y ?z</code>) - "The numbers <code>?x</code> and <code>?y</code> will add up to <code>?z</code>."<br />
<br />
Consider the example of:<br />
<pre>(query (add 2 4 6))</pre><br />
Here's an idea of what's happening when Logic tries to match the query with the facts you've stated.<br />
* ?x+1 = 2, ?y = 4, ?z+1 = 6<br />
* It finds a match for <code>?x = 1</code>, since (<code>increment ?x ?x+1</code>) gives <code>?x = 1</code> because <code>?x+1 = 2</code><br />
* It finds a match for <code>?z = 5</code>, since (<code>increment ?z ?z+1</code>) gives <code>?z = 5</code> because <code>?z+1 = 6</code><br />
* It then checks for a match for (<code>add ?x ?y ?z</code>) which in this case is (<code>add 1 4 5</code>). This goes to our first "base" fact for add. (<code>add 1 4 5</code>) is a success because (<code>increment 4 5</code>) is a true fact [refer again to the "base fact" to see why this is the case]. This is also where we would get a "Failed.", if it turns out that (<code>increment ?x ?x+1</code>) wasn't actually true!<br />
* Hence, all 3 of our hypotheses are true, and so (<code>query (add 2 4 6)</code>) is a success!<br />
<br />
In this example, we only have to recurse once to get to our "base" fact. In other examples, where <code>?x+1</code> is not 2, but some number greater, such as 5, we will have to recurse 4 whole times to get to 1, at which point our "base" fact is reached. <br />
<br />
This recursion is similar to this idea in mathematical equations:<br />
<pre>x + y = z</pre><br />
is the same as<br />
<pre>(x - 1) + y = (z - 1)</pre><br />
is the same as<br />
<br />
<pre>(x - 2) + y = (z - 2)</pre?<br />
and so on... In Logic, we stop when we find that the first term (x) is 1, and then we use our increment facts to determine if the original statement is true, because all of these equations are equivalent.<br />
<br />
== Python syntax and semantics ==<br />
=== <code>print</code> vs <code>return</code> ===<br />
----<br />
==== Andrew's tips ====<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=779 Source: Spring 2014 Piazza (779)]<br />
<br />
Remember the differences between return and print.<br />
* <code>return</code> can only be used in a <code>def</code> statement. It returns a value from a function. Once Python evaluates a <code>return</code> statement, it immediately exits the function.<br />
* <code>print</code> is a function that displays its argument on the screen. It always returns <code>None</code>.<br />
<br />
Examples:<br />
<pre>def foo1(x):<br />
return x<br />
<br />
def foo2(x):<br />
print(x)<br />
<br />
>>> foo2(1) # In foo2, we print 1 ourselves using the print function<br />
1<br />
>>> foo1(1) # HERE, THE PYTHON INTERPRETER PRINTS THE RETURN VALUE OF FOO1. CANNOT STRESS HOW IMPORTANT TO UNDERSTAND THIS<br />
1<br />
>>> foo1(1) + 1<br />
2<br />
>>> foo2(1) + 1<br />
1<br />
Traceback (most recent call last):<br />
File "<stdin>", line 1, in <module><br />
TypeError: unsupported operand type(s) for +: 'NoneType' and 'int'</pre><br />
<br />
=== Function decorators ===<br />
<br />
----<br />
<br />
==== How function decorators work ====<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=638 Source: Spring 2014 Piazza (638)]<br />
<br />
'''Student Question'''<br />
<br />
I'm having difficulties understanding what exactly a function decorator is. Can someone elaborate and potentially provide me with an example other than the one in the readings?<br />
<br />
'''Instructor Answer'''<br />
<br />
So imagine you wanted your functions to print their arguments before they executed them. Here's one way to do this.<br />
<pre>def loud(fn):<br />
def new_fn(*args):<br />
print(args)<br />
return fn(*args)<br />
return new_fn </pre><br />
Here's a function loud that takes in a function and returns a new function that when called, prints out its arguments, and then does what the old function does.<br />
<br />
For example:<br />
<pre>def sq(x):<br />
return x * x<br />
>>> sq(4)<br />
16<br />
>>> sq = loud(sq) # replace the old square with our loud one.<br />
>>> sq(4)<br />
(4,)<br />
16</pre><br />
A function decorator does the same thing as the above. Assuming loud is defined, we can do this:<br />
<pre>@loud<br />
def sq(x):<br />
return x * x<br />
<br />
>>> sq(4)<br />
(4,)</pre><br />
<br />
== Student guides ==<br />
=== How to learn computer science ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=241 Source: Spring 2014 Piazza (241)]<br />
<br />
If you've never programmed before, or if you've never taken a class quite like 61A before, things right now might be scary. Everything is strange and new and there quite a lot to take in all at once. So if you're having a hard time so far, here are a few articles that might help.<br />
<br />
Note: these articles are pretty long, so feel free to read them in multiple sittings.<br />
<br />
'''At the beginning, everything seems a bit scary in CS'''. Michelle Bu, a Berkeley alum and a crazy good hacker, shares one of her experiences when she was a wee n00b in [http://blog.michellebu.com/2013/03/21-nested-callbacks/ 21 Nested Callbacks].<br />
<br />
'''Start here!''' [http://www.jamesmaa.com/2013/08/26/a-beginners-guide-to-computer-science/ "A Beginner's Guide to Computer Science"] Written by Berkeley's own James Maa. James is known for his killer walkthroughs (check out his Productivity guide). This article gives you some background on learning CS and then provides a practical guide on how to learn effectively.<br />
<br />
'''How do we learn?''' Mark Eichenlaub explains in this [http://www.quora.com/Learning/Do-grad-school-students-remember-everything-they-were-taught-in-college-all-the-time/answer/Mark-Eichenlaub Introduction to Learning Theory]. This is quite possibly the best introduction to Learning Theory.<br />
<br />
'''Sometimes, you're stuck and you end up really, really frustrated.''' Marcus Geduld explains [http://www.quora.com/Why-do-we-get-frustrated-when-learning-something/answer/Marcus-Geduld Why do we get frustrated when learning something?]<br />
<br />
=== Quick guide on getting unstuck ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=1264 Source: Quick Guide on Getting Unstuck (Retrieved June 16th, 2014)]<br />
<br />
A major frustration you might encounter in 61A is when you stare at a homework problem and have no idea where to start. Or you write some code and it doesn't pass the doctests, but now what? You work at it for a while, but next thing you know, you've been stuck for hours on the the same problem and have little to show about it.<br />
<br />
So here's a checklist of things you can do when you're stuck. Experienced programmers do these things almost naturally (because of how much practice they've had being stuck), and so while they get stuck just as much as your or I, they always know what to do next.<br />
<br />
# Do I understand what the problem is asking?<br />
## If not, which part of the problem is confusing me?<br />
### Identify the exact sentences/phrases/words/etc.<br />
## Check the given examples. Do they make sense to me?<br />
## Can I come up with my own examples? '''A good indicator that you understand the question is that you can come up with some nontrivial examples of how the function works.'''<br />
# What concepts should I use here?<br />
## Do I understand the concepts? '''Can I explain the concept in English to one of my friends such that they get it?'''<br />
### If not, go back and relearn the specific concepts that are unclear (through discussion, lab, lecture, etc.) Don't read the entire book in order to solve one problem..<br />
## How do I apply the concept to the given problem?<br />
# Write your code and test it.<br />
## Use doctests, '''BUT ALSO LOAD IT INTERACTIVELY (python3 -i ...)'''<br />
### '''Saying "my function works because the doctests pass" is a lot like saying "this airplane will fly because it has wings."'''<br />
## If your code breaks, ask yourself:<br />
### Does it error? Is it a....<br />
#### Syntax error? If so, find the syntax bug and fix it.<br />
#### Logic error? Is it something weird that you don't understand? (E.g. cannot add integer and tuple)<br />
### Why did it do that? Why didn't it do what I expected? Trace through the code by hand with an example (sample values) you came up with in step 0. '''Add calls to <code>print</code> in order to figure out how your function is handling the arguments.'''<br />
# Am I missing a trick?<br />
## Oftentimes you've never seen this type of problem before. This is expected on homework (and this is why homework can take a long time) because if you see it on the homework, then you will be familiar with it on the exam and when you program for fun and profit. <br />
## The key here is just to learn the trick however you need to.<br />
### Stare at it yourself<br />
### Stare at it with others (peers in the class)<br />
### Ask on PIazza what the approach is.<br />
### Stare at it with the TAs/lab Assistants<br />
## '''Once you figure it out, remember the trick so that you can use it next time.'''<br />
# At any point you identify what you're stuck on, you can begin to resolve it.<br />
## Use the tips above. Try things out on the interpreter. Review the lecture/discussion/labs/etc. Do whatever helps you get a better understanding of the problem.<br />
## Once you have something specific that you're stuck on, you can ask other people in the class.<br />
### '''Don't be afraid to ask. Everyone gets stuck and feels stupid sometimes. However, you get to choose how you react to it.'''<br />
### '''At the same time, it really helps to work with people who are on about the same level in the course.'''<br />
## Look on Piazza. Ask questions if yours hasn't come up yet. Be that awesome guy/girl who helps answer questions.<br />
## You can ask the TA if all else fails. We are here to help you learn!<br />
<br />
Here is an old algorithm for studying for tests (the final in this case), salvaged from the sands of time:<br />
<pre>For each topic on the final, find problems on them and do them.<br />
If you can solve them on your own, move on.<br />
Else if you are stuck, look at the solution and figure out if you<br />
are missing a trick or if you do not understand the concepts.<br />
If the problem is that you are stuck on some random trick,<br />
just learn the trick.<br />
Stare at the solutions, ask Piazza, your TA, etc.<br />
Questions you should ask at this stage:<br />
What is the problem asking me to do?<br />
How was I suppose to follow the instructions<br />
to solve the problem?<br />
What part of the problem do I not understand?<br />
What is the fastest way to clear up that misunderstanding?<br />
Then if you think you are still stuck conceptually, review<br />
and learn the concept, however you learn best.<br />
Suggestions for picking up concepts quickly (~1-2 hours):<br />
Discussion notes typically have a very concise recap of the<br />
thing they are going over.<br />
There are guides for particularly tricky things on Piazza,<br />
like Logic, Pairs and Lists in Scheme, etc.<br />
Find them and go over them.<br />
Ask a TA: "what is the best way to learn X?"<br />
If these do not work and you are still shaky after an hour<br />
or two, it might be worth watching a lecture or reading<br />
the notes.</pre><br />
<br />
== Composition ==<br />
=== General style guidelines from 61A website ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=149 Source: Spring 2014 Piazza (149)]<br />
<br />
'''Student Question'''<br />
<br />
Are we required to add any comments to our code to say what a function does, etc.? And does clarity of code count for this project, in which case should we write comments at the end of not-so-clear statements? Thanks.<br />
<br />
'''Student Answer'''<br />
<br />
Docstrings of each function are already provided. If you add a helper function, you should write a docstring for it.<br />
<br />
The [http://inst.eecs.berkeley.edu/~cs61a/sp14/style_guide.html#comments style guide on the course website] advises: "Your actual code should be self-documenting -- try to make it as obvious as possible what you are doing without resorting to comments. Only use comments if something is not obvious or needs to be explicitly emphasized"<br />
<br />
'''Instructor Answer'''<br />
<br />
You should always aim to make your code "self-documenting," meaning it is clear what your code is doing without the aid of comments. You should try to keep the number of comments to a minimum, but if there are lines which you think are unclear/ambiguous, feel free to add a comment.<br />
<br />
All projects in this class contain a 3 point component that is judged solely on your code "composition" -- i.e. whether your code is clear, concise, and easy to read.<br />
<br />
=== Simplifying code ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=1116 Source: Spring 2014 Piazza (1116)]<br />
<br />
Hi everyone, here's some tips about certain functions in Python that can greatly simplify your code for the Trends project.<br />
<br />
'''Sorting keys'''<br />
<br />
You should be familiar with the max and min functions in python, which can take in many arguments and return the maximum value.<br />
<br />
<pre>>>> max(1,3,2)<br />
3</pre><br />
<br />
These functions can also take in lists:<br />
<br />
<pre>>>> min([1,5,1,6])<br />
1</pre><br />
<br />
(In fact they can take in any ''iterable'' and return the maximum/minimum value)<br />
<br />
These functions work because Python knows how to compare the elements in the list (they are all integers). But what if the elements in the list are not integers? Fortunately, there is a way for you to tell Python how to turn each element of the list into a number that it can understand.<br />
<br />
Lets start with an example. Lets say you have a list of strings, and want to find the shortest string in the list. Here's what you can do:<br />
<br />
<pre>>>> min(['hihi', 'bye', 'a', 'zebra'], key=len)<br />
'a'</pre><br />
<br />
Notice the new keyword argument key we are passing into the min function. key is a function that min applies to each element of the list. In this case, the key is the len function, which returns the length of each string. Applying the key function to each element will return a cooresponding integer, which Python can easily use to find the minimum element.<br />
<br />
You can also use keys in the <code>sorted</code> function too, which returns a sorted list of its inputs, based on the key function passed in.<br />
<br />
<pre>>>> sorted(['hihi', 'bye', 'a', 'zebra'], key=len)<br />
['a', 'bye', 'hihi', 'zebra']</pre><br />
<br />
We can have more complex key functions. Here we sort a list of people by their age, which is the second element in the tuple. A key function, once defined, works for sorted, min and max:<br />
<br />
<pre>>>> names = [('Alice', 19, 'F'), ('Bob', 5, 'M'), ('Charlie', 12, 'M')]<br />
>>> get_age = lambda name: name[1]<br />
>>> sorted(names, key=get_age)<br />
[('Bob', 5, 'M'), ('Charlie', 12, 'M'), ('Alice', 19, 'F')]<br />
>>> max(names, key=get_age)<br />
('Alice', 19, 'F')</pre><br />
<br />
'''Dictionary default values'''<br />
<br />
Suppose we have a dictionary mapping names to counts:<br />
<br />
<pre>>>> d = {'apples': 1, 'pears': 9000}</pre><br />
<br />
If we want to add a new pear to the dictionary, we can use:<br />
<br />
<pre>>>> d['pears'] = d['pears'] + 1<br />
>>> d<br />
{'apples': 1, 'pears': 9001}</pre><br />
However we cannot use the same code to add a new item that is not already in the dictionary.<br />
<br />
<pre>>>> d['oranges'] = d['oranges'] + 1<br />
Traceback (most recent call last):<br />
...<br />
KeyError: 'oranges'</pre><br />
<br />
To solve this problem, we have to use <code>dict.setdefault(key, default)</code>. If <code>key</code> is in <code>dict</code>, it will return <code>dict[key]</code>. If not, it will insert <code>key</code> with a value of <code>default</code> and return <code>default</code>. Now we can write:<br />
<br />
<pre>>>> d['oranges'] = d.setdefault('oranges', 0) + 1<br />
>>> d<br />
{'oranges': 1, 'apples': 1, 'pears': 9001}<br />
>>> d['oranges'] = d.setdefault('oranges', 0) + 1<br />
>>> d<br />
{'oranges': 2, 'apples': 1, 'pears': 9001}</pre><br />
<br />
There's actually a even better way of doing this. If you are curious to find out, look up <code>collections.defaultdict</code>.<br />
<br />
'''For loops'''<br />
<br />
If you are iterating through a list and want to get both the item and the index the item is at, the built-in function enumerate is helpful here.<br />
<br />
<pre>>>> a = ["apple", "pear", "orange"]<br />
>>> for index, fruit in enumerate(a):<br />
... print(index, fruit)<br />
...<br />
0 apple<br />
1 pear<br />
2 orange</pre><br />
<br />
You can iterate through each key-value pair in a dictionary with dictionary.items. This is useful if you want to access both the key and the value at the same time.<br />
<br />
<pre>>>> prices = {"apple": 3, "pear": 5, "orange": 20}<br />
>>> for fruit, price in prices.items():<br />
... print(fruit, price)<br />
...<br />
apple 3<br />
pear 5<br />
orange 20</pre><br />
<br />
Hope this helps for the project!<br />
<br />
=== Programming style in scheme ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=2068 Source: Spring 2014 Piazza (2068)]<br />
<br />
Since Scheme has no rules on whitespace and indentation, you could technically write all your Scheme in one line like this:<br />
<pre>(define (fib n) (if (< n 1) n (+ (fib (- n 1)) (fib (- n 2)))))</pre><br />
<br />
But that would be terrible and everyone who had to read your code would hate you for it. Here is a more reasonable version:<br />
<pre>(define (fib n)<br />
(if (< n 1) ; Arguments to if are flush<br />
n ; Each argument gets a new line<br />
(+ (fib (- n 1)) ; Sometimes it makes to insert a newline<br />
(fib (- n 2))))) ; so that you can see arguments side by side</pre><br />
<br />
Remember that code is primarily for humans to read and incidentally for computers to run.<br />
<br />
Here are some more examples:<br />
<pre>(define (deep-map f lst)<br />
(cond ((null? lst) lst)<br />
((list? (car lst))<br />
(cons (deep-map f (car lst))<br />
(deep-map f (cdr lst))))<br />
(else<br />
(cons (f (car lst))<br />
(deep-map f (cdr lst)))) ))<br />
<br />
STk> (deep-map (lambda (x) (* x x)) (list 1 2 (list (list 3) 4)))<br />
(1 4 ((9) 16))</pre><br />
<br />
<pre>(define (reverse lst)<br />
(define (helper lst result)<br />
(if (null? lst)<br />
result<br />
(helper (cdr lst)<br />
(cons (car lst) result)) ))<br />
(helper lst ()) )<br />
<br />
STk> (reverse (list 1 2 3))<br />
(3 2 1)</pre><br />
<br />
=== ucb.py's <code>trace</code> method ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=3000 Source: Spring 2014 Piazza (3000)]<br />
<br />
'''Student Question'''<br />
<br />
How does the trace in ucb.py actually work?<br />
<br />
'''Student Answer'''<br />
<br />
It's actually very similar to the <code>printed</code> function that was defined in the Hog spec!<br />
<br />
<pre>def printed(fn):<br />
def print_and_return(*args):<br />
result = fn(*args)<br />
print('Result:', result)<br />
return result<br />
return print_and_return<br />
</pre><br />
<br />
The main idea is still the same in <code>trace</code> -- we want to figure out all the arguments and save the result of calling the function with those arguments (so we can print it before returning it). One (sort of) major improvement is <code>**kwds</code>. Just like how <code>*args</code> collected all the "positional arguments", <code>**kwds</code> captures all the "keyword arguments" (the ones of the form <code>param='some_val'</code>). This can be a bit confusing, but a couple of examples might help!<br />
<br />
<pre>&gt;&gt;&gt; def add_three(a, b, c):<br />
... return a + b + c<br />
&gt;&gt;&gt; add_three(1, 2, 3) # all arguments are positional (normal)<br />
&gt;&gt;&gt; add_three(1, 2, c=3) # a, b are positional arguments, c is a keyword argument<br />
<br />
&gt;&gt;&gt; def fn(*args, **kwargs):<br />
... print(args)<br />
... print(kwargs)<br />
&gt;&gt;&gt; fn(1, 2, 3)<br />
(1, 2, 3)<br />
{}<br />
&gt;&gt;&gt; fn(1, k=2)<br />
(1,)<br />
{'k' : 2}<br />
&gt;&gt;&gt; fn(a=1, b=2, c=3)<br />
()<br />
{'a':1, 'b':2, 'c':3}<br />
</pre><br />
<br />
Since there are only two types of arguments, having both <code>*args</code> and <code>**kwds</code> covers all our bases. If we passed <code>printed</code> a keyword argument, it could cause an error!<br />
<br />
Everything else in <code>trace</code> just makes the output prettier and more helpful. <code>trace</code> uses the <code>_PREFIX</code> global variable to keep track of how far to indent the next print statement. It catches exceptions and prints them out, before re-raising that exception. It also uses some Python black magic to figure out the name of the function so we can print <code>some_fn</code> instead of <code>&lt;function some_fn at 0x...&gt;</code>.<br />
<br />
If there's a particular aspect of <code>trace</code> that you're confused about, feel free to post a followup!<br />
<br />
== Debugging ==<br />
== Miscellaneous ==<br />
=== Andrew Huang's tips ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=779 Source: Spring 2014 Piazza (779)]<br />
<br />
Order of evaluation matters. The rules for evaluating call expressions are<br />
<br />
# Evaluate the operator<br />
# Evaluate the operands<br />
# Call the operator on the operands (and draw a new frame...)<br />
<br />
For example:<br />
<pre>def baz():<br />
print("this was first")<br />
def bar(x):<br />
print(x)<br />
return lambda x: x * x<br />
return bar # baz is a function that when called, returns a function named bar<br />
<br />
>>> baz() # the operator is baz, there are no operands<br />
this was first<br />
<function bar at 0x2797e20><br />
>>> baz()("this was second") # the operator is baz(), the operand is "this was second"<br />
this was first<br />
this was second<br />
<function <lambda> at 0x2120e20><br />
>>> baz()("this was second")(3) # the operator is baz()("this was second"), the operand is 3<br />
this was first<br />
this was second<br />
9<br />
>>> def bar(x):<br />
... print(x)<br />
... return 3<br />
... <br />
>>> baz()("this was second")(bar("this was third")) # the operator is baz()("this was second"), the operand is bar("this was third")<br />
this was first<br />
this was second<br />
this was third<br />
9</pre><br />
<br />
In order to solve any problem, you must first understand what the problem is asking. Often times it helps to try to explain it concisely in English. It also helps to come up with small examples. For example:<br />
<br />
<pre>def mouse(n):<br />
if n >= 10:<br />
squeak = n // 100<br />
n = frog(squeak) + n % 10<br />
return n<br />
<br />
def frog(croak):<br />
if croak == 0:<br />
return 1<br />
else:<br />
return 10 * mouse(croak+1)<br />
<br />
mouse(21023508479)</pre><br />
<br />
So the goal is to figure out what <code>mouse(21023508479)</code> evaluates to.<br />
<br />
One way is to just step-by-step evaluate this, as an interpreter would.<br />
<br />
Another way, is to understand what the functions are doing.<br />
<br />
Looking at <code>mouse</code>, we see that it takes in a number and outputs that same number if it is smaller than 10. otherwise, it'll return something weird. In order to understand that weird thing, we have to understand what <code>frog</code> is doing. <code>frog</code> takes in a number and if that number is <code>0</code>, return <code>1</code>. Otherwise, return ten times <code>mouse(croak+1)</code>. Well, this is still confusing. Let's try a small example.<br />
<pre>>>> mouse(357)<br />
47<br />
>>> mouse(123)<br />
23<br />
>>> mouse(1234)<br />
44<br />
>>> mouse(12345)<br />
245</pre><br />
There is a pattern. We notice that the resulting number is composed of every other digit of the original, plus one (except for the last one.)<br />
So <code>21023508479</code> is <code>[2+1][0+1][3+1][0+1][4+1][9] = 314159</code>. Can you see how the code reflects that?<br />
However in this particular example, the pattern is definitely tricky to find here, so it might make more sense to brute force it.<br />
<br />
<br />
Remember for recursion, you always need to find three things:<br />
* One or more base cases<br />
* One or more was to reduce the problem<br />
* A way to solve the problem given solutions to smaller problems<br />
For example, the discussion notes, we asked you to write count_stairs. This function takes in n, the number of steps, and returns all the ways you can climb up them if at each step, you can take either one or two steps.<br />
<br />
* Base cases: if we consider n to be the number of steps left to climb, then it makes sense that if there is 1 step left, then there is exactly one way. If there are two steps left, then there are exactly 2 ways (1 step, 1 step, or two steps). Why do we need two base cases here?<br />
* We can make the problem smaller by reducing the n. At each step, we can take one step (resulting in count_stairs(n-1)) or two steps (count_stairs(n-2)).<br />
* Assuming we get the solutions to the two recursive calls, we should add them together to get all the ways we can climb the stairs.<br />
<br />
Thus we end up with<br />
<pre>def count_stairs(n):<br />
if n <= 2:<br />
return n<br />
else:<br />
return count_stairs(n-1) + count_stars(n-2)</pre><br />
Notice that at each stair step, we either take one step or two steps. This is a common pattern in tree recursion. Look through Discussion 3 for more info.<br />
<br />
=== Y combinators (in Scheme) ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=2450 Source: Spring 2014 Piazza (2450)]<br />
<br />
'''Student Question'''<br />
<br />
Can someone explain this to me?<br />
<pre>scm> (((lambda (f) (lambda (x) (f f x)))<br />
(lambda (f k) (if (zero? k) 1 (* k (f f (- k 1)))))) 5)</pre><br />
I've edited the code as follows:<br />
<br />
<pre>(<br />
(<br />
(lambda (f) <br />
(lambda (x) (f f x))<br />
)<br />
(lambda (f k) <br />
(if (zero? k) 1 <br />
(* k (f f (- k 1)))<br />
)<br />
)<br />
) 5<br />
)</pre><br />
<br />
My understanding is that the second lambda function is passed as the first <code>f</code> in the first lambda function and the <code>5</code> is passed in as <code>x</code>. But does that mean <code>f f x</code> becomes the second lambda function with itself and <code>x</code> passed as the arguments to <code>(f k)</code>?<br />
<br />
'''Student Answer'''<br />
<br />
You're on the right track. The first lambda function is a higher order function that takes in a function, and then returns a function that takes one argument. It's actually the third lambda that is then passed into the first lambda (currying!) and then 5 is then passed into the resulting function.<br />
<br />
In case you're curious, this is the Python equivalent:<br />
<br />
<pre>>>> (lambda f: (lambda x: f(f, x))) (lambda f, k: 1 if k == 0 else (k * f(f, k - 1)))(5)</pre><br />
Which is then equivalent to:<br />
<br />
<pre>>>> def func1(f):<br />
def func2(x):<br />
return f(f, x)<br />
return func2<br />
>>> def func3(f, k):<br />
if k == 0:<br />
return 1<br />
else:<br />
return k * f(f, k - 1)<br />
>>> func1(func3)(5)<br />
120</pre><br />
By the way, this is just a fancy way of recursively calculating the factorial using only lambda functions. If you're still curious as to how this works, you could try this in Python tutor. Except I would recommend calculating 3! instead of 5, because it's a lot of frames.<br />
<br />
'''Instructor Answer'''<br />
<br />
Maybe it will look a little nicer in Python:<br />
<br />
<pre>(lambda f: lambda x: f(f, x))(lambda g, k: 1 if k == 0 else (k * g(g, k-1)))(5)</pre><br />
Or maybe not.<br />
<br />
So the idea is, you define a lambda function that takes a function <code>f</code>, and that returns a lambda function that takes an argument <code>x</code> and returns <code>f(f, x)</code>. Then, you call this lambda function you just defined on another lambda function (let's call this <code>func</code>) that takes a function g and another argument k, and is basically the factorial function. This first call returns the inner lambda of the first part, and when that's called with <code>5</code> you're essentially calling <code>func(func, 5)</code>. The chain of recursive calls then works as follows:<br />
<br />
<pre>func(func, 5) -> 5 * func(func, 4) -> 5 * 4 * func(func, 3) -> ... -> 120</pre><br />
In functional programming theory, this is known as a Y Combinator, and it is how you achieve recursion with just lambda functions. If you're wondering why we need <code>func</code> to take in a function as the first parameter, see what would happen if you took that part out!</div>Jeffreylu017https://www.ocf.berkeley.edu/~shidi/cs61a/wiki/GuidesGuides2014-06-21T02:14:03Z<p>Jeffreylu017: </p>
<hr />
<div>== Higher-order functions ==<br />
== Environment diagrams ==<br />
=== Environment diagram Rules ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=131 Source: Spring 2014 Piazza (131)]<br />
<br />
Environment Diagrams are very important in our understanding of how the computer interprets our code.<br />
<br />
'''We will test you on this in every exam.'''<br />
<br />
'''It will never go away.'''<br />
<br />
'''Given that, master it as quickly as you can! :)'''<br />
<br />
Below are the rules I follow when drawing environment diagrams. If you understand and faithfully follow these rules when drawing them, you'll never get them wrong.<br />
<br />
One thing you haven't learned yet is nonlocal. You can skip that particular step for now (step 2 of Assignment).<br />
<br />
Post here if you have any questions!<br />
<br />
You can also take a look at this link for some examples of environment diagrams: http://albertwu.org/cs61a/notes/environments<br />
<br />
For a different perspective on the rules, check out: http://markmiyashita.com/cs61a/sp14/environment_diagrams/rules_of_environment_diagrams/<br />
<br />
A handout with detailed instructions on drawing environment diagrams is also available here (linked on the bottom of the course homepage): http://inst.eecs.berkeley.edu/~cs61a/sp14/pdfs/environment-diagrams.pdf<br />
<br />
<pre>Environment Diagram Rules<br />
=========================<br />
<br />
Creating a Function<br />
--------------------<br />
1. Draw the func <name>(<arg1>, <arg2>, ...)<br />
2. The parent of the function is wherever the function was defined<br />
(the frame we're currently in, since we're creating the function).<br />
3. If we used def, make a binding of the name to the value in the current frame.<br />
<br />
Calling User Defined Functions<br />
------------------------------<br />
1. Evaluate the operator and operands.<br />
2. Create a new frame; the parent is whatever the operator s parent is.<br />
Now this is the current frame.<br />
3. Bind the formal parameters to the argument values (the evaluated operands).<br />
4. Evaluate the body of the operator in the context of this new frame.<br />
5. After evaluating the body, go back to the frame that called the function.<br />
<br />
Assignment<br />
----------<br />
1. Evaluate the expression to the right of the assignment operator (=).<br />
2. If nonlocal, find the frame that has the variable you re looking for,<br />
starting in the parent frame and ending just before the global frame (via<br />
Lookup rules). Otherwise, use the current frame. Note: If there are multiple<br />
frames that have the same variable, pick the frame closest to the current<br />
frame.<br />
3. Bind the variable name to the value of the expression in the identified<br />
frame. Be sure you override the variable name if it had a previous binding.<br />
<br />
Lookup<br />
------<br />
1. Start at the current frame. Is the variable in this frame?<br />
If yes, that's the answer.<br />
2. If it isn't, go to the parent frame and repeat 1.<br />
3. If you run out of frames (reach the Global frame and it's not there), complain.<br />
<br />
Tips<br />
----<br />
1. You can only bind names to values.<br />
No expressions (like 3+4) allowed on environment diagrams!<br />
2. Frames and Functions both have parents.</pre><br />
<br />
== Sequences ==<br />
=== Reversing tuples ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=639 Source: Spring 2014 Piazza (639)]<br />
<br />
'''Student Question'''<br />
<br />
Why does [::-1] tuple work while the tuple [0:3:-1] doesn't?<br />
<br />
I thought the -1 after the second semicolon meant that the interpreter is going to read the indexes "backwards". <br />
<br />
'''Student Answer'''<br />
<br />
The syntax of slicing is <tt>tup[start:end:step]</tt>:<br />
<ul><li>start from index <tt>start</tt> and end just before index <tt>end</tt>, incrementing the index by <tt>step</tt> each time<br />
</li><li>if no <tt>step</tt> is provided, <tt>step</tt> = 1<br />
</li><li>if <tt>step</tt> is positive, default values if not provided: <tt>start</tt> = 0, <tt>end</tt> = <tt>len(tup)</tt><br />
</li><li>if <tt>step</tt> is negative, default values if not provided: <tt>start</tt> = -1, <tt>end</tt> = one position before the start of the string<br />
</li></ul><br />
<br />
<pre>&gt;&gt;&gt; (1, 2, 3)[::-1] # start at index -1, end one position before the start of the string<br />
(3, 2, 1)<br />
&gt;&gt;&gt; (1, 2, 3)[0:3:-1] # start at 0 and go to 3, but step is negative, so this doesn't make sense and an empty tuple is returned<br />
()<br />
</pre><br />
<br />
This is a helpful visualization from http://en.wikibooks.org/wiki/Python_Programming/Strings#Indexing_and_Slicing:<br />
<blockquote><br />
To understand slices, it's easiest not to count the elements themselves. It is a bit like counting not on your fingers, but in the spaces between them. The list is indexed like this:<br />
<pre>Element: 1 2 3 4<br />
Index: 0 1 2 3 4<br />
-4 -3 -2 -1<br />
</pre><br />
</blockquote><br />
More info about slicing at http://stackoverflow.com/a/13005464/2460890.<br />
<br />
=== Slicing with negative step ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=702 Source: Spring 2014 Piazza (702)]<br />
<br />
'''Student Question'''<br />
<br />
if the third example returns an empty tuple because you can't take negative steps from 0 to 4, shouldn't the second example also return an empty tuple?<br />
<br />
Can someone explain why each example returns the respective answers?<br />
<br />
Thanks<br />
<br />
<pre>>>> x= (1,2,3,4)<br />
>>> x[0::-1]<br />
(1,)<br />
>>> x[::-1]<br />
(4, 3, 2, 1)<br />
>>> x[0:4:-1]<br />
()<br />
>>> x[1::-1]<br />
(2, 1)</pre><br />
<br />
'''Instructor Answer'''<br />
<br />
(For reference, the notation is <tt>x[start:end:step]</tt>)<br />
<br />
Python does something a very strange when the step is negative: if you omit the arguments to start and end, Python will fill them with what makes sense for a negative step. In the simple case of <tt>x[::-1]</tt>, Python fills in the start with <tt>len(x)-1</tt> and the end with <tt>-(len(x)+1)</tt>. The end term is strange, but remember that the end term isn't included. We therefore can't use 0, but we can't use -1 either, since that clearly refers to the last element of the tuple. We need to fully wrap the negative index around, to refer to the element "before" the 0th index. This way, Python will start at the end of the tuple and proceed to the beginning of the tuple.<br />
<br />
That's why <tt>x[0:4:-1]</tt> doesn't make sense: how can we start at 0 and end at 4, if we're proceeding backwards?<br />
<br />
And that's why <tt>x[0::-1]</tt> makes sense (albeit, in a strange way): Python is proceeding from the 0 index to the beginning of the list. It includes the start index, which is why you see a 1 pop up.<br />
<br />
Let me know if that was confusing!<br />
<br />
== Recursion ==<br />
== Data abstraction ==<br />
== Time complexity ==<br />
=== Andrew Huang's guide to order of growth and function runtime ===<br />
[https://docs.google.com/document/d/1TxfKmM3MlH032hjSUh92I0kQDVcvmitTSzYObGMr8Bk Source: Guide to Order of Growth and Function Runtime (Retrieved June 16th, 2014)]<br />
<br />
'''Introduction'''<br />
<br />
Confused by $O$, $\Omega$, and $\Theta$?<br />
<br />
Want to figure out the runtime of that tricky function?<br />
<br />
Read this.<br />
<br />
'''NOTE THAT THIS GUIDE STARTS WITH BIG O, WHICH IS DIFFERENT FROM THETA. IF YOU UNDERSTAND BIG O, THETA IS EASY (IN FACT, IT DEFINES THETA IN TERMS OF BIG O BELOW).'''<br />
<br />
First some math.<br />
<br />
Formal definition of O(Big O):<br />
<br />
Let $f(n)$ and $g(n)$ be functions from positive integers to positive reals. We say $f \in O(g)$ (“f grows no faster than g”) if there is a constant $0 < c < \inf$ <such that $f(n) \leq c \cdot g(n)$.<br />
<br />
(Paraphrased from Dasgupta, Papadimitriou, & Vazirani)<br />
<br />
(You'll see this again in CS 170)<br />
<br />
What the heck does that mean?<br />
<br />
Let’s look at math functions for a second (just a second).<br />
<br />
Say $f(n)=5n$ and $g(n)=n^{2}$<br />
<br />
What does that look like on a graph?<br />
<br />
http://www.wolframalpha.com/input/?i=plot+5n+and+n%5E2+from+0+to+10<br />
<br />
There’s a section where $n$ dominates $n^{2}$, from 0 to 5, but we don’t really care, because after that point, $n^{2}$ is larger, all the way to infinity! By the definition, we could scale $n^{2}$ by 5 and we would span that initial gap.<br />
<br />
Thus we can say $5n \in O(n^{2})$ or $f \in O(g)$.<br />
<br />
Can we say the converse? That is, is $n^{2} \in O(5n)$?<br />
<br />
Not at all! From the graph we see that $n^{2}$ grows too quickly for $n$ to catch up, no matter what constant we scale $n$ by.<br />
<br />
So what if $f(n)=n+1000$ and $g(n)=n^{2}$?<br />
<br />
It turns out $n+1000 \in O(n^{2})$ still, because according to the definition, as long as we can multiply $n^{2}$ by some $c$, such that the gap of 1000 is spanned, we’re good. In the case, $c=1001$.<br />
<br />
'''What about and $\Omega$ and $\Theta$?'''<br />
<br />
If you digested all of the above, the rest isn’t scary! (Note, $a \equiv b$ means $a$ is equivalent to $b$)<br />
<br />
$f \in \Omega(g) \equiv g \in O(f)$ (You'll see this again briefly in CS 170)<br />
<br />
$f \in \Theta(g) (f \in O(g) and g \in O(f))$<br />
<br />
'''This means that if $f$ is Theta of $g$, then there exist some $c_{1}$ and $c_{2}$ such that'''<br />
<br />
'''$c_{1}g > f$ and'''<br />
<br />
'''$c_{2}g < f$'''<br />
<br />
'''for all positive integers.'''<br />
<br />
'''What does that mean for Python functions?'''<br />
<br />
Given a function $f$, we want to find out how fast that function runs. One way of doing this is to take out a stopwatch, and clock the amount of time it takes for $f$ to run on some input. However, there are tons of problems with that (different computers => different speeds; only one fixed input? Maybe $f$ is really fast for that input but slow for everything else; next year, all the measurements need to be redone on new computers; etc.) Instead, we'll count the steps that a function needs to perform as a function of its input. For example, here are some of the functions that take one step regardless of their input:<br />
<br />
<code>mul</code><br />
<br />
<code>add</code><br />
<br />
<code>sub</code><br />
<br />
<code>print</code><br />
<br />
<code>return</code><br />
<br />
<code>...</code><br />
<br />
So for example, <code>(3 + 3 * 8) % 3</code> would be 3 steps--one for the multiply, one of the add, and one for the mod.<br />
<br />
Let's take a simple example:<br />
<pre>def square(x):<br />
return x * x</pre><br />
<code>square</code> is a function that for any input, always takes two steps, one of the multiplication, and one for returning. Using the notation, we can say square ∈ Θ(1).<br />
<br />
Functions with iteration (for loops, recursion, etc.), usually multiply the steps by some factor. For example, consider factorial:<br />
<pre>def factorial(n):<br />
if n == 0:<br />
return 1<br />
else:<br />
return n * factorial(n-1)</pre><br />
factorial ∈ Θ(n). Why? Well given some input <code>n</code>, we do <code>n</code> recursive calls. At each recursive call, we carry out 4 steps, one for if <code>n == 0</code>, one for subtraction, one for multiply, one for return. Plus, we have the base case, which is another 2 steps, one for if and one for return. So <code>factorial(n)</code> takes $4n+2$ steps => ∈ Θ(n).<br />
<br />
As mentioned, we care about how the running time (how long the function takes to run) of the function changes, '''as we increase the size of the argument'''. So if we imagine a graph, then the x-axis represents the size of our input, and the y-axis represents how long the function took to run for each x. As the size of the input increases, the function’s runtime does something on the graph. So when we say something like “$O(n^{2})$ where $n$ is the length of the list”, we are saying as we double the size of the list, the function is expected to run at most four times as long. '''NOTE ALSO THAT I SAID WHAT $n$ IS! ALWAYS GIVE YOUR UNITS.'''<br />
<br />
This means that when we compare two functions A and B, A may be overall slower than B as we increase the size of their arguments. However, it’s possible at some specific arguments, the A may run faster (like the $f(n)=5n$ and $g(n)=n^{2}$ example above.)<br />
<br />
'''This also means we do not care about the time taken of any particular input! This implies that all those constant-time base cases all those functions don’t really matter, because they don’t scale. That is, only one specific input causes the base case to be reached, and if we increased the size of the argument, $O(1)$ doesn't necessarily hold.'''<br />
<br />
'''Brief “What runs faster than what”'''<br />
<br />
Sorted from fastest to slowest. This is by no means comprehensive.<br />
* $\Theta(1)$<br />
* $\Theta(\log(n))$<br />
* $\Theta(n)$<br />
* $\Theta(n \log(n))$<br />
* $\Theta(n^{2})$<br />
* $\Theta(n^{3})$<br />
* $\Theta(2^{n})$<br />
* (Anything past this point is kind of ridiculous)<br />
* $\Theta(n!)$<br />
* $\Theta(n^{n})$<br />
<br />
'''So we know about the math and the motivation, now how do we actually assign runtimes to real Python functions?'''<br />
<br />
What you must understand, is that there is no one method for finding the runtime. You MUST look at a function holistically or you won’t get the right answer. What does this mean? '''In order to get the correct runtime, you first must understand what the function is doing! You cannot pattern-match your way to becoming good at this.'''<br />
<br />
'''This cannot be stressed enough: UNITS MATTER, if you say O((n)), you must tell us what $n$ is.'''<br />
<br />
General tips<br />
# UNDERSTAND WHAT THE FUNCTION IS DOING!!!<br />
# Try some sample input. That is, pretend you’re the interpreter and execute the code with some small inputs. What is the function doing with the input? Having concrete examples lets you do tip 1 better. You can also graph how the runtime increases as the argument size increases.<br />
# If applicable, draw a picture of the tree of function calls. This shows you the "growth" of the function or how the function is getting "bigger", which will help you do tip 1 better.<br />
# If applicable, draw a picture of how the input is being modified through the function calls. For example, if your input is a list and your function recursively does something to that list, draw out a list, then draw out parts of the list underneath it that are called during the recursion. Helps with tip 1.<br />
# See tip 1.<br />
<br />
Anyways, let's examine some common runtimes (keep scrolling). Remember, '''this is in no way a comprehensive list, NOR IS IT TRYING TO TEACH YOU HOW TO FIND THEM.''' This post is just to give you a starting point into orders of growth by showing you some examples and basic details about each runtime.<br />
<br />
'''Constant $\Theta(1)$'''<br />
<br />
''What it looks like:''<br />
<br />
http://www.wolframalpha.com/input/?i=plot+5<br />
<br />
''Example:''<br />
<br />
<pre>def add(x, y):<br />
return x + y</pre><br />
$add \in \Theta(1)$, where 1 is.. well a constant...<br />
<br />
''Approach:''<br />
<br />
The key behind constant time functions is that regardless of the size of the input, they always run the same number of instructions.<br />
<br />
''Don’t fall for this Trap:''<br />
<br />
<pre>def bar(n):<br />
if n % 7 == 0:<br />
return "Bzzst"<br />
else:<br />
return bar(n -1)</pre><br />
$\mathtt{bar} \in \Theta(1)$. Why?<br />
<br />
'''Logarithmic $\Theta(\log(n))$'''<br />
<br />
''What it looks like:''<br />
<br />
http://www.wolframalpha.com/input/?i=plot+4log3n+from+0+to+10<br />
<br />
''Example:''<br />
<br />
<pre>def binary_search(sorted_L, n):<br />
""" sorted_L is a list of numbers sorted from<br />
smallest to largest<br />
"""<br />
if sorted_L == []:<br />
return False<br />
mid_num = sorted_L[len(sorted_L) // 2]<br />
if n == mid_num:<br />
return True<br />
elif n < mid_num:<br />
return binary_search(sorted_L[:mid_num], n)<br />
else:<br />
return binary_search(sorted_L[mid_num:], n)</pre><br />
$\mathtt{binary\_search} \in \Theta(log(n))$, where $n$ is the number of elements in <code>sorted_L</code>.<br />
<br />
''Approach:''<br />
<br />
Logarithmic functions scale down the size of the problem by some constant every iteration (either with a recursive loop, a for loop, or a while loop). Also, logarithmic functions do not branch out--they generally do not make more than one call to themselves per recursion.<br />
<br />
'''Linear $\Theta(n)$'''<br />
<br />
''What it looks like:''<br />
<br />
http://www.wolframalpha.com/input/?i=plot+8n+from+0+to+10<br />
<br />
''Examples:''<br />
<br />
<pre>def sum_list(L):<br />
sum = 0<br />
for e in L:<br />
sum += e<br />
return sum</pre><br />
$\mathtt{sum\_list} \in \Theta(n)$, where $n$ is the number of elements in $L$.<br />
<br />
</pre>def countdown(n):<br />
if n > 0:<br />
print(n)<br />
countdown(n - 1)<br />
else:<br />
print("Blast off!")</pre><br />
$\mathtt{countdown} \in \Theta(n)$, where $n$ is n.<br />
<br />
''Approach:''<br />
<br />
Linear functions usually act on sequences or other collections of data. In that case, the function will go through the elements once or twice or ''k'' times, where $k<<n$. If the function acts on a number, the number usually gets smaller by a constant each iteration.<br />
<br />
Don't fall for this trap:<br />
<pre>def two_for_loops(n):<br />
for a in range(n):<br />
if n == 4:<br />
for y in range(n):<br />
print("Admiral Ackbar")<br />
else:<br />
print("It's a trap!")</pre><br />
$\mathtt{two\_for\_loops} \in \Theta(n)$, where $n$ is n. Why?<br />
<br />
'''Loglinear/Linearithmic $\Theta(n \log(n))$'''<br />
<br />
''What it looks like:''<br />
<br />
http://www.wolframalpha.com/input/?i=plot+nlog%28n%29+from+0+to+10<br />
<br />
''Example:''<br />
<br />
<pre>def merge(s1, s2):<br />
if len(s1) == 0:<br />
return s2<br />
elif len(s2) == 0:<br />
return s1<br />
elif s1[0] < s2[0]:<br />
return [s1[0]] + merge(s1[1:], s2)<br />
else:<br />
return [s2[0]] + merge(s1, s2[1:])<br />
<br />
def mergesort(lst):<br />
if len(lst) <= 1:<br />
return lst<br />
else:<br />
middle = len(lst) // 2<br />
return merge(mergesort(lst[:middle]), \<br />
mergesort(lst[middle:]))</pre><br />
$\mathtt{mergesort} \in \Theta(n \log(n))$, where $n$ is the number of elements in <code>lst</code>.<br />
<br />
Approach:<br />
These functions tend to make two recursive calls, each making the problem smaller by a half. There's a neat way to see this. For example in mergesort, start with an entire line, which represents mergesort called on the initial list. From there, the list gets split in half by the two recursive calls to mergesort in the code, so draw the another line right below the first, of the same length, but with a small gap in the middle to represent the split. Repeat until you're tired. At the end, you get a rectangle that's nwide and (n)tall!<br />
<pre><nowiki><br />
---------------<br />
------- -------<br />
--- --- --- ---<br />
- - - - - - - -<br />
</nowiki></pre><br />
The total area is the runtime, $\Theta(n \log(n))$<br />
<br />
''Don’t fall for this trap:''<br />
<br />
Don’t confuse functions that have an average running time of n(n)(like quicksort) with functions that are in (n(n))<br />
<br />
'''Polynomial $\Theta(n^{2})$,$\Theta(n^{3})$, etc.'''<br />
<br />
''What it looks like:''<br />
<br />
http://www.wolframalpha.com/input/?i=plot+n%5E2%2B3+from+0+to+10<br />
<br />
''Example:''<br />
<br />
<pre>def print_a_grid(n):<br />
for _ in range(n):<br />
for _ in range(n):<br />
print("+", end="")<br />
print("")</pre><br />
$\mathtt{print\_a\_grid} \in \Theta(n^{2})$, where $n$ is n.<br />
<br />
''Approach:''<br />
<br />
Polynomial functions will examine each element of an input many, many times, as opposed to linear functions, which examine some constant number of times.<br />
<br />
''Don’t fall into this trap:''<br />
<br />
Don’t get polynomial confused with exponential (below).<br />
<br />
'''Exponential'''<br />
<br />
''What it looks like:''<br />
<br />
http://www.wolframalpha.com/input/?i=plot+2%5En+from+0+to+10<br />
<br />
''Example:''<br />
<br />
<pre>(define (strange-add x)<br />
(if (zero? x)<br />
1<br />
(+ (strange-add (- x 1))<br />
(strange-add (- x 1)) )))<br />
<br />
def strange_add(x):<br />
if x == 0:<br />
return 1<br />
else:<br />
return strange_add(x - 1) + strange_add(x - 1)</pre><br />
$\mathtt{strange\_add} \in \Theta(2^{n})$, where $n$ is x.<br />
<br />
''Approach:''<br />
<br />
Exponential functions tend to branch out as you get deeper and deeper into their call tree, and each call only makes the work smaller by a little bit. For example, <code>(strange-add 8)</code> calls <code>(strange-add 7)</code> and <code>(strange-add 7)</code>. Those two calls each make two calls, <code>(strange-add 6)</code>, <code>(strange-add 6)</code>, <code>(strange-add 6)</code>, and <code>(strange-add 6)</code> respectively, and so on.<br />
<br />
== Mutability ==<br />
=== Michelle Chang's guide to immutability and mutability ===<br />
[https://d1b10bmlvqabco.cloudfront.net/attach/hoxc5uu6sud761/gozdkhgdUbT/htdlpko411i0/Python__Immutable_vs_Mutable.pdf Source: What You Should Know about Immutability vs Mutability]<br />
<br />
== Mutable data-structures ==<br />
== Object-oriented programming ==<br />
=== Inheritance and class vs instance attributes ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=1413 Source: Spring 2014 Piazza (1413)]<br />
<br />
'''Student Question'''<br />
<br />
I'm confused on how Classes and Inheritance work.<br />
<br />
If there's a Parent class and a Child class, when coding in the Child class, when do you write <code>Parent.attribute</code>, when do you write <code>Child.attribute</code>, and when do you write <code>self.attribute</code>?<br />
<br />
Also, I'm also confused as to when to put <code>self</code> into the parentheses as well.<br />
<br />
'''Instructor Answer'''<br />
<br />
<code>Parent.attribute</code> and <code>Child.attribute</code> would both be ways of accessing a'''class variable'''. These are variables that can be accessed without creating new '''instances''' of the that class.<br />
<br />
<code>self.attribute</code> would be used in '''methods''' to access an '''instance variable''' (an attribute specific to an instance).<br />
<br />
So for example, <code>Insect.watersafe</code> is <code>False</code>, but <code>Bee.watersafe</code> is <code>True</code>. These are class attributes because you don't have to create an Insect object or a Bee object in order to say <code>Insect.watersafe</code> or <code>Bee.watersafe</code>.<br />
<br />
However it wouldn't make any sense to say <code>Bee.armor</code>, since armor is an '''instance variable'''. You have to first create a new Bee before you could ask it for it's armor. If you created a second Bee after that, the second Bee would also have its own armor.<br />
<br />
There's a lot of vocab (in bold) that might trip you up. Try reading Discussion 6 and posting a followup if you're still unsure!<br />
<br />
== Iterables, iterators and generators ==<br />
== Python semantics and syntax ==<br />
== Scheme ==<br />
=== Scheme semantics and syntax ===<br />
----<br />
==== Difference between <code>eq?</code>, <code>eqv?</code>, <code>equal?</code> and <code>=</code> ====<br />
[http://stackoverflow.com/questions/16299246/what-is-the-difference-between-eq-eqv-equal-and-in-scheme/17719745#17719745 What is the difference between eq?, eqv?, equal?, and = in Scheme?]<br />
<br />
Let's start with the <code>=</code> equivalence predicate. The <code>=</code> predicate is used to check whether two numbers are equal. If you supply it anything else but a number then it will raise an error:<br />
<br />
<pre>(= 2 3) => #f<br />
(= 2.5 2.5) => #t<br />
(= '() '()) => error</pre><br />
The <code>eq?</code> predicate is used to check whether its two parameters respresent the same object in memory. For example:<br />
<br />
<code>(define x '(2 3))<br />
(define y '(2 3))<br />
(eq? x y) => #f<br />
(define y x)<br />
(eq? x y) => #t</code><br />
Note however that there's only one empty list <code>'()</code> in memory (actually the empty list doesn't exist in memory, but a pointer to the memory location <code>0</code> is considered as the empty list). Hence when comparing empty lists <code>eq?</code> will always return <code>#t</code> (because they represent the same object in memory):<br />
<br />
<pre>(define x '())<br />
(define y '())<br />
(eq? x y) => #t</pre><br />
Now depending upon the implementation <code>eq?</code> may or may not return <code>#t</code> for primitive values such as numbers, strings, etc. For example:<br />
<br />
(<pre>eq? 2 2) => depends upon the implementation<br />
(eq? "a" "a") => depends upon the implementation</pre><br />
This is where the <code>eqv?</code> predicate comes into picture. The <code>eqv?</code> is exactly the same as the <code>eq?</code> predicate, except that it will always return <code>#t</code> for same primitive values. For example:<br />
<br />
<pre>(eqv? 2 2) => #t<br />
(eqv? "a" "a") => #t</pre><br />
Hence <code>eqv?</code> is a superset of <code>eq?</code> and for most cases you should use <code>eqv?</code> instead of <code>eq?</code>.<br />
<br />
Finally we come to the <code>equal?</code> predicate. The <code>equal?</code> predicate is exactly the same as the <code>eqv?</code> predicate, except that it can also be used to test whether two lists, vectors, etc. have corresponding elements which satisfy the <code>eqv?</code> predicate. For example:<br />
<br />
<pre>(define x '(2 3))<br />
(define y '(2 3))<br />
(equal? x y) => #t<br />
(eqv? x y) => #f</pre><br />
In general:<br />
# Use the <code>=</code> predicate when you wish to test whether two numbers are equivalent.<br />
# Use the <code>eqv?</code> predicate when you wish to test whether two non-numeric values are equivalent.<br />
# Use the <code>equal?</code> predicate when you wish to test whether two lists, vectors, etc. are equivalent.<br />
# Don't use the <code>eq?</code> predicate unless you know exactly what you're doing.<br />
<br />
=== Scheme lists ===<br />
----<br />
==== Using <code>cons</code> ====<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=3004 Source: Spring 2014 Piazza (3004)]<br />
<br />
'''Student Question<br />
<br />
What's the difference between the following in Scheme?<br />
<br />
(cons 1 2)<br />
(cons 1 . 2)<br />
<br />
(cons 1 (cons 2 (cons 3 nil)))<br />
(cons 1 . (cons 2 . (cons 3 . 4)))<br />
<br />
Why does putting a dot before "(cons" cause it to be a malformed list? But when you put in (cons 1 2) it returns (1 . 2)? Is the dot something that only the interpreter returns, and that the user can't use in defining a list?<br />
<br />
'''Student Answer'''<br />
<br />
I struggled with this a bit as well. It helps to know the difference between a list containing a "." and one that doesn't. <br />
<br />
First off, scheme lists love to be recursive, kind of like rlists. That is, if you did cdr on the list, you would keep getting a list until you finally get an empty list/nil.<br />
<pre>STk&gt; (define a '(1 2 3))<br />
a<br />
STk&gt; a<br />
(1 2 3)<br />
STk&gt; (cdr a)<br />
(2 3)<br />
STk&gt; (cddr a)<br />
(3)<br />
STk&gt; (cdddr a)<br />
()</pre><br />
Note how each call of cdr returns a list. Even (3) is a list. It is just a list containing one member. So what happens with something like (1 2 . 3)?<br />
<pre>STk&gt; (define a '(1 2 . 3))<br />
a<br />
STk&gt; a<br />
(1 2 . 3)<br />
STk&gt; (cdr a)<br />
(2 . 3)<br />
STk&gt; (cddr a)<br />
3</pre><br />
Notice how the last <code>cdr</code> returns a simple 3. Running <code>cdddr</code> would throw an error, because the list stops at 3. A list containing a "." is known as an <em>improper </em>list.<br />
<br />
As for the ".", you won't be using it unless you use it in combination with a quote, otherwise it will always return a malformed list and throw an error. For example:<br />
<pre>STk&gt; (define a (1 2 . 3))<br />
*** Error:<br />
eval: malformed list: (1 2 . 3)<br />
Current eval stack:<br />
__________________<br />
0 (1 2 . 3)<br />
1 (define a (1 2 . 3))<br />
STk&gt; (define a '(1 2 . 3))<br />
a</pre><br />
Basically, the "." is seen in output, but not input. The only exception is the quote. Think of scheme as having two stages. First, it interprets your commands to construct the lists etc. Next, it will simplify the expression. Think of the quote as skipping straight to the second stage.<br />
<br />
Finally, the only way to get the standard lists is to end the list with nil or to use the "list" function (or to use a quote). If the list doesn't end with nil, then it will become an improper list. That is why you <em>can't</em> do things like <code>(1 . 2 . 3 . 4)</code> to make <code>(1 2 3 4)</code>. In order to form a "proper" list, each element must be represented by a list. You <em>can</em> do <code>(1 . (2 . (3 . (4))))</code> because you are treating each element like a list. When in doubt, just test some output:<br />
<pre>STk&gt; (cons 1 2)<br />
(1 . 2) ; doesn't end in an empty list/nil<br />
STk&gt; (cons 1 (cons 2 '()))<br />
(1 2)<br />
STk&gt; (cons 1 (cons 2 (3)))<br />
; ERROR<br />
STk&gt; (cons 1 (cons 2 (list 3)))<br />
(1 2 3) ; lists are formed with an nil at the end, so this works<br />
STk&gt; '(1 . (2 . (3 . 4)))<br />
(1 2 3 . 4)<br />
STk&gt; '(1 . (2 . (3 . (4)))<br />
(1 2 3 4)<br />
STk&gt; '(1 (2 . 3) 4)<br />
(1 (2 . 3) 4) ; still works, but this has 3 elements: (1), (2 . 3), and (4)<br />
STk&gt; (cons 1 (cons (cons 2 3) (cons 4 nil))) <br />
(1 (2 . 3) 4) ; equivalent to above, without quote</pre><br />
<br />
==== <code>append</code> vs <code>cons</code> vs <code>list</code> ====<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=2067 Source: Spring 2014 Piazza (2067)]<br />
<br />
This post isn't meant to be comprehensive. '''Ask questions in lab or as a followup here if you're confused.'''<br />
One of its major flaws is that it doesn't cover box and pointers. '''LEARN BOX AND POINTERS. '''<br />
<br />
Here is a beautiful web based Scheme interpreter that will draw box and pointer diagrams for you. Run through the examples below with this thing:<br />
http://xuanji.appspot.com/js-scheme-stk/index.html<br />
<br />
In order to understand these three procedures, you first have to understand a little about Pairs and Lists.<br />
<br />
'''Pairs''' are data structures that have two slots. You can put different stuff in these slots, like numbers or words or sentences or booleans--pretty much anything. You make a pair using cons.<br />
<pre>STk> (cons 'foo 'bar)<br />
(foo . bar)<br />
<br />
STk> (cons 1 'ring)<br />
(1 . ring)<br />
<br />
STk> (cons (+ 1 2 3) (member? 3 '(the 3 stooges)))<br />
(6 . #t)</pre><br />
<br />
In order to get stuff from a pair that you have made, you use <code>car</code> and <code>cdr</code>. <code>car</code> gets the thing in the first slot. <code>cdr</code> gets the thing in the second slot.<br />
<br />
<pre>STk> (define foo (cons 'x 'y))<br />
foo<br />
<br />
STk> foo<br />
(x . y)<br />
<br />
STk> (car foo)<br />
x<br />
<br />
STk> (cdr foo)<br />
y</pre><br />
That was straightforward. Now for the trippy part:<br />
'''You can put pairs inside of pairs:'''<br />
<pre>STk> (define foo (cons (cons 3 4) 5))<br />
foo<br />
<br />
STk> foo<br />
((3 . 4) . 5)<br />
<br />
STk> (car foo)<br />
(3 . 4)<br />
<br />
STk> (car (car foo))<br />
3<br />
<br />
STk> (caar foo) ; functionally equivalent as above.<br />
3<br />
<br />
STk> (cdr foo)<br />
5<br />
<br />
STk> (cdr (car foo))<br />
4<br />
<br />
STk> (cdar foo) ; functionally equivalent as above.<br />
4</pre> <br />
<br />
There's a certain style of pair nesting that is especially useful&mdash;'''Lists.'''<br />
<br />
Each list has these properties:<br />
* Every list is a pair or the empty list (denoted by '() or nil).<br />
* The car of a nonempty list is some item.<br />
* The cdr of a nonempty list must be another list.<br />
<pre>STk> (cons 1 (cons 2 (cons 3 '()))) ; list of numbers<br />
(1 2 3)<br />
<br />
STk> (define stooges (cons 'larry (cons 'curly (cons 'moe nil))))<br />
stooges<br />
<br />
STk> stooges<br />
(larry curly moe)<br />
<br />
STk> (car stooges)<br />
larry<br />
<br />
STk> (cdr stooges) ; Calling cdr on a non-empty list gives you another list!<br />
(curly moe)<br />
<br />
STk> (cadr stooges)<br />
curly<br />
<br />
STk> (cdar stooges) ; Why does this break?<br />
*** Error:<br />
cdar: bad list: (moe larry curly)<br />
Current eval stack:<br />
__________________<br />
0 (cdar stooges)<br />
<br />
STk> (define not-a-list (cons 'foo (cons 'bar 'baz))) ; This is not a list.<br />
not-a-list<br />
<br />
STk> not-a-list ; What property does this break?</pre><br />
Notice how Scheme knew that we were making lists. Before we had <code>parens</code> and periods which organized our items. Scheme now recognizes that we're making a list and does away with the periods and some of the <code>parens</code>.<br />
<br />
If you stare a bit at the list rules above, you can notice we used a recursive definition to define lists. Recursion... '''on data'''!<br />
<br />
Let's talk about <code>list</code>. <code>list</code> takes a bunch of stuff and makes a list out of them. The stuff can be anything. Words, numbers, pairs, other lists. <code>list</code> doesn't care. [picture of a honey badger]<br />
<pre>STk> (list 'foo 'bar' 'baz) ; Lists takes anything and makes a list out of it.<br />
(foo bar baz)<br />
<br />
STk> (list 'foo ((lambda (x) (+ x 4)) 8) #f (cons 1 (cons 3 4)) (cons 1 (cons 2 nil)) (list 1 2 3)) ; ANYTHING <br />
(foo 12 #f (1 3 . 4) (1 2) (1 2 3))<br />
<br />
STk> (list 'x 'y 'z)<br />
(x y z)<br />
<br />
STk> '(x y z) ; Sometimes you can get away with using quote to make literal lists. Yes, sentences are secretly lists.<br />
(x y z)</pre><br />
Now we can talk about <code>append</code>:<br />
<pre>STk> (append '(a b c) '(d e f) '(g h i)) ; Append takes in lists and appends them together.<br />
(a b c d e f g h i)<br />
<br />
STk> (append 'foo '(1 2 3)) ; foo is not a list. Stuff will break.<br />
*** Error:<br />
append: argument is not a list: foo<br />
Current eval stack:<br />
__________________<br />
0 (append (quote foo) (quote (1 2 3)))</pre><br />
You know that <code>cons</code> makes a pair. You also know that you can make a list out of pairs. You can abuse <code>cons</code> for your own maniacal purposes.<br />
<pre>STk> (cons 'joe stooges) ; Put stuff at the beginning of a list!<br />
(joe larry curly moe)</pre><br />
'''The following only applies to the STk interpreter.'''<br />
<pre>STk> (append '(1 2 3) 'foo) ; Wait... what?<br />
(1 2 3 . foo)<br />
<br />
STk> (append '(1 2 3) (cons 4 5)) ; The plot thickens!<br />
(1 2 3 4 . 5)<br />
<br />
STk> (append stooges 'shemp) ; You should really figure out why this works.<br />
(larry curly moe . shemp)</pre><br />
<br />
To summarize:<br />
* append takes in lists and outputs a big list.<br />
* cons takes in things and makes a pair out of them. However, we know that lists are made of pairs, so we can throw together a list if we use cons a certain way<br />
* list takes in things and makes a list out of those things, regardless of what they are.<br />
<br />
=== Tail recursion ===<br />
----<br />
==== Mark Miyashita's guide on tail recursion ====<br />
[http://markmiyashita.com/cs61a/sp14/tail_recursion/tail_recursion_and_tail_optimized_calls/ Source: Tail Recursion and Tail Optimized Calls]<br />
<br />
First off, I think this is an excellent article to read about tail recursion and tail calls in Python: [http://paulbutler.org/archives/tail-recursion-in-python/ here]<br />
<br />
Basically, you can write tail recursive functions in any language. Tail recursion, in one sentence, is where you return the answer in the final frame instead of following the frames back up to the original frame. For example, we have factorial which is normally not tail recursive:<br />
<br />
<pre>def factorial(n):<br />
if n == 1:<br />
return 1<br />
return n * factorial(n - 1)</pre><br />
<br />
because it needs to keep track of the <code>n *</code> at each level of recursion.<br />
<br />
The following implementation of <code>factorial</code>, is tail recursive because at the end of the last frame, it can return the answer, instead of going back up through all the frames to multiply and compute the answer:<br />
<br />
<pre>def factorial(n):<br />
def helper(n, total):<br />
if n == 1:<br />
return total<br />
return helper(n - 1, total * n)<br />
return helper(n, 1)</pre><br />
<br />
You can’t have tail optimized calls in Python – at least, not like the code that we defined above. You can define your own sort of tail optimized way of evaluating the functions by using lambdas, and I believe the article linked at the top of this post goes into detail about how to implement this if you are interested. In Scheme, the language detects when you have something like the helper function in the example above where your return statement consists of only the recursive call. In the first example, we have the <code>n *</code> and the recursive call which means it cannot be tail optimized because it needs to keep track of all of the frames that it creates. In a tail optimized call, Scheme will get rid of the frames that are no longer necessary.<br />
<br />
'''tl;dr''' – Tail recursion can be done in any language where the basic idea is that you return the answer in the final frame of recursion. Tail optimized calls are a Scheme (and some other languages, not including Python) feature where it will get rid of the frames above, if certain conditions are met – such as where the return statement is only the recursive call and nothing else. The cases in which Scheme uses a tail optimized call are located on the lecture slides located [http://www-inst.eecs.berkeley.edu/~cs61a/sp13/slides/35-TailCalls_6pp.pdf here].<br />
<br />
==== Tail recursion in Python ====<br />
[http://kylem.net/programming/tailcall.html Source: http://kylem.net/programming/tailcall.html (Retrieved June 16th, 2014)]<br />
<br />
In this page, we’re going to look at tail call recursion and see how to force Python to let us eliminate tail calls by using a trampoline. We will go through two iterations of the design: first to get it to work, and second to try to make the syntax seem reasonable. I would not consider this a useful technique in itself, but I do think it’s a good example which shows off some of the power of decorators.<br />
<br />
The first thing we should be clear about is the definition of a tail call. The “call” part means that we are considering function calls, and the “tail” part means that, of those, we are considering calls which are the last thing a function does before it returns. In the following example, the recursive call to f is a tail call (the use of the variable <code>ret</code> is immaterial because it just connects the result of the call to <code>f</code> to the return statement), and the call to <code>g</code> is not a tail call because the operation of adding one is done after <code>g</code> returns (so it’s not in “tail position”).<br />
<br />
<pre>def f(n) :<br />
if n > 0 :<br />
n -= 1<br />
ret = f(n)<br />
return ret<br />
else :<br />
ret = g(n)<br />
return ret + 1</pre><br />
<br />
'''1. Why tail calls matter'''<br />
<br />
Recursive tail calls can be replaced by jumps. This is called “tail call eliminination,” and is a transformation that can help limit the maximum stack depth used by a recursive function, with the benefit of reducing memory traffic by not having to allocate stack frames. Sometimes, recursive function which wouldn’t ordinarily be able to run due to stack overflow are transformed into function which can.<br />
<br />
Because of the benefits, some compilers (like <code>gcc</code>) perform tail call elimination[1], replacing recursive tail calls with jumps (and, depending on the language and circumstances, tail calls to other functions can sometimes be replaced with stack massaging and a jump). In the following example, we will eliminate the tail calls in a piece of code which does a binary search. It has two recursive tail calls.<br />
<br />
<pre>def binary_search(x, lst, low=None, high=None) :<br />
if low == None : low = 0<br />
if high == None : high = len(lst)-1<br />
mid = low + (high - low) // 2<br />
if low > high :<br />
return None<br />
elif lst[mid] == x :<br />
return mid<br />
elif lst[mid] > x :<br />
return binary_search(x, lst, low, mid-1)<br />
else :<br />
return binary_search(x, lst, mid+1, high)</pre><br />
<br />
Supposing Python had a <code>goto</code> statement, we could replace the tail calls with a jump to the beginning of the function, modifying the arguments at the call sites appropriately:<br />
<br />
<pre>def binary_search(x, lst, low=None, high=None) :<br />
start:<br />
if low == None : low = 0<br />
if high == None : high = len(lst)-1<br />
mid = low + (high - low) // 2<br />
if low > high :<br />
return None<br />
elif lst[mid] == x :<br />
return mid<br />
elif lst[mid] > x :<br />
(x, lst, low, high) = (x, lst, low, mid-1)<br />
goto start<br />
else :<br />
(x, lst, low, high) = (x, lst, mid+1, high)<br />
goto start</pre><br />
<br />
which, one can observe, can be written in actual Python as<br />
<br />
<pre>def binary_search(x, lst, low=None, high=None) :<br />
if low == None : low = 0<br />
if high == None : high = len(lst)-1<br />
while True :<br />
mid = low + (high - low) // 2<br />
if low > high :<br />
return None<br />
elif lst[mid] == x :<br />
return mid<br />
elif lst[mid] > x :<br />
high = mid - 1<br />
else :<br />
low = mid + 1</pre><br />
<br />
I haven’t tested the speed difference between this iterative version and the original recursive version, but I would expect it to be quite a bit faster because of there being much, much less memory traffic.<br />
<br />
Unfortunately, the transformation makes it harder to prove the binary search is correct in the resulting code. With the original recursive algorithm, it is almost trivial by induction.<br />
<br />
Programming languages like Scheme depend on tail calls being eliminated for control flow, and it’s also necessary for continuation passing style.[2]<br />
<br />
'''2. A first attempt'''<br />
<br />
Our running example is going to be the factorial function (a classic), written with an accumulator argument so that its recursive call is a tail call:<br />
<br />
<pre>def fact(n, r=1) :<br />
if n <= 1 :<br />
return r<br />
else :<br />
return fact(n-1, n*r)</pre><br />
<br />
If <code>n</code> is too large, then this recursive function will overflow the stack, despite the fact that Python can deal with really big integers. On my machine, it can compute <code>fact(999)</code>, but <code>fact(1000)</code> results in a sad <code>RuntimeError: Maximum recursion depth exceeded</code>.<br />
<br />
One solution is to modify fact to return objects which represent tail calls and then to build a trampoline underneath fact which executes these tail calls after fact returns. This way, the stack depth will only contain two stack frames: one for the trampoline and another for each call to fact.<br />
<br />
First, we define a tail call object which reifies the concept of a tail call:<br />
<br />
<pre>class TailCall(object) :<br />
def __init__(self, call, *args, **kwargs) :<br />
self.call = call<br />
self.args = args<br />
self.kwargs = kwargs<br />
def handle(self) :<br />
return self.call(*self.args, **self.kwargs)</pre><br />
<br />
This is basically just the thunk <code>lambda : call(*args, **kwargs)</code>, but we don’t use a thunk because we would like to be able to differentiate between a tail call and returning a function as a value.<br />
<br />
The next ingredient is a function which wraps a trampoline around an arbitrary function:<br />
<br />
<pre>def t(f) :<br />
def _f(*args, **kwargs) :<br />
ret = f(*args, **kwargs)<br />
while type(ret) is TailCall :<br />
ret = ret.handle()<br />
return ret<br />
return _f</pre><br />
<br />
Then, we modify fact to be<br />
<br />
<pre>def fact(n, r=1) :<br />
if n <= 1 :<br />
return r<br />
else :<br />
return TailCall(fact, n-1, n*r)</pre><br />
<br />
Now, instead of calling <code>fact(n)</code>, we must instead invoke <code>t(fact)(n)</code> (otherwise we’d just get a TailCall object).<br />
<br />
This isn’t that bad: we can get tail calls of arbitrary depth, and it’s Pythonic in the sense that the user must explicitly label the tail calls, limiting the amount of unexpected magic. But, can we eliminate the need to wrap t around the initial call? I myself find it unclean to have to write that <code>t</code> because it makes calling fact different from calling a normal function (which is how it was before the transformation).<br />
<br />
'''3. A second attempt'''<br />
<br />
The basic idea is that we will redefine fact to roughly be <code>t(fact)</code>. It’s tempting to just use <code>t</code> as a decorator:<br />
<br />
<pre>@t<br />
def fact(n, r=1) :<br />
if n <= 1 :<br />
return r<br />
else :<br />
return TailCall(fact, n-1, n*r)</pre><br />
<br />
(which, if you aren’t familiar with decorator syntax, is equivalent to writing <code>fact = t(fact)</code> right after the function definition). However, there is a problem with this in that the fact in the returned tail call is bound to <code>t(fact)</code>, so the trampoline will recursively call the trampoline, completely defeating the purpose of our work. In fact, the situation is now worse than before: on my machine, <code>fact(333)</code> causes a <code>RuntimeError</code>!<br />
<br />
For this solution, the first ingredient is the following class, which defines the trampoline as before, but wraps it in a new type so we can distinguish a trampolined function from a plain old function:<br />
<br />
<pre>class TailCaller(object) :<br />
def __init__(self, f) :<br />
self.f = f<br />
def __call__(self, *args, **kwargs) :<br />
ret = self.f(*args, **kwargs)<br />
while type(ret) is TailCall :<br />
ret = ret.handle()<br />
return ret</pre><br />
<br />
and then we modify <code>TailCall</code> to be aware of <code>TailCallers</code>:<br />
<br />
<pre>class TailCall(object) :<br />
def __init__(self, call, *args, **kwargs) :<br />
self.call = call<br />
self.args = args<br />
self.kwargs = kwargs<br />
def handle(self) :<br />
if type(self.call) is TailCaller :<br />
return self.call.f(*self.args, **self.kwargs)<br />
else :<br />
return self.call(*self.args, **self.kwargs)</pre><br />
<br />
Since classes are function-like and return their constructed object, we can just decorate our factorial function with <code>TailCaller</code>:<br />
<br />
<pre>@TailCaller<br />
def fact(n, r=1) :<br />
if n <= 1 :<br />
return r<br />
else :<br />
return TailCall(fact, n-1, n*r)</pre><br />
<br />
And then we can call fact directly with large numbers!<br />
<br />
Also, unlike in the first attempt, we can now have mutually recursive functions which all perform tail calls. The first-called <code>TailCall</code> object will handle all the trampolining.<br />
<br />
If we wanted, we could also define the following function to make the argument lists for tail calls be more consistent with those for normal function calls:[3]<br />
<br />
<pre>def tailcall(f) :<br />
def _f(*args, **kwargs) :<br />
return TailCall(f, *args, **kwargs)<br />
return _f</pre><br />
<br />
and then fact could be rewritten as<br />
<br />
<pre>@TailCaller<br />
def fact(n, r=1) :<br />
if n <= 1 :<br />
return r<br />
else :<br />
return tailcall(fact)(n-1, n*r)</pre><br />
<br />
One would hope that marking the tail calls manually could just be done away with, but I can’t think of any way to detect whether a call is a tail call without inspecting the source code. Perhaps an idea for further work is to convince Guido von Rossum that Python should support tail recursion (which is quite unlikely to happen).<br />
<br />
[1] This is compiler-writer speak. For some reason, “elimination” is what you do when you replace a computation with something equivalent. In this case, it’s true that the call is being eliminated, but in its place there’s a jump. The same is true for “common subexpression elimination” (known as CSE), which takes, for instance,<br />
<br />
<pre>a = b + c<br />
d = (b + c) + e<br />
and replaces it with<br />
a = b + c<br />
d = a + e</pre><br />
<br />
Sure, the <code>b+c</code> is eliminated from the second statement, but it’s not really gone...<br />
The optimization known as “dead code elimination” actually eliminates something, but that’s because dead code has no effect, and so it can be removed (that is, be replaced with nothing).<br />
<br />
[2] In Scheme, all loops are written as recursive functions since tail calls are the pure way of redefining variables (this is the same technique Haskell uses). For instance, to print the numbers from 1 to 100, you’d write<br />
<br />
<pre>(let next ((n 1))<br />
(if (<= n 100)<br />
(begin<br />
(display n)<br />
(newline)<br />
(next (+ n 1)))))</pre><br />
<br />
where next is bound to be a one-argument function which takes one argument, <code>n</code>, and which has the body of the <code>let</code> statement as its body. If that <code>100</code> were some arbitrarily large number, the tail call to next had better be handled as a jump, otherwise the stack would overflow! And there’s no other reasonable way to write such a loop!<br />
<br />
Continuation passing style is commonly used to handle exceptions and backtracking. You write functions of the form<br />
<br />
<pre>(define (f cont)<br />
(let ((cont2 (lambda ... (cont ...) ...)))<br />
(g cont2)))</pre><br />
<br />
along with functions which take multiple such f’s and combines them into another function which also takes a single cont argument. I’ll probably talk about this more in another page, but for now notice how the call to g is in the tail position.<br />
<br />
[3] This is basically a curried[4] version of <code>TailCall</code>.<br />
<br />
[4] That is, ''Schönfinkelized''.<br />
<br />
=== Miscellaneous ===<br />
----<br />
==== Useful Scheme Procedures ====<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=2226 Source: Spring 2014 Piazza (2226)]<br />
<br />
Here is a short list of Scheme procedures that you might use in writing your programs:<br />
<pre>; define - defines a variable or a procedure<br />
(define my-variable 4)<br />
(define (square x)<br />
(* x x))<br />
<br />
; if - conditional branching akin to if ... else<br />
(define (fib n)<br />
(if (< n 2)<br />
n<br />
(+ (fib (- n 1)) (fib (- n 2))) ))<br />
<br />
; conditional branching akin to if ... elif ... elif ... else<br />
(define (deep-map f lst)<br />
(cond ((null? lst) lst)<br />
((list? (car lst))<br />
(cons (deep-map f (car lst))<br />
(deep-map f (cdr lst))))<br />
(else<br />
(cons (f (car lst))<br />
(deep-map f (cdr lst)))) ))<br />
<br />
; and - outputs the the rightmost value if all of the arguments evaluate to #t. Outputs #f otherwise.<br />
STk> (and 0 1 2 3)<br />
3<br />
<br />
; or - outputs the the first value that evaluates to #t. Outputs #f otherwise.<br />
STk> (or 0 1 2 3)<br />
0<br />
<br />
; equal - tests if symbols are the same<br />
STk> (equal? 'foo 'bar)<br />
#f<br />
STk> (equal? 'foo 'foo)<br />
#t<br />
<br />
STk> (list? 'foo)<br />
#f<br />
STk> (list? '(1 2 3))<br />
#t<br />
STk> (list? '())<br />
#t<br />
<br />
STk> (null? '(1 2 3))<br />
#f<br />
STk> (null? ())<br />
#t<br />
<br />
; member? - tests if a symbol is in a list EDIT: NOT BUILT IN (BUT SUPER USEFUL SEE FOLLOWUP)<br />
STk> (member? 'quick '(the quick brown fox jumped over the lazy dog))<br />
#t<br />
<br />
; number? - checks if input is a number<br />
STk> (number? 42)<br />
#t<br />
STk> (number? #t)<br />
#f<br />
<br />
; remainder - computes the remainder of the first number divided by the second<br />
STk> (remainder 100 21)<br />
16</pre><br />
<br />
== Streams ==<br />
== Logic ==<br />
=== Quick Guide to Logic Programming ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=2524 Source: Spring 2014 Piazza (2524)]<br />
''Note: Someone should convert this from scmlog to Logic notation''<br />
<br />
'''Here's something I wrote a long time ago. The logic interpreter scmlog still exists and you should be able to access it using your cs61a-xx account. Post a followup if you have any questions. Hope this helps!'''<br />
<br />
<br />
'''Introduction:''' logic programming is a completely different way to think about telling computers to do stuff. Instead of telling the computer what to compute, you give the computer facts and ask it questions. The computer does its own thinking given the facts and the question, and then returns an answer.<br />
<br />
'''Table of Contents'''<br />
- Introduction<br />
- What is Logic Programming<br />
- - Giving Facts and Asking Questions<br />
- - More Complicated Facts and Questions<br />
- How to write Logic Programs<br />
- - Common Pitfalls<br />
- More Resources<br />
<br />
<br />
'''What is Logic Programming?'''<br />
Logic Programming is a way to ask the computer questions and get answers without telling it explicitly how to reach the conclusion. Kind of like this: http://youtu.be/tpKx7Oi0oeM<br />
<br />
Anyways, we have a logic programming interpreter called Scmlog. It interprets a simpler version of the logic programming language, Prolog. The first few things to understand about it is that '''SCMLOG IS NOT SCHEME'''. It just happens to look like it. Scmlog is its own language and so it has its own rules. You can't program in Scmlog like you would in Scheme or Python, so it might be good to forget what you know about those languages for a second.<br />
<br />
When you fire up scmlog (type scmlog into a terminal on the school computers), you get this prompt:<br />
<pre>star [501] ~ # scmlog<br />
scmlog (Prolog in Scheme), v. 0.2<br />
Type 'help' for help; 'quit' to exit.<br />
?- </pre><br />
You can interact with this prompt in two ways:<br />
# Give facts - "Let's tell the computer some things it should know!"<br />
# Ask questions - "Let's ask the computer questions about the things we told it about!"<br />
<br />
'''Giving Facts and Asking Questions'''<br />
A basic fact takes this form: <code>(fact (assertion))</code>, '''Where each assertion is simply a relation between things.'''<br />
<br />
For example: <code>(fact (likes potstickers brian))</code> relates three ideas, liking something, potstickers, and some guy name Brian. "Brian likes potstickers."<br />
Note that we put the relation first, and then the parties the relation acts upon. In this example, "likes" is the relation, it hooks up "brian" and "potstickers". Here's some more:<br />
<pre>?- (fact (likes potstickers brian)) <br />
?- (fact (likes potstickers andrew)) <br />
?- (fact (likes the_beatles brian)) <br />
?- (fact (likes the_beatles andrew)) <br />
?- (fact (likes led_zeppelin andrew)) <br />
?- (fact (dislikes led_zeppelin brian))</pre><br />
Now that we've given the computer a bunch of facts, how do we ask questions about them? Just replace "fact" with a "?", and replace any part of the relation (besides the relation itself) with a variable prefixed by an underscore. This is called "querying":<br />
<pre>?- (? (likes _what brian))<br />
_what : potstickers<br />
More? <br />
_what : the_beatles<br />
More? <br />
?- (? (likes potstickers _who))<br />
_who : brian<br />
More? <br />
_who : andrew<br />
More? <br />
?- (? (dislikes _what brian))<br />
_what : led_zeppelin<br />
More?<br />
?- (? (likes led_zeppelin andrew))<br />
Yes.<br />
?- (? (dislikes the_beatles _who))<br />
No.</pre><br />
Notice:<br />
* We can query any part of the assertion, besides the relation itself (can't replace "likes" with a variable).<br />
* All possible answers to the question show up.<br />
* If Scmlog couldn't find a fact that matched your query, it'll say "No."<br />
* Asking a question without any variables essentially asks if that fact exists. To which Scmlog will answer "Yes." or "No."<br />
<br />
'''More Complicated Facts and Questions'''<br />
Now this isn't the whole picture. We also have the ability to make more powerful assertions via variables, hypotheses, and conclusions:<br />
<br />
<code>(fact (ancestor _x _y) (parent _x _y))</code> "X is an ancestor of Y if X is a parent of Y"<br />
<br />
Here _x and _y are variables like usual. However, we have two parts to this fact, the conclusion ("X is an ancestor of Y") and the hypothesis ("X is a parent of Y"). We can have more than one hypotheses, and they can be any kind of query. Here's ancestor in action:<br />
<pre>?- (fact (parent george paul))<br />
?- (fact (parent martin george))<br />
?- (fact (parent martin martin_jr))<br />
?- (fact (parent martin donald))<br />
?- (fact (parent george ann))<br />
?- (fact (ancestor _X _Y) (parent _X _Y))<br />
?- (fact (ancestor _X _Y) (parent _X _Z) (ancestor _Z _Y))<br />
?- (? (ancestor paul george))<br />
No.<br />
?- (? (ancestor george paul))<br />
Yes.<br />
?- (? (ancestor george george))<br />
No.<br />
?- (? (ancestor martin paul))<br />
Yes.</pre><br />
Now there's one more thing you need to know about Scmlog. Scmlog knows about pairs and lists:<br />
<pre>?- (fact (lst (1 2 3)))<br />
?- (? (lst _x))<br />
_x : (1 2 3)<br />
More?<br />
?- (? (lst (1 . _x)))<br />
_x : (2 3)<br />
More? <br />
?- (? (lst (1 2 . _x)))<br />
_x : (3)<br />
More? <br />
?- (? (lst (_x . _y)))<br />
_x : 1<br />
_y : (2 3)<br />
?- (fact (my_pair (2 . 3)))<br />
?- (? (my_pair _y))<br />
_y : (2 . 3)<br />
?- (? (my_pair (2 . _x)))<br />
_x : 3</pre><br />
<br />
'''How to write Logic Programs'''<br />
<br />
The trick behind writing logic programs is to forget everything you know about programming. You instead want to focus on the relation you're trying to establish. Take append for example:<br />
<pre>?- (fact (append () _b _b))<br />
?- (fact (append (_x . _rest) _b (_x . _z)) (append _rest _b _z))<br />
?- (? (append (1 2 3) (3 2 1) _answer))<br />
_answer : (1 2 3 3 2 1)<br />
?- (append (1 2) 3 (1 2 3))<br />
Huh?<br />
?- (? (append (1 2) 3 (1 2 3))<br />
)<br />
No.<br />
?- (? (append (1 2) (3) (1 2 3))<br />
)<br />
Yes.<br />
?- (? (append (1 2) (3) (1 2 3)))<br />
Yes.<br />
?- (? (append _x (3 4 9) (1 0 3 2 3 4 9)))<br />
_x : (1 0 3 2)<br />
More? <br />
?- (? (append _x _y (2 1)))<br />
_x : ()<br />
_y : (2 1)<br />
More? <br />
_x : (2)<br />
_y : (1)<br />
More? <br />
_x : (2 1)<br />
_y : ()</pre><br />
<br />
What is append? Well the first fact establishes that if we append an empty list to something, the result is that something.<br />
The next fact is the meat of the code. You can almost think of this as a recursive relation. Any append relation between some three lists a b c must also fulfill another append relation between the rest of a, b, and the rest of c, respectively. Think about why that will always be true for all inputs. If the example above doesn't make sense to you, see the following:<br />
<br />
<br />
'''Common Pitfalls'''<br />
<br />
'''- One of the biggest issues people have with writing logic programs is that they don't realize that the variables don't work like they do in Scheme or Python. All Scmlog ever does is pattern match:'''<br />
<pre>?- (fact (x (0 1 8)))<br />
?- (fact (x (1 8 4)))<br />
?- (fact (x (9 4 4)))<br />
?- (fact (x (3 0 8)))<br />
?- (? (x _y))<br />
_y : (0 1 8)<br />
More? <br />
_y : (1 8 4)<br />
More? <br />
_y : (9 4 4)<br />
More? <br />
_y : (3 0 8)<br />
?- (? (x (0 1 . _z)))<br />
_z : (8)</pre><br />
<br />
- SCMLOG IS NOT SCHEME. SCMLOG IS NOT PYTHON.<br />
<br />
More Resources:<br />
* http://www-inst.eecs.berkeley.edu/~cs61a/sp12/lectures/prolog/ReadMe<br />
* http://www-inst.eecs.berkeley.edu/~cs61a/sp12/lectures/prolog/<br />
* http://www-inst.eecs.berkeley.edu/~cs61a/sp12/discussion/week14/<br />
* http://www-inst.eecs.berkeley.edu/~cs61a/fa13/slides/31-Logic_6pp.pdf<br />
<br />
=== Logic Mathematics ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=3050 Source: Spring 2014 Piazza (3050)]<br />
<br />
'''Student Question'''<br />
<br />
Can someone explain the intuition behind the implementation of the increments and the addition facts? The following is from Mark's website<br />
<br />
<pre>(fact (increment 0 1))<br />
(fact (increment 1 2))<br />
(fact (increment 2 3))<br />
(fact (increment 3 4))<br />
(fact (increment 4 5))<br />
(fact (increment 5 6))<br />
(fact (increment 6 7))<br />
(fact (increment 7 8))<br />
(fact (increment 8 9))<br />
(fact (increment 9 10))<br />
(fact (increment 10 11))<br />
(fact (increment 11 12))<br />
(fact (increment 12 13))<br />
<br />
(fact (add 1 ?x ?x+1)<br />
(increment ?x ?x+1))<br />
<br />
(fact (add ?x+1 ?y ?z+1)<br />
(increment ?x ?x+1)<br />
(increment ?z ?z+1)<br />
(add ?x ?y ?z))<br />
<br />
(query (add 2 4 6))<br />
; expect Success!</pre><br />
<br />
'''Student Answer'''<br />
<br />
First, we state a bunch of facts that denote relations between a number and the number that follows it (e.g. 1 and 2, 2 and 3, and so on). This is the increment fact.<br />
<br />
Then, we state a "base" fact, which is:<br />
<pre>(fact (add 1 ?x ?x+1)<br />
(increment ?x ?x+1))</pre><br />
In English, this fact states that some value <code>?x</code> added to 1 will give us some value <code>?x+1</code> (<code>x+1</code> is a valid variable name in Logic) '''if and only if''' the fact (<code>increment ?x ?x+1</code>) is true. As an example, <code>(add 1 2 3)</code> is true '''if and only if''' (<code>increment 2 3</code>) is true. This is true because of the increment facts we stated before.<br />
<br />
Now, to deal with additions that aren't just the sum of two numbers in which one number is a 1, we need:<br />
<pre>(fact (add ?x+1 ?y ?z+1)<br />
(increment ?x ?x+1)<br />
(increment ?z ?z+1)<br />
(add ?x ?y ?z))</pre><br />
I will try to give an intuition as to what is happening here.<br />
<br />
We state our fact: that two numbers, <code>?x+1</code> and <code>?y</code>, will add up to some number <code>?z+1</code> if and only if the following 3 hypotheses are true:<br />
* (<code>increment ?x ?x+1</code>) - "There exists some number <code>?x</code> that is 1 less than <code>?x+1</code>, and"<br />
* (<code>increment ?z ?z+1</code>) - "There exists some number <code>?z</code> that is 1 less than <code>?z+1</code>, and"<br />
* (<code>add ?x ?y ?z</code>) - "The numbers <code>?x</code> and <code>?y</code> will add up to <code>?z</code>."<br />
<br />
Consider the example of:<br />
<pre>(query (add 2 4 6))</pre><br />
Here's an idea of what's happening when Logic tries to match the query with the facts you've stated.<br />
* ?x+1 = 2, ?y = 4, ?z+1 = 6<br />
* It finds a match for <code>?x = 1</code>, since (<code>increment ?x ?x+1</code>) gives <code>?x = 1</code> because <code>?x+1 = 2</code><br />
* It finds a match for <code>?z = 5</code>, since (<code>increment ?z ?z+1</code>) gives <code>?z = 5</code> because <code>?z+1 = 6</code><br />
* It then checks for a match for (<code>add ?x ?y ?z</code>) which in this case is (<code>add 1 4 5</code>). This goes to our first "base" fact for add. (<code>add 1 4 5</code>) is a success because (<code>increment 4 5</code>) is a true fact [refer again to the "base fact" to see why this is the case]. This is also where we would get a "Failed.", if it turns out that (<code>increment ?x ?x+1</code>) wasn't actually true!<br />
* Hence, all 3 of our hypotheses are true, and so (<code>query (add 2 4 6)</code>) is a success!<br />
<br />
In this example, we only have to recurse once to get to our "base" fact. In other examples, where <code>?x+1</code> is not 2, but some number greater, such as 5, we will have to recurse 4 whole times to get to 1, at which point our "base" fact is reached. <br />
<br />
This recursion is similar to this idea in mathematical equations:<br />
<pre>x + y = z</pre><br />
is the same as<br />
<pre>(x - 1) + y = (z - 1)</pre><br />
is the same as<br />
<br />
<pre>(x - 2) + y = (z - 2)</pre?<br />
and so on... In Logic, we stop when we find that the first term (x) is 1, and then we use our increment facts to determine if the original statement is true, because all of these equations are equivalent.<br />
<br />
== Python syntax and semantics ==<br />
=== <code>print</code> vs <code>return</code> ===<br />
----<br />
==== Andrew's tips ====<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=779 Source: Spring 2014 Piazza (779)]<br />
<br />
Remember the differences between return and print.<br />
* <code>return</code> can only be used in a <code>def</code> statement. It returns a value from a function. Once Python evaluates a <code>return</code> statement, it immediately exits the function.<br />
* <code>print</code> is a function that displays its argument on the screen. It always returns <code>None</code>.<br />
<br />
Examples:<br />
<pre>def foo1(x):<br />
return x<br />
<br />
def foo2(x):<br />
print(x)<br />
<br />
>>> foo2(1) # In foo2, we print 1 ourselves using the print function<br />
1<br />
>>> foo1(1) # HERE, THE PYTHON INTERPRETER PRINTS THE RETURN VALUE OF FOO1. CANNOT STRESS HOW IMPORTANT TO UNDERSTAND THIS<br />
1<br />
>>> foo1(1) + 1<br />
2<br />
>>> foo2(1) + 1<br />
1<br />
Traceback (most recent call last):<br />
File "<stdin>", line 1, in <module><br />
TypeError: unsupported operand type(s) for +: 'NoneType' and 'int'</pre><br />
<br />
=== Function decorators ===<br />
<br />
----<br />
<br />
==== How function decorators work ====<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=638 Source: Spring 2014 Piazza (638)]<br />
<br />
'''Student Question'''<br />
<br />
I'm having difficulties understanding what exactly a function decorator is. Can someone elaborate and potentially provide me with an example other than the one in the readings?<br />
<br />
'''Instructor Answer'''<br />
<br />
So imagine you wanted your functions to print their arguments before they executed them. Here's one way to do this.<br />
<pre>def loud(fn):<br />
def new_fn(*args):<br />
print(args)<br />
return fn(*args)<br />
return new_fn </pre><br />
Here's a function loud that takes in a function and returns a new function that when called, prints out its arguments, and then does what the old function does.<br />
<br />
For example:<br />
<pre>def sq(x):<br />
return x * x<br />
>>> sq(4)<br />
16<br />
>>> sq = loud(sq) # replace the old square with our loud one.<br />
>>> sq(4)<br />
(4,)<br />
16</pre><br />
A function decorator does the same thing as the above. Assuming loud is defined, we can do this:<br />
<pre>@loud<br />
def sq(x):<br />
return x * x<br />
<br />
>>> sq(4)<br />
(4,)</pre><br />
<br />
== Student guides ==<br />
=== How to learn computer science ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=241 Source: Spring 2014 Piazza (241)]<br />
<br />
If you've never programmed before, or if you've never taken a class quite like 61A before, things right now might be scary. Everything is strange and new and there quite a lot to take in all at once. So if you're having a hard time so far, here are a few articles that might help.<br />
<br />
Note: these articles are pretty long, so feel free to read them in multiple sittings.<br />
<br />
'''At the beginning, everything seems a bit scary in CS'''. Michelle Bu, a Berkeley alum and a crazy good hacker, shares one of her experiences when she was a wee n00b in [http://blog.michellebu.com/2013/03/21-nested-callbacks/ 21 Nested Callbacks].<br />
<br />
'''Start here!''' [http://www.jamesmaa.com/2013/08/26/a-beginners-guide-to-computer-science/ "A Beginner's Guide to Computer Science"] Written by Berkeley's own James Maa. James is known for his killer walkthroughs (check out his Productivity guide). This article gives you some background on learning CS and then provides a practical guide on how to learn effectively.<br />
<br />
'''How do we learn?''' Mark Eichenlaub explains in this [http://www.quora.com/Learning/Do-grad-school-students-remember-everything-they-were-taught-in-college-all-the-time/answer/Mark-Eichenlaub Introduction to Learning Theory]. This is quite possibly the best introduction to Learning Theory.<br />
<br />
'''Sometimes, you're stuck and you end up really, really frustrated.''' Marcus Geduld explains [http://www.quora.com/Why-do-we-get-frustrated-when-learning-something/answer/Marcus-Geduld Why do we get frustrated when learning something?]<br />
<br />
=== Quick guide on getting unstuck ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=1264 Source: Quick Guide on Getting Unstuck (Retrieved June 16th, 2014)]<br />
<br />
A major frustration you might encounter in 61A is when you stare at a homework problem and have no idea where to start. Or you write some code and it doesn't pass the doctests, but now what? You work at it for a while, but next thing you know, you've been stuck for hours on the the same problem and have little to show about it.<br />
<br />
So here's a checklist of things you can do when you're stuck. Experienced programmers do these things almost naturally (because of how much practice they've had being stuck), and so while they get stuck just as much as your or I, they always know what to do next.<br />
<br />
# Do I understand what the problem is asking?<br />
## If not, which part of the problem is confusing me?<br />
### Identify the exact sentences/phrases/words/etc.<br />
## Check the given examples. Do they make sense to me?<br />
## Can I come up with my own examples? '''A good indicator that you understand the question is that you can come up with some nontrivial examples of how the function works.'''<br />
# What concepts should I use here?<br />
## Do I understand the concepts? '''Can I explain the concept in English to one of my friends such that they get it?'''<br />
### If not, go back and relearn the specific concepts that are unclear (through discussion, lab, lecture, etc.) Don't read the entire book in order to solve one problem..<br />
## How do I apply the concept to the given problem?<br />
# Write your code and test it.<br />
## Use doctests, '''BUT ALSO LOAD IT INTERACTIVELY (python3 -i ...)'''<br />
### '''Saying "my function works because the doctests pass" is a lot like saying "this airplane will fly because it has wings."'''<br />
## If your code breaks, ask yourself:<br />
### Does it error? Is it a....<br />
#### Syntax error? If so, find the syntax bug and fix it.<br />
#### Logic error? Is it something weird that you don't understand? (E.g. cannot add integer and tuple)<br />
### Why did it do that? Why didn't it do what I expected? Trace through the code by hand with an example (sample values) you came up with in step 0. '''Add calls to <code>print</code> in order to figure out how your function is handling the arguments.'''<br />
# Am I missing a trick?<br />
## Oftentimes you've never seen this type of problem before. This is expected on homework (and this is why homework can take a long time) because if you see it on the homework, then you will be familiar with it on the exam and when you program for fun and profit. <br />
## The key here is just to learn the trick however you need to.<br />
### Stare at it yourself<br />
### Stare at it with others (peers in the class)<br />
### Ask on PIazza what the approach is.<br />
### Stare at it with the TAs/lab Assistants<br />
## '''Once you figure it out, remember the trick so that you can use it next time.'''<br />
# At any point you identify what you're stuck on, you can begin to resolve it.<br />
## Use the tips above. Try things out on the interpreter. Review the lecture/discussion/labs/etc. Do whatever helps you get a better understanding of the problem.<br />
## Once you have something specific that you're stuck on, you can ask other people in the class.<br />
### '''Don't be afraid to ask. Everyone gets stuck and feels stupid sometimes. However, you get to choose how you react to it.'''<br />
### '''At the same time, it really helps to work with people who are on about the same level in the course.'''<br />
## Look on Piazza. Ask questions if yours hasn't come up yet. Be that awesome guy/girl who helps answer questions.<br />
## You can ask the TA if all else fails. We are here to help you learn!<br />
<br />
Here is an old algorithm for studying for tests (the final in this case), salvaged from the sands of time:<br />
<pre>For each topic on the final, find problems on them and do them.<br />
If you can solve them on your own, move on.<br />
Else if you are stuck, look at the solution and figure out if you<br />
are missing a trick or if you do not understand the concepts.<br />
If the problem is that you are stuck on some random trick,<br />
just learn the trick.<br />
Stare at the solutions, ask Piazza, your TA, etc.<br />
Questions you should ask at this stage:<br />
What is the problem asking me to do?<br />
How was I suppose to follow the instructions<br />
to solve the problem?<br />
What part of the problem do I not understand?<br />
What is the fastest way to clear up that misunderstanding?<br />
Then if you think you are still stuck conceptually, review<br />
and learn the concept, however you learn best.<br />
Suggestions for picking up concepts quickly (~1-2 hours):<br />
Discussion notes typically have a very concise recap of the<br />
thing they are going over.<br />
There are guides for particularly tricky things on Piazza,<br />
like Logic, Pairs and Lists in Scheme, etc.<br />
Find them and go over them.<br />
Ask a TA: "what is the best way to learn X?"<br />
If these do not work and you are still shaky after an hour<br />
or two, it might be worth watching a lecture or reading<br />
the notes.</pre><br />
<br />
== Composition ==<br />
=== General style guidelines from 61A website ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=149 Source: Spring 2014 Piazza (149)]<br />
<br />
'''Student Question'''<br />
<br />
Are we required to add any comments to our code to say what a function does, etc.? And does clarity of code count for this project, in which case should we write comments at the end of not-so-clear statements? Thanks.<br />
<br />
'''Student Answer'''<br />
<br />
Docstrings of each function are already provided. If you add a helper function, you should write a docstring for it.<br />
<br />
The [http://inst.eecs.berkeley.edu/~cs61a/sp14/style_guide.html#comments style guide on the course website] advises: "Your actual code should be self-documenting -- try to make it as obvious as possible what you are doing without resorting to comments. Only use comments if something is not obvious or needs to be explicitly emphasized"<br />
<br />
'''Instructor Answer'''<br />
<br />
You should always aim to make your code "self-documenting," meaning it is clear what your code is doing without the aid of comments. You should try to keep the number of comments to a minimum, but if there are lines which you think are unclear/ambiguous, feel free to add a comment.<br />
<br />
All projects in this class contain a 3 point component that is judged solely on your code "composition" -- i.e. whether your code is clear, concise, and easy to read.<br />
<br />
=== Simplifying code ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=1116 Source: Spring 2014 Piazza (1116)]<br />
<br />
Hi everyone, here's some tips about certain functions in Python that can greatly simplify your code for the Trends project.<br />
<br />
'''Sorting keys'''<br />
<br />
You should be familiar with the max and min functions in python, which can take in many arguments and return the maximum value.<br />
<br />
<pre>>>> max(1,3,2)<br />
3</pre><br />
<br />
These functions can also take in lists:<br />
<br />
<pre>>>> min([1,5,1,6])<br />
1</pre><br />
<br />
(In fact they can take in any ''iterable'' and return the maximum/minimum value)<br />
<br />
These functions work because Python knows how to compare the elements in the list (they are all integers). But what if the elements in the list are not integers? Fortunately, there is a way for you to tell Python how to turn each element of the list into a number that it can understand.<br />
<br />
Lets start with an example. Lets say you have a list of strings, and want to find the shortest string in the list. Here's what you can do:<br />
<br />
<pre>>>> min(['hihi', 'bye', 'a', 'zebra'], key=len)<br />
'a'</pre><br />
<br />
Notice the new keyword argument key we are passing into the min function. key is a function that min applies to each element of the list. In this case, the key is the len function, which returns the length of each string. Applying the key function to each element will return a cooresponding integer, which Python can easily use to find the minimum element.<br />
<br />
You can also use keys in the <code>sorted</code> function too, which returns a sorted list of its inputs, based on the key function passed in.<br />
<br />
<pre>>>> sorted(['hihi', 'bye', 'a', 'zebra'], key=len)<br />
['a', 'bye', 'hihi', 'zebra']</pre><br />
<br />
We can have more complex key functions. Here we sort a list of people by their age, which is the second element in the tuple. A key function, once defined, works for sorted, min and max:<br />
<br />
<pre>>>> names = [('Alice', 19, 'F'), ('Bob', 5, 'M'), ('Charlie', 12, 'M')]<br />
>>> get_age = lambda name: name[1]<br />
>>> sorted(names, key=get_age)<br />
[('Bob', 5, 'M'), ('Charlie', 12, 'M'), ('Alice', 19, 'F')]<br />
>>> max(names, key=get_age)<br />
('Alice', 19, 'F')</pre><br />
<br />
'''Dictionary default values'''<br />
<br />
Suppose we have a dictionary mapping names to counts:<br />
<br />
<pre>>>> d = {'apples': 1, 'pears': 9000}</pre><br />
<br />
If we want to add a new pear to the dictionary, we can use:<br />
<br />
<pre>>>> d['pears'] = d['pears'] + 1<br />
>>> d<br />
{'apples': 1, 'pears': 9001}</pre><br />
However we cannot use the same code to add a new item that is not already in the dictionary.<br />
<br />
<pre>>>> d['oranges'] = d['oranges'] + 1<br />
Traceback (most recent call last):<br />
...<br />
KeyError: 'oranges'</pre><br />
<br />
To solve this problem, we have to use <code>dict.setdefault(key, default)</code>. If <code>key</code> is in <code>dict</code>, it will return <code>dict[key]</code>. If not, it will insert <code>key</code> with a value of <code>default</code> and return <code>default</code>. Now we can write:<br />
<br />
<pre>>>> d['oranges'] = d.setdefault('oranges', 0) + 1<br />
>>> d<br />
{'oranges': 1, 'apples': 1, 'pears': 9001}<br />
>>> d['oranges'] = d.setdefault('oranges', 0) + 1<br />
>>> d<br />
{'oranges': 2, 'apples': 1, 'pears': 9001}</pre><br />
<br />
There's actually a even better way of doing this. If you are curious to find out, look up <code>collections.defaultdict</code>.<br />
<br />
'''For loops'''<br />
<br />
If you are iterating through a list and want to get both the item and the index the item is at, the built-in function enumerate is helpful here.<br />
<br />
<pre>>>> a = ["apple", "pear", "orange"]<br />
>>> for index, fruit in enumerate(a):<br />
... print(index, fruit)<br />
...<br />
0 apple<br />
1 pear<br />
2 orange</pre><br />
<br />
You can iterate through each key-value pair in a dictionary with dictionary.items. This is useful if you want to access both the key and the value at the same time.<br />
<br />
<pre>>>> prices = {"apple": 3, "pear": 5, "orange": 20}<br />
>>> for fruit, price in prices.items():<br />
... print(fruit, price)<br />
...<br />
apple 3<br />
pear 5<br />
orange 20</pre><br />
<br />
Hope this helps for the project!<br />
<br />
=== Programming style in scheme ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=2068 Source: Spring 2014 Piazza (2068)]<br />
<br />
Since Scheme has no rules on whitespace and indentation, you could technically write all your Scheme in one line like this:<br />
<pre>(define (fib n) (if (< n 1) n (+ (fib (- n 1)) (fib (- n 2)))))</pre><br />
<br />
But that would be terrible and everyone who had to read your code would hate you for it. Here is a more reasonable version:<br />
<pre>(define (fib n)<br />
(if (< n 1) ; Arguments to if are flush<br />
n ; Each argument gets a new line<br />
(+ (fib (- n 1)) ; Sometimes it makes to insert a newline<br />
(fib (- n 2))))) ; so that you can see arguments side by side</pre><br />
<br />
Remember that code is primarily for humans to read and incidentally for computers to run.<br />
<br />
Here are some more examples:<br />
<pre>(define (deep-map f lst)<br />
(cond ((null? lst) lst)<br />
((list? (car lst))<br />
(cons (deep-map f (car lst))<br />
(deep-map f (cdr lst))))<br />
(else<br />
(cons (f (car lst))<br />
(deep-map f (cdr lst)))) ))<br />
<br />
STk> (deep-map (lambda (x) (* x x)) (list 1 2 (list (list 3) 4)))<br />
(1 4 ((9) 16))</pre><br />
<br />
<pre>(define (reverse lst)<br />
(define (helper lst result)<br />
(if (null? lst)<br />
result<br />
(helper (cdr lst)<br />
(cons (car lst) result)) ))<br />
(helper lst ()) )<br />
<br />
STk> (reverse (list 1 2 3))<br />
(3 2 1)</pre><br />
<br />
=== ucb.py's <code>trace</code> method ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=3000 Source: Spring 2014 Piazza (3000)]<br />
<br />
'''Student Question'''<br />
<br />
How does the trace in ucb.py actually work?<br />
<br />
'''Student Answer'''<br />
<br />
It's actually very similar to the <code>printed</code> function that was defined in the Hog spec!<br />
<br />
<pre>def printed(fn):<br />
def print_and_return(*args):<br />
result = fn(*args)<br />
print('Result:', result)<br />
return result<br />
return print_and_return<br />
</pre><br />
<br />
The main idea is still the same in <code>trace</code> -- we want to figure out all the arguments and save the result of calling the function with those arguments (so we can print it before returning it). One (sort of) major improvement is <code>**kwds</code>. Just like how <code>*args</code> collected all the "positional arguments", <code>**kwds</code> captures all the "keyword arguments" (the ones of the form <code>param='some_val'</code>). This can be a bit confusing, but a couple of examples might help!<br />
<br />
<pre>&gt;&gt;&gt; def add_three(a, b, c):<br />
... return a + b + c<br />
&gt;&gt;&gt; add_three(1, 2, 3) # all arguments are positional (normal)<br />
&gt;&gt;&gt; add_three(1, 2, c=3) # a, b are positional arguments, c is a keyword argument<br />
<br />
&gt;&gt;&gt; def fn(*args, **kwargs):<br />
... print(args)<br />
... print(kwargs)<br />
&gt;&gt;&gt; fn(1, 2, 3)<br />
(1, 2, 3)<br />
{}<br />
&gt;&gt;&gt; fn(1, k=2)<br />
(1,)<br />
{'k' : 2}<br />
&gt;&gt;&gt; fn(a=1, b=2, c=3)<br />
()<br />
{'a':1, 'b':2, 'c':3}<br />
</pre><br />
<br />
Since there are only two types of arguments, having both <code>*args</code> and <code>**kwds</code> covers all our bases. If we passed <code>printed</code> a keyword argument, it could cause an error!<br />
<br />
Everything else in <code>trace</code> just makes the output prettier and more helpful. <code>trace</code> uses the <code>_PREFIX</code> global variable to keep track of how far to indent the next print statement. It catches exceptions and prints them out, before re-raising that exception. It also uses some Python black magic to figure out the name of the function so we can print <code>some_fn</code> instead of <code>&lt;function some_fn at 0x...&gt;</code>.<br />
<br />
If there's a particular aspect of <code>trace</code> that you're confused about, feel free to post a followup!<br />
<br />
== Debugging ==<br />
== Miscellaneous ==<br />
=== Andrew Huang's tips ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=779 Source: Spring 2014 Piazza (779)]<br />
<br />
Order of evaluation matters. The rules for evaluating call expressions are<br />
<br />
# Evaluate the operator<br />
# Evaluate the operands<br />
# Call the operator on the operands (and draw a new frame...)<br />
<br />
For example:<br />
<pre>def baz():<br />
print("this was first")<br />
def bar(x):<br />
print(x)<br />
return lambda x: x * x<br />
return bar # baz is a function that when called, returns a function named bar<br />
<br />
>>> baz() # the operator is baz, there are no operands<br />
this was first<br />
<function bar at 0x2797e20><br />
>>> baz()("this was second") # the operator is baz(), the operand is "this was second"<br />
this was first<br />
this was second<br />
<function <lambda> at 0x2120e20><br />
>>> baz()("this was second")(3) # the operator is baz()("this was second"), the operand is 3<br />
this was first<br />
this was second<br />
9<br />
>>> def bar(x):<br />
... print(x)<br />
... return 3<br />
... <br />
>>> baz()("this was second")(bar("this was third")) # the operator is baz()("this was second"), the operand is bar("this was third")<br />
this was first<br />
this was second<br />
this was third<br />
9</pre><br />
<br />
In order to solve any problem, you must first understand what the problem is asking. Often times it helps to try to explain it concisely in English. It also helps to come up with small examples. For example:<br />
<br />
<pre>def mouse(n):<br />
if n >= 10:<br />
squeak = n // 100<br />
n = frog(squeak) + n % 10<br />
return n<br />
<br />
def frog(croak):<br />
if croak == 0:<br />
return 1<br />
else:<br />
return 10 * mouse(croak+1)<br />
<br />
mouse(21023508479)</pre><br />
<br />
So the goal is to figure out what <code>mouse(21023508479)</code> evaluates to.<br />
<br />
One way is to just step-by-step evaluate this, as an interpreter would.<br />
<br />
Another way, is to understand what the functions are doing.<br />
<br />
Looking at <code>mouse</code>, we see that it takes in a number and outputs that same number if it is smaller than 10. otherwise, it'll return something weird. In order to understand that weird thing, we have to understand what <code>frog</code> is doing. <code>frog</code> takes in a number and if that number is <code>0</code>, return <code>1</code>. Otherwise, return ten times <code>mouse(croak+1)</code>. Well, this is still confusing. Let's try a small example.<br />
<pre>>>> mouse(357)<br />
47<br />
>>> mouse(123)<br />
23<br />
>>> mouse(1234)<br />
44<br />
>>> mouse(12345)<br />
245</pre><br />
There is a pattern. We notice that the resulting number is composed of every other digit of the original, plus one (except for the last one.)<br />
So <code>21023508479</code> is <code>[2+1][0+1][3+1][0+1][4+1][9] = 314159</code>. Can you see how the code reflects that?<br />
However in this particular example, the pattern is definitely tricky to find here, so it might make more sense to brute force it.<br />
<br />
<br />
Remember for recursion, you always need to find three things:<br />
* One or more base cases<br />
* One or more was to reduce the problem<br />
* A way to solve the problem given solutions to smaller problems<br />
For example, the discussion notes, we asked you to write count_stairs. This function takes in n, the number of steps, and returns all the ways you can climb up them if at each step, you can take either one or two steps.<br />
<br />
* Base cases: if we consider n to be the number of steps left to climb, then it makes sense that if there is 1 step left, then there is exactly one way. If there are two steps left, then there are exactly 2 ways (1 step, 1 step, or two steps). Why do we need two base cases here?<br />
* We can make the problem smaller by reducing the n. At each step, we can take one step (resulting in count_stairs(n-1)) or two steps (count_stairs(n-2)).<br />
* Assuming we get the solutions to the two recursive calls, we should add them together to get all the ways we can climb the stairs.<br />
<br />
Thus we end up with<br />
<pre>def count_stairs(n):<br />
if n <= 2:<br />
return n<br />
else:<br />
return count_stairs(n-1) + count_stars(n-2)</pre><br />
Notice that at each stair step, we either take one step or two steps. This is a common pattern in tree recursion. Look through Discussion 3 for more info.<br />
<br />
=== Y combinators (in Scheme) ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=2450 Source: Spring 2014 Piazza (2450)]<br />
<br />
'''Student Question'''<br />
<br />
Can someone explain this to me?<br />
<pre>scm> (((lambda (f) (lambda (x) (f f x)))<br />
(lambda (f k) (if (zero? k) 1 (* k (f f (- k 1)))))) 5)</pre><br />
I've edited the code as follows:<br />
<br />
<pre>(<br />
(<br />
(lambda (f) <br />
(lambda (x) (f f x))<br />
)<br />
(lambda (f k) <br />
(if (zero? k) 1 <br />
(* k (f f (- k 1)))<br />
)<br />
)<br />
) 5<br />
)</pre><br />
<br />
My understanding is that the second lambda function is passed as the first <code>f</code> in the first lambda function and the <code>5</code> is passed in as <code>x</code>. But does that mean <code>f f x</code> becomes the second lambda function with itself and <code>x</code> passed as the arguments to <code>(f k)</code>?<br />
<br />
'''Student Answer'''<br />
<br />
You're on the right track. The first lambda function is a higher order function that takes in a function, and then returns a function that takes one argument. It's actually the third lambda that is then passed into the first lambda (currying!) and then 5 is then passed into the resulting function.<br />
<br />
In case you're curious, this is the Python equivalent:<br />
<br />
<pre>>>> (lambda f: (lambda x: f(f, x))) (lambda f, k: 1 if k == 0 else (k * f(f, k - 1)))(5)</pre><br />
Which is then equivalent to:<br />
<br />
<pre>>>> def func1(f):<br />
def func2(x):<br />
return f(f, x)<br />
return func2<br />
>>> def func3(f, k):<br />
if k == 0:<br />
return 1<br />
else:<br />
return k * f(f, k - 1)<br />
>>> func1(func3)(5)<br />
120</pre><br />
By the way, this is just a fancy way of recursively calculating the factorial using only lambda functions. If you're still curious as to how this works, you could try this in Python tutor. Except I would recommend calculating 3! instead of 5, because it's a lot of frames.<br />
<br />
'''Instructor Answer'''<br />
<br />
Maybe it will look a little nicer in Python:<br />
<br />
<pre>(lambda f: lambda x: f(f, x))(lambda g, k: 1 if k == 0 else (k * g(g, k-1)))(5)</pre><br />
Or maybe not.<br />
<br />
So the idea is, you define a lambda function that takes a function <code>f</code>, and that returns a lambda function that takes an argument <code>x</code> and returns <code>f(f, x)</code>. Then, you call this lambda function you just defined on another lambda function (let's call this <code>func</code>) that takes a function g and another argument k, and is basically the factorial function. This first call returns the inner lambda of the first part, and when that's called with <code>5</code> you're essentially calling <code>func(func, 5)</code>. The chain of recursive calls then works as follows:<br />
<br />
<pre>func(func, 5) -> 5 * func(func, 4) -> 5 * 4 * func(func, 3) -> ... -> 120</pre><br />
In functional programming theory, this is known as a Y Combinator, and it is how you achieve recursion with just lambda functions. If you're wondering why we need <code>func</code> to take in a function as the first parameter, see what would happen if you took that part out!</div>Jeffreylu017https://www.ocf.berkeley.edu/~shidi/cs61a/wiki/GuidesGuides2014-06-21T02:10:47Z<p>Jeffreylu017: /* Scheme */</p>
<hr />
<div>== Higher-order functions ==<br />
== Environment diagrams ==<br />
=== Environment diagram Rules ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=131 Source: Spring 2014 Piazza (131)]<br />
<br />
Environment Diagrams are very important in our understanding of how the computer interprets our code.<br />
<br />
'''We will test you on this in every exam.'''<br />
<br />
'''It will never go away.'''<br />
<br />
'''Given that, master it as quickly as you can! :)'''<br />
<br />
Below are the rules I follow when drawing environment diagrams. If you understand and faithfully follow these rules when drawing them, you'll never get them wrong.<br />
<br />
One thing you haven't learned yet is nonlocal. You can skip that particular step for now (step 2 of Assignment).<br />
<br />
Post here if you have any questions!<br />
<br />
You can also take a look at this link for some examples of environment diagrams: http://albertwu.org/cs61a/notes/environments<br />
<br />
For a different perspective on the rules, check out: http://markmiyashita.com/cs61a/sp14/environment_diagrams/rules_of_environment_diagrams/<br />
<br />
A handout with detailed instructions on drawing environment diagrams is also available here (linked on the bottom of the course homepage): http://inst.eecs.berkeley.edu/~cs61a/sp14/pdfs/environment-diagrams.pdf<br />
<br />
<pre>Environment Diagram Rules<br />
=========================<br />
<br />
Creating a Function<br />
--------------------<br />
1. Draw the func <name>(<arg1>, <arg2>, ...)<br />
2. The parent of the function is wherever the function was defined<br />
(the frame we're currently in, since we're creating the function).<br />
3. If we used def, make a binding of the name to the value in the current frame.<br />
<br />
Calling User Defined Functions<br />
------------------------------<br />
1. Evaluate the operator and operands.<br />
2. Create a new frame; the parent is whatever the operator s parent is.<br />
Now this is the current frame.<br />
3. Bind the formal parameters to the argument values (the evaluated operands).<br />
4. Evaluate the body of the operator in the context of this new frame.<br />
5. After evaluating the body, go back to the frame that called the function.<br />
<br />
Assignment<br />
----------<br />
1. Evaluate the expression to the right of the assignment operator (=).<br />
2. If nonlocal, find the frame that has the variable you re looking for,<br />
starting in the parent frame and ending just before the global frame (via<br />
Lookup rules). Otherwise, use the current frame. Note: If there are multiple<br />
frames that have the same variable, pick the frame closest to the current<br />
frame.<br />
3. Bind the variable name to the value of the expression in the identified<br />
frame. Be sure you override the variable name if it had a previous binding.<br />
<br />
Lookup<br />
------<br />
1. Start at the current frame. Is the variable in this frame?<br />
If yes, that's the answer.<br />
2. If it isn't, go to the parent frame and repeat 1.<br />
3. If you run out of frames (reach the Global frame and it's not there), complain.<br />
<br />
Tips<br />
----<br />
1. You can only bind names to values.<br />
No expressions (like 3+4) allowed on environment diagrams!<br />
2. Frames and Functions both have parents.</pre><br />
<br />
== Sequences ==<br />
=== Reversing tuples ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=639 Source: Spring 2014 Piazza (639)]<br />
<br />
'''Student Question'''<br />
<br />
Why does [::-1] tuple work while the tuple [0:3:-1] doesn't?<br />
<br />
I thought the -1 after the second semicolon meant that the interpreter is going to read the indexes "backwards". <br />
<br />
'''Student Answer'''<br />
<br />
The syntax of slicing is <tt>tup[start:end:step]</tt>:<br />
<ul><li>start from index <tt>start</tt> and end just before index <tt>end</tt>, incrementing the index by <tt>step</tt> each time<br />
</li><li>if no <tt>step</tt> is provided, <tt>step</tt> = 1<br />
</li><li>if <tt>step</tt> is positive, default values if not provided: <tt>start</tt> = 0, <tt>end</tt> = <tt>len(tup)</tt><br />
</li><li>if <tt>step</tt> is negative, default values if not provided: <tt>start</tt> = -1, <tt>end</tt> = one position before the start of the string<br />
</li></ul><br />
<br />
<pre>&gt;&gt;&gt; (1, 2, 3)[::-1] # start at index -1, end one position before the start of the string<br />
(3, 2, 1)<br />
&gt;&gt;&gt; (1, 2, 3)[0:3:-1] # start at 0 and go to 3, but step is negative, so this doesn't make sense and an empty tuple is returned<br />
()<br />
</pre><br />
<br />
This is a helpful visualization from http://en.wikibooks.org/wiki/Python_Programming/Strings#Indexing_and_Slicing:<br />
<blockquote><br />
To understand slices, it's easiest not to count the elements themselves. It is a bit like counting not on your fingers, but in the spaces between them. The list is indexed like this:<br />
<pre>Element: 1 2 3 4<br />
Index: 0 1 2 3 4<br />
-4 -3 -2 -1<br />
</pre><br />
</blockquote><br />
More info about slicing at http://stackoverflow.com/a/13005464/2460890.<br />
<br />
=== Slicing with negative step ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=702 Source: Spring 2014 Piazza (702)]<br />
<br />
'''Student Question'''<br />
<br />
if the third example returns an empty tuple because you can't take negative steps from 0 to 4, shouldn't the second example also return an empty tuple?<br />
<br />
Can someone explain why each example returns the respective answers?<br />
<br />
Thanks<br />
<br />
<pre>>>> x= (1,2,3,4)<br />
>>> x[0::-1]<br />
(1,)<br />
>>> x[::-1]<br />
(4, 3, 2, 1)<br />
>>> x[0:4:-1]<br />
()<br />
>>> x[1::-1]<br />
(2, 1)</pre><br />
<br />
'''Instructor Answer'''<br />
<br />
(For reference, the notation is <tt>x[start:end:step]</tt>)<br />
<br />
Python does something a very strange when the step is negative: if you omit the arguments to start and end, Python will fill them with what makes sense for a negative step. In the simple case of <tt>x[::-1]</tt>, Python fills in the start with <tt>len(x)-1</tt> and the end with <tt>-(len(x)+1)</tt>. The end term is strange, but remember that the end term isn't included. We therefore can't use 0, but we can't use -1 either, since that clearly refers to the last element of the tuple. We need to fully wrap the negative index around, to refer to the element "before" the 0th index. This way, Python will start at the end of the tuple and proceed to the beginning of the tuple.<br />
<br />
That's why <tt>x[0:4:-1]</tt> doesn't make sense: how can we start at 0 and end at 4, if we're proceeding backwards?<br />
<br />
And that's why <tt>x[0::-1]</tt> makes sense (albeit, in a strange way): Python is proceeding from the 0 index to the beginning of the list. It includes the start index, which is why you see a 1 pop up.<br />
<br />
Let me know if that was confusing!<br />
<br />
== Recursion ==<br />
== Data abstraction ==<br />
== Time complexity ==<br />
=== Andrew Huang's guide to order of growth and function runtime ===<br />
[https://docs.google.com/document/d/1TxfKmM3MlH032hjSUh92I0kQDVcvmitTSzYObGMr8Bk Source: Guide to Order of Growth and Function Runtime (Retrieved June 16th, 2014)]<br />
<br />
'''Introduction'''<br />
<br />
Confused by $O$, $\Omega$, and $\Theta$?<br />
<br />
Want to figure out the runtime of that tricky function?<br />
<br />
Read this.<br />
<br />
'''NOTE THAT THIS GUIDE STARTS WITH BIG O, WHICH IS DIFFERENT FROM THETA. IF YOU UNDERSTAND BIG O, THETA IS EASY (IN FACT, IT DEFINES THETA IN TERMS OF BIG O BELOW).'''<br />
<br />
First some math.<br />
<br />
Formal definition of O(Big O):<br />
<br />
Let $f(n)$ and $g(n)$ be functions from positive integers to positive reals. We say $f \in O(g)$ (“f grows no faster than g”) if there is a constant $0 < c < \inf$ <such that $f(n) \leq c \cdot g(n)$.<br />
<br />
(Paraphrased from Dasgupta, Papadimitriou, & Vazirani)<br />
<br />
(You'll see this again in CS 170)<br />
<br />
What the heck does that mean?<br />
<br />
Let’s look at math functions for a second (just a second).<br />
<br />
Say $f(n)=5n$ and $g(n)=n^{2}$<br />
<br />
What does that look like on a graph?<br />
<br />
http://www.wolframalpha.com/input/?i=plot+5n+and+n%5E2+from+0+to+10<br />
<br />
There’s a section where $n$ dominates $n^{2}$, from 0 to 5, but we don’t really care, because after that point, $n^{2}$ is larger, all the way to infinity! By the definition, we could scale $n^{2}$ by 5 and we would span that initial gap.<br />
<br />
Thus we can say $5n \in O(n^{2})$ or $f \in O(g)$.<br />
<br />
Can we say the converse? That is, is $n^{2} \in O(5n)$?<br />
<br />
Not at all! From the graph we see that $n^{2}$ grows too quickly for $n$ to catch up, no matter what constant we scale $n$ by.<br />
<br />
So what if $f(n)=n+1000$ and $g(n)=n^{2}$?<br />
<br />
It turns out $n+1000 \in O(n^{2})$ still, because according to the definition, as long as we can multiply $n^{2}$ by some $c$, such that the gap of 1000 is spanned, we’re good. In the case, $c=1001$.<br />
<br />
'''What about and $\Omega$ and $\Theta$?'''<br />
<br />
If you digested all of the above, the rest isn’t scary! (Note, $a \equiv b$ means $a$ is equivalent to $b$)<br />
<br />
$f \in \Omega(g) \equiv g \in O(f)$ (You'll see this again briefly in CS 170)<br />
<br />
$f \in \Theta(g) (f \in O(g) and g \in O(f))$<br />
<br />
'''This means that if $f$ is Theta of $g$, then there exist some $c_{1}$ and $c_{2}$ such that'''<br />
<br />
'''$c_{1}g > f$ and'''<br />
<br />
'''$c_{2}g < f$'''<br />
<br />
'''for all positive integers.'''<br />
<br />
'''What does that mean for Python functions?'''<br />
<br />
Given a function $f$, we want to find out how fast that function runs. One way of doing this is to take out a stopwatch, and clock the amount of time it takes for $f$ to run on some input. However, there are tons of problems with that (different computers => different speeds; only one fixed input? Maybe $f$ is really fast for that input but slow for everything else; next year, all the measurements need to be redone on new computers; etc.) Instead, we'll count the steps that a function needs to perform as a function of its input. For example, here are some of the functions that take one step regardless of their input:<br />
<br />
<code>mul</code><br />
<br />
<code>add</code><br />
<br />
<code>sub</code><br />
<br />
<code>print</code><br />
<br />
<code>return</code><br />
<br />
<code>...</code><br />
<br />
So for example, <code>(3 + 3 * 8) % 3</code> would be 3 steps--one for the multiply, one of the add, and one for the mod.<br />
<br />
Let's take a simple example:<br />
<pre>def square(x):<br />
return x * x</pre><br />
<code>square</code> is a function that for any input, always takes two steps, one of the multiplication, and one for returning. Using the notation, we can say square ∈ Θ(1).<br />
<br />
Functions with iteration (for loops, recursion, etc.), usually multiply the steps by some factor. For example, consider factorial:<br />
<pre>def factorial(n):<br />
if n == 0:<br />
return 1<br />
else:<br />
return n * factorial(n-1)</pre><br />
factorial ∈ Θ(n). Why? Well given some input <code>n</code>, we do <code>n</code> recursive calls. At each recursive call, we carry out 4 steps, one for if <code>n == 0</code>, one for subtraction, one for multiply, one for return. Plus, we have the base case, which is another 2 steps, one for if and one for return. So <code>factorial(n)</code> takes $4n+2$ steps => ∈ Θ(n).<br />
<br />
As mentioned, we care about how the running time (how long the function takes to run) of the function changes, '''as we increase the size of the argument'''. So if we imagine a graph, then the x-axis represents the size of our input, and the y-axis represents how long the function took to run for each x. As the size of the input increases, the function’s runtime does something on the graph. So when we say something like “$O(n^{2})$ where $n$ is the length of the list”, we are saying as we double the size of the list, the function is expected to run at most four times as long. '''NOTE ALSO THAT I SAID WHAT $n$ IS! ALWAYS GIVE YOUR UNITS.'''<br />
<br />
This means that when we compare two functions A and B, A may be overall slower than B as we increase the size of their arguments. However, it’s possible at some specific arguments, the A may run faster (like the $f(n)=5n$ and $g(n)=n^{2}$ example above.)<br />
<br />
'''This also means we do not care about the time taken of any particular input! This implies that all those constant-time base cases all those functions don’t really matter, because they don’t scale. That is, only one specific input causes the base case to be reached, and if we increased the size of the argument, $O(1)$ doesn't necessarily hold.'''<br />
<br />
'''Brief “What runs faster than what”'''<br />
<br />
Sorted from fastest to slowest. This is by no means comprehensive.<br />
* $\Theta(1)$<br />
* $\Theta(\log(n))$<br />
* $\Theta(n)$<br />
* $\Theta(n \log(n))$<br />
* $\Theta(n^{2})$<br />
* $\Theta(n^{3})$<br />
* $\Theta(2^{n})$<br />
* (Anything past this point is kind of ridiculous)<br />
* $\Theta(n!)$<br />
* $\Theta(n^{n})$<br />
<br />
'''So we know about the math and the motivation, now how do we actually assign runtimes to real Python functions?'''<br />
<br />
What you must understand, is that there is no one method for finding the runtime. You MUST look at a function holistically or you won’t get the right answer. What does this mean? '''In order to get the correct runtime, you first must understand what the function is doing! You cannot pattern-match your way to becoming good at this.'''<br />
<br />
'''This cannot be stressed enough: UNITS MATTER, if you say O((n)), you must tell us what $n$ is.'''<br />
<br />
General tips<br />
# UNDERSTAND WHAT THE FUNCTION IS DOING!!!<br />
# Try some sample input. That is, pretend you’re the interpreter and execute the code with some small inputs. What is the function doing with the input? Having concrete examples lets you do tip 1 better. You can also graph how the runtime increases as the argument size increases.<br />
# If applicable, draw a picture of the tree of function calls. This shows you the "growth" of the function or how the function is getting "bigger", which will help you do tip 1 better.<br />
# If applicable, draw a picture of how the input is being modified through the function calls. For example, if your input is a list and your function recursively does something to that list, draw out a list, then draw out parts of the list underneath it that are called during the recursion. Helps with tip 1.<br />
# See tip 1.<br />
<br />
Anyways, let's examine some common runtimes (keep scrolling). Remember, '''this is in no way a comprehensive list, NOR IS IT TRYING TO TEACH YOU HOW TO FIND THEM.''' This post is just to give you a starting point into orders of growth by showing you some examples and basic details about each runtime.<br />
<br />
'''Constant $\Theta(1)$'''<br />
<br />
''What it looks like:''<br />
<br />
http://www.wolframalpha.com/input/?i=plot+5<br />
<br />
''Example:''<br />
<br />
<pre>def add(x, y):<br />
return x + y</pre><br />
$add \in \Theta(1)$, where 1 is.. well a constant...<br />
<br />
''Approach:''<br />
<br />
The key behind constant time functions is that regardless of the size of the input, they always run the same number of instructions.<br />
<br />
''Don’t fall for this Trap:''<br />
<br />
<pre>def bar(n):<br />
if n % 7 == 0:<br />
return "Bzzst"<br />
else:<br />
return bar(n -1)</pre><br />
$\mathtt{bar} \in \Theta(1)$. Why?<br />
<br />
'''Logarithmic $\Theta(\log(n))$'''<br />
<br />
''What it looks like:''<br />
<br />
http://www.wolframalpha.com/input/?i=plot+4log3n+from+0+to+10<br />
<br />
''Example:''<br />
<br />
<pre>def binary_search(sorted_L, n):<br />
""" sorted_L is a list of numbers sorted from<br />
smallest to largest<br />
"""<br />
if sorted_L == []:<br />
return False<br />
mid_num = sorted_L[len(sorted_L) // 2]<br />
if n == mid_num:<br />
return True<br />
elif n < mid_num:<br />
return binary_search(sorted_L[:mid_num], n)<br />
else:<br />
return binary_search(sorted_L[mid_num:], n)</pre><br />
$\mathtt{binary\_search} \in \Theta(log(n))$, where $n$ is the number of elements in <code>sorted_L</code>.<br />
<br />
''Approach:''<br />
<br />
Logarithmic functions scale down the size of the problem by some constant every iteration (either with a recursive loop, a for loop, or a while loop). Also, logarithmic functions do not branch out--they generally do not make more than one call to themselves per recursion.<br />
<br />
'''Linear $\Theta(n)$'''<br />
<br />
''What it looks like:''<br />
<br />
http://www.wolframalpha.com/input/?i=plot+8n+from+0+to+10<br />
<br />
''Examples:''<br />
<br />
<pre>def sum_list(L):<br />
sum = 0<br />
for e in L:<br />
sum += e<br />
return sum</pre><br />
$\mathtt{sum\_list} \in \Theta(n)$, where $n$ is the number of elements in $L$.<br />
<br />
</pre>def countdown(n):<br />
if n > 0:<br />
print(n)<br />
countdown(n - 1)<br />
else:<br />
print("Blast off!")</pre><br />
$\mathtt{countdown} \in \Theta(n)$, where $n$ is n.<br />
<br />
''Approach:''<br />
<br />
Linear functions usually act on sequences or other collections of data. In that case, the function will go through the elements once or twice or ''k'' times, where $k<<n$. If the function acts on a number, the number usually gets smaller by a constant each iteration.<br />
<br />
Don't fall for this trap:<br />
<pre>def two_for_loops(n):<br />
for a in range(n):<br />
if n == 4:<br />
for y in range(n):<br />
print("Admiral Ackbar")<br />
else:<br />
print("It's a trap!")</pre><br />
$\mathtt{two\_for\_loops} \in \Theta(n)$, where $n$ is n. Why?<br />
<br />
'''Loglinear/Linearithmic $\Theta(n \log(n))$'''<br />
<br />
''What it looks like:''<br />
<br />
http://www.wolframalpha.com/input/?i=plot+nlog%28n%29+from+0+to+10<br />
<br />
''Example:''<br />
<br />
<pre>def merge(s1, s2):<br />
if len(s1) == 0:<br />
return s2<br />
elif len(s2) == 0:<br />
return s1<br />
elif s1[0] < s2[0]:<br />
return [s1[0]] + merge(s1[1:], s2)<br />
else:<br />
return [s2[0]] + merge(s1, s2[1:])<br />
<br />
def mergesort(lst):<br />
if len(lst) <= 1:<br />
return lst<br />
else:<br />
middle = len(lst) // 2<br />
return merge(mergesort(lst[:middle]), \<br />
mergesort(lst[middle:]))</pre><br />
$\mathtt{mergesort} \in \Theta(n \log(n))$, where $n$ is the number of elements in <code>lst</code>.<br />
<br />
Approach:<br />
These functions tend to make two recursive calls, each making the problem smaller by a half. There's a neat way to see this. For example in mergesort, start with an entire line, which represents mergesort called on the initial list. From there, the list gets split in half by the two recursive calls to mergesort in the code, so draw the another line right below the first, of the same length, but with a small gap in the middle to represent the split. Repeat until you're tired. At the end, you get a rectangle that's nwide and (n)tall!<br />
<pre><nowiki><br />
---------------<br />
------- -------<br />
--- --- --- ---<br />
- - - - - - - -<br />
</nowiki></pre><br />
The total area is the runtime, $\Theta(n \log(n))$<br />
<br />
''Don’t fall for this trap:''<br />
<br />
Don’t confuse functions that have an average running time of n(n)(like quicksort) with functions that are in (n(n))<br />
<br />
'''Polynomial $\Theta(n^{2})$,$\Theta(n^{3})$, etc.'''<br />
<br />
''What it looks like:''<br />
<br />
http://www.wolframalpha.com/input/?i=plot+n%5E2%2B3+from+0+to+10<br />
<br />
''Example:''<br />
<br />
<pre>def print_a_grid(n):<br />
for _ in range(n):<br />
for _ in range(n):<br />
print("+", end="")<br />
print("")</pre><br />
$\mathtt{print\_a\_grid} \in \Theta(n^{2})$, where $n$ is n.<br />
<br />
''Approach:''<br />
<br />
Polynomial functions will examine each element of an input many, many times, as opposed to linear functions, which examine some constant number of times.<br />
<br />
''Don’t fall into this trap:''<br />
<br />
Don’t get polynomial confused with exponential (below).<br />
<br />
'''Exponential'''<br />
<br />
''What it looks like:''<br />
<br />
http://www.wolframalpha.com/input/?i=plot+2%5En+from+0+to+10<br />
<br />
''Example:''<br />
<br />
<pre>(define (strange-add x)<br />
(if (zero? x)<br />
1<br />
(+ (strange-add (- x 1))<br />
(strange-add (- x 1)) )))<br />
<br />
def strange_add(x):<br />
if x == 0:<br />
return 1<br />
else:<br />
return strange_add(x - 1) + strange_add(x - 1)</pre><br />
$\mathtt{strange\_add} \in \Theta(2^{n})$, where $n$ is x.<br />
<br />
''Approach:''<br />
<br />
Exponential functions tend to branch out as you get deeper and deeper into their call tree, and each call only makes the work smaller by a little bit. For example, <code>(strange-add 8)</code> calls <code>(strange-add 7)</code> and <code>(strange-add 7)</code>. Those two calls each make two calls, <code>(strange-add 6)</code>, <code>(strange-add 6)</code>, <code>(strange-add 6)</code>, and <code>(strange-add 6)</code> respectively, and so on.<br />
<br />
== Mutability ==<br />
=== Michelle Chang's guide to immutability and mutability ===<br />
[https://d1b10bmlvqabco.cloudfront.net/attach/hoxc5uu6sud761/gozdkhgdUbT/htdlpko411i0/Python__Immutable_vs_Mutable.pdf Source: What You Should Know about Immutability vs Mutability]<br />
<br />
== Mutable data-structures ==<br />
== Object-oriented programming ==<br />
=== Inheritance and class vs instance attributes ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=1413 Source: Spring 2014 Piazza (1413)]<br />
<br />
'''Student Question'''<br />
<br />
I'm confused on how Classes and Inheritance work.<br />
<br />
If there's a Parent class and a Child class, when coding in the Child class, when do you write <code>Parent.attribute</code>, when do you write <code>Child.attribute</code>, and when do you write <code>self.attribute</code>?<br />
<br />
Also, I'm also confused as to when to put <code>self</code> into the parentheses as well.<br />
<br />
'''Instructor Answer'''<br />
<br />
<code>Parent.attribute</code> and <code>Child.attribute</code> would both be ways of accessing a'''class variable'''. These are variables that can be accessed without creating new '''instances''' of the that class.<br />
<br />
<code>self.attribute</code> would be used in '''methods''' to access an '''instance variable''' (an attribute specific to an instance).<br />
<br />
So for example, <code>Insect.watersafe</code> is <code>False</code>, but <code>Bee.watersafe</code> is <code>True</code>. These are class attributes because you don't have to create an Insect object or a Bee object in order to say <code>Insect.watersafe</code> or <code>Bee.watersafe</code>.<br />
<br />
However it wouldn't make any sense to say <code>Bee.armor</code>, since armor is an '''instance variable'''. You have to first create a new Bee before you could ask it for it's armor. If you created a second Bee after that, the second Bee would also have its own armor.<br />
<br />
There's a lot of vocab (in bold) that might trip you up. Try reading Discussion 6 and posting a followup if you're still unsure!<br />
<br />
== Iterables, iterators and generators ==<br />
== Scheme ==<br />
=== Semantics and syntax ===<br />
----<br />
==== Difference between <code>eq?</code>, <code>eqv?</code>, <code>equal?</code> and <code>=</code> ====<br />
[http://stackoverflow.com/questions/16299246/what-is-the-difference-between-eq-eqv-equal-and-in-scheme/17719745#17719745 What is the difference between eq?, eqv?, equal?, and = in Scheme?]<br />
<br />
Let's start with the <code>=</code> equivalence predicate. The <code>=</code> predicate is used to check whether two numbers are equal. If you supply it anything else but a number then it will raise an error:<br />
<br />
<pre>(= 2 3) => #f<br />
(= 2.5 2.5) => #t<br />
(= '() '()) => error</pre><br />
The <code>eq?</code> predicate is used to check whether its two parameters respresent the same object in memory. For example:<br />
<br />
<code>(define x '(2 3))<br />
(define y '(2 3))<br />
(eq? x y) => #f<br />
(define y x)<br />
(eq? x y) => #t</code><br />
Note however that there's only one empty list <code>'()</code> in memory (actually the empty list doesn't exist in memory, but a pointer to the memory location <code>0</code> is considered as the empty list). Hence when comparing empty lists <code>eq?</code> will always return <code>#t</code> (because they represent the same object in memory):<br />
<br />
<pre>(define x '())<br />
(define y '())<br />
(eq? x y) => #t</pre><br />
Now depending upon the implementation <code>eq?</code> may or may not return <code>#t</code> for primitive values such as numbers, strings, etc. For example:<br />
<br />
(<pre>eq? 2 2) => depends upon the implementation<br />
(eq? "a" "a") => depends upon the implementation</pre><br />
This is where the <code>eqv?</code> predicate comes into picture. The <code>eqv?</code> is exactly the same as the <code>eq?</code> predicate, except that it will always return <code>#t</code> for same primitive values. For example:<br />
<br />
<pre>(eqv? 2 2) => #t<br />
(eqv? "a" "a") => #t</pre><br />
Hence <code>eqv?</code> is a superset of <code>eq?</code> and for most cases you should use <code>eqv?</code> instead of <code>eq?</code>.<br />
<br />
Finally we come to the <code>equal?</code> predicate. The <code>equal?</code> predicate is exactly the same as the <code>eqv?</code> predicate, except that it can also be used to test whether two lists, vectors, etc. have corresponding elements which satisfy the <code>eqv?</code> predicate. For example:<br />
<br />
<pre>(define x '(2 3))<br />
(define y '(2 3))<br />
(equal? x y) => #t<br />
(eqv? x y) => #f</pre><br />
In general:<br />
# Use the <code>=</code> predicate when you wish to test whether two numbers are equivalent.<br />
# Use the <code>eqv?</code> predicate when you wish to test whether two non-numeric values are equivalent.<br />
# Use the <code>equal?</code> predicate when you wish to test whether two lists, vectors, etc. are equivalent.<br />
# Don't use the <code>eq?</code> predicate unless you know exactly what you're doing.<br />
<br />
=== Scheme lists ===<br />
----<br />
==== Using <code>cons</code> ====<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=3004 Source: Spring 2014 Piazza (3004)]<br />
<br />
'''Student Question<br />
<br />
What's the difference between the following in Scheme?<br />
<br />
(cons 1 2)<br />
(cons 1 . 2)<br />
<br />
(cons 1 (cons 2 (cons 3 nil)))<br />
(cons 1 . (cons 2 . (cons 3 . 4)))<br />
<br />
Why does putting a dot before "(cons" cause it to be a malformed list? But when you put in (cons 1 2) it returns (1 . 2)? Is the dot something that only the interpreter returns, and that the user can't use in defining a list?<br />
<br />
'''Student Answer'''<br />
<br />
I struggled with this a bit as well. It helps to know the difference between a list containing a "." and one that doesn't. <br />
<br />
First off, scheme lists love to be recursive, kind of like rlists. That is, if you did cdr on the list, you would keep getting a list until you finally get an empty list/nil.<br />
<pre>STk&gt; (define a '(1 2 3))<br />
a<br />
STk&gt; a<br />
(1 2 3)<br />
STk&gt; (cdr a)<br />
(2 3)<br />
STk&gt; (cddr a)<br />
(3)<br />
STk&gt; (cdddr a)<br />
()</pre><br />
Note how each call of cdr returns a list. Even (3) is a list. It is just a list containing one member. So what happens with something like (1 2 . 3)?<br />
<pre>STk&gt; (define a '(1 2 . 3))<br />
a<br />
STk&gt; a<br />
(1 2 . 3)<br />
STk&gt; (cdr a)<br />
(2 . 3)<br />
STk&gt; (cddr a)<br />
3</pre><br />
Notice how the last <code>cdr</code> returns a simple 3. Running <code>cdddr</code> would throw an error, because the list stops at 3. A list containing a "." is known as an <em>improper </em>list.<br />
<br />
As for the ".", you won't be using it unless you use it in combination with a quote, otherwise it will always return a malformed list and throw an error. For example:<br />
<pre>STk&gt; (define a (1 2 . 3))<br />
*** Error:<br />
eval: malformed list: (1 2 . 3)<br />
Current eval stack:<br />
__________________<br />
0 (1 2 . 3)<br />
1 (define a (1 2 . 3))<br />
STk&gt; (define a '(1 2 . 3))<br />
a</pre><br />
Basically, the "." is seen in output, but not input. The only exception is the quote. Think of scheme as having two stages. First, it interprets your commands to construct the lists etc. Next, it will simplify the expression. Think of the quote as skipping straight to the second stage.<br />
<br />
Finally, the only way to get the standard lists is to end the list with nil or to use the "list" function (or to use a quote). If the list doesn't end with nil, then it will become an improper list. That is why you <em>can't</em> do things like <code>(1 . 2 . 3 . 4)</code> to make <code>(1 2 3 4)</code>. In order to form a "proper" list, each element must be represented by a list. You <em>can</em> do <code>(1 . (2 . (3 . (4))))</code> because you are treating each element like a list. When in doubt, just test some output:<br />
<pre>STk&gt; (cons 1 2)<br />
(1 . 2) ; doesn't end in an empty list/nil<br />
STk&gt; (cons 1 (cons 2 '()))<br />
(1 2)<br />
STk&gt; (cons 1 (cons 2 (3)))<br />
; ERROR<br />
STk&gt; (cons 1 (cons 2 (list 3)))<br />
(1 2 3) ; lists are formed with an nil at the end, so this works<br />
STk&gt; '(1 . (2 . (3 . 4)))<br />
(1 2 3 . 4)<br />
STk&gt; '(1 . (2 . (3 . (4)))<br />
(1 2 3 4)<br />
STk&gt; '(1 (2 . 3) 4)<br />
(1 (2 . 3) 4) ; still works, but this has 3 elements: (1), (2 . 3), and (4)<br />
STk&gt; (cons 1 (cons (cons 2 3) (cons 4 nil))) <br />
(1 (2 . 3) 4) ; equivalent to above, without quote</pre><br />
<br />
==== <code>append</code> vs <code>cons</code> vs <code>list</code> ====<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=2067 Source: Spring 2014 Piazza (2067)]<br />
<br />
This post isn't meant to be comprehensive. '''Ask questions in lab or as a followup here if you're confused.'''<br />
One of its major flaws is that it doesn't cover box and pointers. '''LEARN BOX AND POINTERS. '''<br />
<br />
Here is a beautiful web based Scheme interpreter that will draw box and pointer diagrams for you. Run through the examples below with this thing:<br />
http://xuanji.appspot.com/js-scheme-stk/index.html<br />
<br />
In order to understand these three procedures, you first have to understand a little about Pairs and Lists.<br />
<br />
'''Pairs''' are data structures that have two slots. You can put different stuff in these slots, like numbers or words or sentences or booleans--pretty much anything. You make a pair using cons.<br />
<pre>STk> (cons 'foo 'bar)<br />
(foo . bar)<br />
<br />
STk> (cons 1 'ring)<br />
(1 . ring)<br />
<br />
STk> (cons (+ 1 2 3) (member? 3 '(the 3 stooges)))<br />
(6 . #t)</pre><br />
<br />
In order to get stuff from a pair that you have made, you use <code>car</code> and <code>cdr</code>. <code>car</code> gets the thing in the first slot. <code>cdr</code> gets the thing in the second slot.<br />
<br />
<pre>STk> (define foo (cons 'x 'y))<br />
foo<br />
<br />
STk> foo<br />
(x . y)<br />
<br />
STk> (car foo)<br />
x<br />
<br />
STk> (cdr foo)<br />
y</pre><br />
That was straightforward. Now for the trippy part:<br />
'''You can put pairs inside of pairs:'''<br />
<pre>STk> (define foo (cons (cons 3 4) 5))<br />
foo<br />
<br />
STk> foo<br />
((3 . 4) . 5)<br />
<br />
STk> (car foo)<br />
(3 . 4)<br />
<br />
STk> (car (car foo))<br />
3<br />
<br />
STk> (caar foo) ; functionally equivalent as above.<br />
3<br />
<br />
STk> (cdr foo)<br />
5<br />
<br />
STk> (cdr (car foo))<br />
4<br />
<br />
STk> (cdar foo) ; functionally equivalent as above.<br />
4</pre> <br />
<br />
There's a certain style of pair nesting that is especially useful&mdash;'''Lists.'''<br />
<br />
Each list has these properties:<br />
* Every list is a pair or the empty list (denoted by '() or nil).<br />
* The car of a nonempty list is some item.<br />
* The cdr of a nonempty list must be another list.<br />
<pre>STk> (cons 1 (cons 2 (cons 3 '()))) ; list of numbers<br />
(1 2 3)<br />
<br />
STk> (define stooges (cons 'larry (cons 'curly (cons 'moe nil))))<br />
stooges<br />
<br />
STk> stooges<br />
(larry curly moe)<br />
<br />
STk> (car stooges)<br />
larry<br />
<br />
STk> (cdr stooges) ; Calling cdr on a non-empty list gives you another list!<br />
(curly moe)<br />
<br />
STk> (cadr stooges)<br />
curly<br />
<br />
STk> (cdar stooges) ; Why does this break?<br />
*** Error:<br />
cdar: bad list: (moe larry curly)<br />
Current eval stack:<br />
__________________<br />
0 (cdar stooges)<br />
<br />
STk> (define not-a-list (cons 'foo (cons 'bar 'baz))) ; This is not a list.<br />
not-a-list<br />
<br />
STk> not-a-list ; What property does this break?</pre><br />
Notice how Scheme knew that we were making lists. Before we had <code>parens</code> and periods which organized our items. Scheme now recognizes that we're making a list and does away with the periods and some of the <code>parens</code>.<br />
<br />
If you stare a bit at the list rules above, you can notice we used a recursive definition to define lists. Recursion... '''on data'''!<br />
<br />
Let's talk about <code>list</code>. <code>list</code> takes a bunch of stuff and makes a list out of them. The stuff can be anything. Words, numbers, pairs, other lists. <code>list</code> doesn't care. [picture of a honey badger]<br />
<pre>STk> (list 'foo 'bar' 'baz) ; Lists takes anything and makes a list out of it.<br />
(foo bar baz)<br />
<br />
STk> (list 'foo ((lambda (x) (+ x 4)) 8) #f (cons 1 (cons 3 4)) (cons 1 (cons 2 nil)) (list 1 2 3)) ; ANYTHING <br />
(foo 12 #f (1 3 . 4) (1 2) (1 2 3))<br />
<br />
STk> (list 'x 'y 'z)<br />
(x y z)<br />
<br />
STk> '(x y z) ; Sometimes you can get away with using quote to make literal lists. Yes, sentences are secretly lists.<br />
(x y z)</pre><br />
Now we can talk about <code>append</code>:<br />
<pre>STk> (append '(a b c) '(d e f) '(g h i)) ; Append takes in lists and appends them together.<br />
(a b c d e f g h i)<br />
<br />
STk> (append 'foo '(1 2 3)) ; foo is not a list. Stuff will break.<br />
*** Error:<br />
append: argument is not a list: foo<br />
Current eval stack:<br />
__________________<br />
0 (append (quote foo) (quote (1 2 3)))</pre><br />
You know that <code>cons</code> makes a pair. You also know that you can make a list out of pairs. You can abuse <code>cons</code> for your own maniacal purposes.<br />
<pre>STk> (cons 'joe stooges) ; Put stuff at the beginning of a list!<br />
(joe larry curly moe)</pre><br />
'''The following only applies to the STk interpreter.'''<br />
<pre>STk> (append '(1 2 3) 'foo) ; Wait... what?<br />
(1 2 3 . foo)<br />
<br />
STk> (append '(1 2 3) (cons 4 5)) ; The plot thickens!<br />
(1 2 3 4 . 5)<br />
<br />
STk> (append stooges 'shemp) ; You should really figure out why this works.<br />
(larry curly moe . shemp)</pre><br />
<br />
To summarize:<br />
* append takes in lists and outputs a big list.<br />
* cons takes in things and makes a pair out of them. However, we know that lists are made of pairs, so we can throw together a list if we use cons a certain way<br />
* list takes in things and makes a list out of those things, regardless of what they are.<br />
<br />
=== Tail recursion ===<br />
----<br />
==== Mark Miyashita's guide on tail recursion ====<br />
[http://markmiyashita.com/cs61a/sp14/tail_recursion/tail_recursion_and_tail_optimized_calls/ Source: Tail Recursion and Tail Optimized Calls]<br />
<br />
First off, I think this is an excellent article to read about tail recursion and tail calls in Python: [http://paulbutler.org/archives/tail-recursion-in-python/ here]<br />
<br />
Basically, you can write tail recursive functions in any language. Tail recursion, in one sentence, is where you return the answer in the final frame instead of following the frames back up to the original frame. For example, we have factorial which is normally not tail recursive:<br />
<br />
<pre>def factorial(n):<br />
if n == 1:<br />
return 1<br />
return n * factorial(n - 1)</pre><br />
<br />
because it needs to keep track of the <code>n *</code> at each level of recursion.<br />
<br />
The following implementation of <code>factorial</code>, is tail recursive because at the end of the last frame, it can return the answer, instead of going back up through all the frames to multiply and compute the answer:<br />
<br />
<pre>def factorial(n):<br />
def helper(n, total):<br />
if n == 1:<br />
return total<br />
return helper(n - 1, total * n)<br />
return helper(n, 1)</pre><br />
<br />
You can’t have tail optimized calls in Python – at least, not like the code that we defined above. You can define your own sort of tail optimized way of evaluating the functions by using lambdas, and I believe the article linked at the top of this post goes into detail about how to implement this if you are interested. In Scheme, the language detects when you have something like the helper function in the example above where your return statement consists of only the recursive call. In the first example, we have the <code>n *</code> and the recursive call which means it cannot be tail optimized because it needs to keep track of all of the frames that it creates. In a tail optimized call, Scheme will get rid of the frames that are no longer necessary.<br />
<br />
'''tl;dr''' – Tail recursion can be done in any language where the basic idea is that you return the answer in the final frame of recursion. Tail optimized calls are a Scheme (and some other languages, not including Python) feature where it will get rid of the frames above, if certain conditions are met – such as where the return statement is only the recursive call and nothing else. The cases in which Scheme uses a tail optimized call are located on the lecture slides located [http://www-inst.eecs.berkeley.edu/~cs61a/sp13/slides/35-TailCalls_6pp.pdf here].<br />
<br />
==== Tail recursion in Python ====<br />
[http://kylem.net/programming/tailcall.html Source: http://kylem.net/programming/tailcall.html (Retrieved June 16th, 2014)]<br />
<br />
In this page, we’re going to look at tail call recursion and see how to force Python to let us eliminate tail calls by using a trampoline. We will go through two iterations of the design: first to get it to work, and second to try to make the syntax seem reasonable. I would not consider this a useful technique in itself, but I do think it’s a good example which shows off some of the power of decorators.<br />
<br />
The first thing we should be clear about is the definition of a tail call. The “call” part means that we are considering function calls, and the “tail” part means that, of those, we are considering calls which are the last thing a function does before it returns. In the following example, the recursive call to f is a tail call (the use of the variable <code>ret</code> is immaterial because it just connects the result of the call to <code>f</code> to the return statement), and the call to <code>g</code> is not a tail call because the operation of adding one is done after <code>g</code> returns (so it’s not in “tail position”).<br />
<br />
<pre>def f(n) :<br />
if n > 0 :<br />
n -= 1<br />
ret = f(n)<br />
return ret<br />
else :<br />
ret = g(n)<br />
return ret + 1</pre><br />
<br />
'''1. Why tail calls matter'''<br />
<br />
Recursive tail calls can be replaced by jumps. This is called “tail call eliminination,” and is a transformation that can help limit the maximum stack depth used by a recursive function, with the benefit of reducing memory traffic by not having to allocate stack frames. Sometimes, recursive function which wouldn’t ordinarily be able to run due to stack overflow are transformed into function which can.<br />
<br />
Because of the benefits, some compilers (like <code>gcc</code>) perform tail call elimination[1], replacing recursive tail calls with jumps (and, depending on the language and circumstances, tail calls to other functions can sometimes be replaced with stack massaging and a jump). In the following example, we will eliminate the tail calls in a piece of code which does a binary search. It has two recursive tail calls.<br />
<br />
<pre>def binary_search(x, lst, low=None, high=None) :<br />
if low == None : low = 0<br />
if high == None : high = len(lst)-1<br />
mid = low + (high - low) // 2<br />
if low > high :<br />
return None<br />
elif lst[mid] == x :<br />
return mid<br />
elif lst[mid] > x :<br />
return binary_search(x, lst, low, mid-1)<br />
else :<br />
return binary_search(x, lst, mid+1, high)</pre><br />
<br />
Supposing Python had a <code>goto</code> statement, we could replace the tail calls with a jump to the beginning of the function, modifying the arguments at the call sites appropriately:<br />
<br />
<pre>def binary_search(x, lst, low=None, high=None) :<br />
start:<br />
if low == None : low = 0<br />
if high == None : high = len(lst)-1<br />
mid = low + (high - low) // 2<br />
if low > high :<br />
return None<br />
elif lst[mid] == x :<br />
return mid<br />
elif lst[mid] > x :<br />
(x, lst, low, high) = (x, lst, low, mid-1)<br />
goto start<br />
else :<br />
(x, lst, low, high) = (x, lst, mid+1, high)<br />
goto start</pre><br />
<br />
which, one can observe, can be written in actual Python as<br />
<br />
<pre>def binary_search(x, lst, low=None, high=None) :<br />
if low == None : low = 0<br />
if high == None : high = len(lst)-1<br />
while True :<br />
mid = low + (high - low) // 2<br />
if low > high :<br />
return None<br />
elif lst[mid] == x :<br />
return mid<br />
elif lst[mid] > x :<br />
high = mid - 1<br />
else :<br />
low = mid + 1</pre><br />
<br />
I haven’t tested the speed difference between this iterative version and the original recursive version, but I would expect it to be quite a bit faster because of there being much, much less memory traffic.<br />
<br />
Unfortunately, the transformation makes it harder to prove the binary search is correct in the resulting code. With the original recursive algorithm, it is almost trivial by induction.<br />
<br />
Programming languages like Scheme depend on tail calls being eliminated for control flow, and it’s also necessary for continuation passing style.[2]<br />
<br />
'''2. A first attempt'''<br />
<br />
Our running example is going to be the factorial function (a classic), written with an accumulator argument so that its recursive call is a tail call:<br />
<br />
<pre>def fact(n, r=1) :<br />
if n <= 1 :<br />
return r<br />
else :<br />
return fact(n-1, n*r)</pre><br />
<br />
If <code>n</code> is too large, then this recursive function will overflow the stack, despite the fact that Python can deal with really big integers. On my machine, it can compute <code>fact(999)</code>, but <code>fact(1000)</code> results in a sad <code>RuntimeError: Maximum recursion depth exceeded</code>.<br />
<br />
One solution is to modify fact to return objects which represent tail calls and then to build a trampoline underneath fact which executes these tail calls after fact returns. This way, the stack depth will only contain two stack frames: one for the trampoline and another for each call to fact.<br />
<br />
First, we define a tail call object which reifies the concept of a tail call:<br />
<br />
<pre>class TailCall(object) :<br />
def __init__(self, call, *args, **kwargs) :<br />
self.call = call<br />
self.args = args<br />
self.kwargs = kwargs<br />
def handle(self) :<br />
return self.call(*self.args, **self.kwargs)</pre><br />
<br />
This is basically just the thunk <code>lambda : call(*args, **kwargs)</code>, but we don’t use a thunk because we would like to be able to differentiate between a tail call and returning a function as a value.<br />
<br />
The next ingredient is a function which wraps a trampoline around an arbitrary function:<br />
<br />
<pre>def t(f) :<br />
def _f(*args, **kwargs) :<br />
ret = f(*args, **kwargs)<br />
while type(ret) is TailCall :<br />
ret = ret.handle()<br />
return ret<br />
return _f</pre><br />
<br />
Then, we modify fact to be<br />
<br />
<pre>def fact(n, r=1) :<br />
if n <= 1 :<br />
return r<br />
else :<br />
return TailCall(fact, n-1, n*r)</pre><br />
<br />
Now, instead of calling <code>fact(n)</code>, we must instead invoke <code>t(fact)(n)</code> (otherwise we’d just get a TailCall object).<br />
<br />
This isn’t that bad: we can get tail calls of arbitrary depth, and it’s Pythonic in the sense that the user must explicitly label the tail calls, limiting the amount of unexpected magic. But, can we eliminate the need to wrap t around the initial call? I myself find it unclean to have to write that <code>t</code> because it makes calling fact different from calling a normal function (which is how it was before the transformation).<br />
<br />
'''3. A second attempt'''<br />
<br />
The basic idea is that we will redefine fact to roughly be <code>t(fact)</code>. It’s tempting to just use <code>t</code> as a decorator:<br />
<br />
<pre>@t<br />
def fact(n, r=1) :<br />
if n <= 1 :<br />
return r<br />
else :<br />
return TailCall(fact, n-1, n*r)</pre><br />
<br />
(which, if you aren’t familiar with decorator syntax, is equivalent to writing <code>fact = t(fact)</code> right after the function definition). However, there is a problem with this in that the fact in the returned tail call is bound to <code>t(fact)</code>, so the trampoline will recursively call the trampoline, completely defeating the purpose of our work. In fact, the situation is now worse than before: on my machine, <code>fact(333)</code> causes a <code>RuntimeError</code>!<br />
<br />
For this solution, the first ingredient is the following class, which defines the trampoline as before, but wraps it in a new type so we can distinguish a trampolined function from a plain old function:<br />
<br />
<pre>class TailCaller(object) :<br />
def __init__(self, f) :<br />
self.f = f<br />
def __call__(self, *args, **kwargs) :<br />
ret = self.f(*args, **kwargs)<br />
while type(ret) is TailCall :<br />
ret = ret.handle()<br />
return ret</pre><br />
<br />
and then we modify <code>TailCall</code> to be aware of <code>TailCallers</code>:<br />
<br />
<pre>class TailCall(object) :<br />
def __init__(self, call, *args, **kwargs) :<br />
self.call = call<br />
self.args = args<br />
self.kwargs = kwargs<br />
def handle(self) :<br />
if type(self.call) is TailCaller :<br />
return self.call.f(*self.args, **self.kwargs)<br />
else :<br />
return self.call(*self.args, **self.kwargs)</pre><br />
<br />
Since classes are function-like and return their constructed object, we can just decorate our factorial function with <code>TailCaller</code>:<br />
<br />
<pre>@TailCaller<br />
def fact(n, r=1) :<br />
if n <= 1 :<br />
return r<br />
else :<br />
return TailCall(fact, n-1, n*r)</pre><br />
<br />
And then we can call fact directly with large numbers!<br />
<br />
Also, unlike in the first attempt, we can now have mutually recursive functions which all perform tail calls. The first-called <code>TailCall</code> object will handle all the trampolining.<br />
<br />
If we wanted, we could also define the following function to make the argument lists for tail calls be more consistent with those for normal function calls:[3]<br />
<br />
<pre>def tailcall(f) :<br />
def _f(*args, **kwargs) :<br />
return TailCall(f, *args, **kwargs)<br />
return _f</pre><br />
<br />
and then fact could be rewritten as<br />
<br />
<pre>@TailCaller<br />
def fact(n, r=1) :<br />
if n <= 1 :<br />
return r<br />
else :<br />
return tailcall(fact)(n-1, n*r)</pre><br />
<br />
One would hope that marking the tail calls manually could just be done away with, but I can’t think of any way to detect whether a call is a tail call without inspecting the source code. Perhaps an idea for further work is to convince Guido von Rossum that Python should support tail recursion (which is quite unlikely to happen).<br />
<br />
[1] This is compiler-writer speak. For some reason, “elimination” is what you do when you replace a computation with something equivalent. In this case, it’s true that the call is being eliminated, but in its place there’s a jump. The same is true for “common subexpression elimination” (known as CSE), which takes, for instance,<br />
<br />
<pre>a = b + c<br />
d = (b + c) + e<br />
and replaces it with<br />
a = b + c<br />
d = a + e</pre><br />
<br />
Sure, the <code>b+c</code> is eliminated from the second statement, but it’s not really gone...<br />
The optimization known as “dead code elimination” actually eliminates something, but that’s because dead code has no effect, and so it can be removed (that is, be replaced with nothing).<br />
<br />
[2] In Scheme, all loops are written as recursive functions since tail calls are the pure way of redefining variables (this is the same technique Haskell uses). For instance, to print the numbers from 1 to 100, you’d write<br />
<br />
<pre>(let next ((n 1))<br />
(if (<= n 100)<br />
(begin<br />
(display n)<br />
(newline)<br />
(next (+ n 1)))))</pre><br />
<br />
where next is bound to be a one-argument function which takes one argument, <code>n</code>, and which has the body of the <code>let</code> statement as its body. If that <code>100</code> were some arbitrarily large number, the tail call to next had better be handled as a jump, otherwise the stack would overflow! And there’s no other reasonable way to write such a loop!<br />
<br />
Continuation passing style is commonly used to handle exceptions and backtracking. You write functions of the form<br />
<br />
<pre>(define (f cont)<br />
(let ((cont2 (lambda ... (cont ...) ...)))<br />
(g cont2)))</pre><br />
<br />
along with functions which take multiple such f’s and combines them into another function which also takes a single cont argument. I’ll probably talk about this more in another page, but for now notice how the call to g is in the tail position.<br />
<br />
[3] This is basically a curried[4] version of <code>TailCall</code>.<br />
<br />
[4] That is, ''Schönfinkelized''.<br />
<br />
=== Miscellaneous ===<br />
----<br />
==== Useful Scheme Procedures ====<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=2226 Source: Spring 2014 Piazza (2226)]<br />
<br />
Here is a short list of Scheme procedures that you might use in writing your programs:<br />
<pre>; define - defines a variable or a procedure<br />
(define my-variable 4)<br />
(define (square x)<br />
(* x x))<br />
<br />
; if - conditional branching akin to if ... else<br />
(define (fib n)<br />
(if (< n 2)<br />
n<br />
(+ (fib (- n 1)) (fib (- n 2))) ))<br />
<br />
; conditional branching akin to if ... elif ... elif ... else<br />
(define (deep-map f lst)<br />
(cond ((null? lst) lst)<br />
((list? (car lst))<br />
(cons (deep-map f (car lst))<br />
(deep-map f (cdr lst))))<br />
(else<br />
(cons (f (car lst))<br />
(deep-map f (cdr lst)))) ))<br />
<br />
; and - outputs the the rightmost value if all of the arguments evaluate to #t. Outputs #f otherwise.<br />
STk> (and 0 1 2 3)<br />
3<br />
<br />
; or - outputs the the first value that evaluates to #t. Outputs #f otherwise.<br />
STk> (or 0 1 2 3)<br />
0<br />
<br />
; equal - tests if symbols are the same<br />
STk> (equal? 'foo 'bar)<br />
#f<br />
STk> (equal? 'foo 'foo)<br />
#t<br />
<br />
STk> (list? 'foo)<br />
#f<br />
STk> (list? '(1 2 3))<br />
#t<br />
STk> (list? '())<br />
#t<br />
<br />
STk> (null? '(1 2 3))<br />
#f<br />
STk> (null? ())<br />
#t<br />
<br />
; member? - tests if a symbol is in a list EDIT: NOT BUILT IN (BUT SUPER USEFUL SEE FOLLOWUP)<br />
STk> (member? 'quick '(the quick brown fox jumped over the lazy dog))<br />
#t<br />
<br />
; number? - checks if input is a number<br />
STk> (number? 42)<br />
#t<br />
STk> (number? #t)<br />
#f<br />
<br />
; remainder - computes the remainder of the first number divided by the second<br />
STk> (remainder 100 21)<br />
16</pre><br />
<br />
== Streams ==<br />
== Logic ==<br />
=== Quick Guide to Logic Programming ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=2524 Source: Spring 2014 Piazza (2524)]<br />
''Note: Someone should convert this from scmlog to Logic notation''<br />
<br />
'''Here's something I wrote a long time ago. The logic interpreter scmlog still exists and you should be able to access it using your cs61a-xx account. Post a followup if you have any questions. Hope this helps!'''<br />
<br />
<br />
'''Introduction:''' logic programming is a completely different way to think about telling computers to do stuff. Instead of telling the computer what to compute, you give the computer facts and ask it questions. The computer does its own thinking given the facts and the question, and then returns an answer.<br />
<br />
'''Table of Contents'''<br />
- Introduction<br />
- What is Logic Programming<br />
- - Giving Facts and Asking Questions<br />
- - More Complicated Facts and Questions<br />
- How to write Logic Programs<br />
- - Common Pitfalls<br />
- More Resources<br />
<br />
<br />
'''What is Logic Programming?'''<br />
Logic Programming is a way to ask the computer questions and get answers without telling it explicitly how to reach the conclusion. Kind of like this: http://youtu.be/tpKx7Oi0oeM<br />
<br />
Anyways, we have a logic programming interpreter called Scmlog. It interprets a simpler version of the logic programming language, Prolog. The first few things to understand about it is that '''SCMLOG IS NOT SCHEME'''. It just happens to look like it. Scmlog is its own language and so it has its own rules. You can't program in Scmlog like you would in Scheme or Python, so it might be good to forget what you know about those languages for a second.<br />
<br />
When you fire up scmlog (type scmlog into a terminal on the school computers), you get this prompt:<br />
<pre>star [501] ~ # scmlog<br />
scmlog (Prolog in Scheme), v. 0.2<br />
Type 'help' for help; 'quit' to exit.<br />
?- </pre><br />
You can interact with this prompt in two ways:<br />
# Give facts - "Let's tell the computer some things it should know!"<br />
# Ask questions - "Let's ask the computer questions about the things we told it about!"<br />
<br />
'''Giving Facts and Asking Questions'''<br />
A basic fact takes this form: <code>(fact (assertion))</code>, '''Where each assertion is simply a relation between things.'''<br />
<br />
For example: <code>(fact (likes potstickers brian))</code> relates three ideas, liking something, potstickers, and some guy name Brian. "Brian likes potstickers."<br />
Note that we put the relation first, and then the parties the relation acts upon. In this example, "likes" is the relation, it hooks up "brian" and "potstickers". Here's some more:<br />
<pre>?- (fact (likes potstickers brian)) <br />
?- (fact (likes potstickers andrew)) <br />
?- (fact (likes the_beatles brian)) <br />
?- (fact (likes the_beatles andrew)) <br />
?- (fact (likes led_zeppelin andrew)) <br />
?- (fact (dislikes led_zeppelin brian))</pre><br />
Now that we've given the computer a bunch of facts, how do we ask questions about them? Just replace "fact" with a "?", and replace any part of the relation (besides the relation itself) with a variable prefixed by an underscore. This is called "querying":<br />
<pre>?- (? (likes _what brian))<br />
_what : potstickers<br />
More? <br />
_what : the_beatles<br />
More? <br />
?- (? (likes potstickers _who))<br />
_who : brian<br />
More? <br />
_who : andrew<br />
More? <br />
?- (? (dislikes _what brian))<br />
_what : led_zeppelin<br />
More?<br />
?- (? (likes led_zeppelin andrew))<br />
Yes.<br />
?- (? (dislikes the_beatles _who))<br />
No.</pre><br />
Notice:<br />
* We can query any part of the assertion, besides the relation itself (can't replace "likes" with a variable).<br />
* All possible answers to the question show up.<br />
* If Scmlog couldn't find a fact that matched your query, it'll say "No."<br />
* Asking a question without any variables essentially asks if that fact exists. To which Scmlog will answer "Yes." or "No."<br />
<br />
'''More Complicated Facts and Questions'''<br />
Now this isn't the whole picture. We also have the ability to make more powerful assertions via variables, hypotheses, and conclusions:<br />
<br />
<code>(fact (ancestor _x _y) (parent _x _y))</code> "X is an ancestor of Y if X is a parent of Y"<br />
<br />
Here _x and _y are variables like usual. However, we have two parts to this fact, the conclusion ("X is an ancestor of Y") and the hypothesis ("X is a parent of Y"). We can have more than one hypotheses, and they can be any kind of query. Here's ancestor in action:<br />
<pre>?- (fact (parent george paul))<br />
?- (fact (parent martin george))<br />
?- (fact (parent martin martin_jr))<br />
?- (fact (parent martin donald))<br />
?- (fact (parent george ann))<br />
?- (fact (ancestor _X _Y) (parent _X _Y))<br />
?- (fact (ancestor _X _Y) (parent _X _Z) (ancestor _Z _Y))<br />
?- (? (ancestor paul george))<br />
No.<br />
?- (? (ancestor george paul))<br />
Yes.<br />
?- (? (ancestor george george))<br />
No.<br />
?- (? (ancestor martin paul))<br />
Yes.</pre><br />
Now there's one more thing you need to know about Scmlog. Scmlog knows about pairs and lists:<br />
<pre>?- (fact (lst (1 2 3)))<br />
?- (? (lst _x))<br />
_x : (1 2 3)<br />
More?<br />
?- (? (lst (1 . _x)))<br />
_x : (2 3)<br />
More? <br />
?- (? (lst (1 2 . _x)))<br />
_x : (3)<br />
More? <br />
?- (? (lst (_x . _y)))<br />
_x : 1<br />
_y : (2 3)<br />
?- (fact (my_pair (2 . 3)))<br />
?- (? (my_pair _y))<br />
_y : (2 . 3)<br />
?- (? (my_pair (2 . _x)))<br />
_x : 3</pre><br />
<br />
'''How to write Logic Programs'''<br />
<br />
The trick behind writing logic programs is to forget everything you know about programming. You instead want to focus on the relation you're trying to establish. Take append for example:<br />
<pre>?- (fact (append () _b _b))<br />
?- (fact (append (_x . _rest) _b (_x . _z)) (append _rest _b _z))<br />
?- (? (append (1 2 3) (3 2 1) _answer))<br />
_answer : (1 2 3 3 2 1)<br />
?- (append (1 2) 3 (1 2 3))<br />
Huh?<br />
?- (? (append (1 2) 3 (1 2 3))<br />
)<br />
No.<br />
?- (? (append (1 2) (3) (1 2 3))<br />
)<br />
Yes.<br />
?- (? (append (1 2) (3) (1 2 3)))<br />
Yes.<br />
?- (? (append _x (3 4 9) (1 0 3 2 3 4 9)))<br />
_x : (1 0 3 2)<br />
More? <br />
?- (? (append _x _y (2 1)))<br />
_x : ()<br />
_y : (2 1)<br />
More? <br />
_x : (2)<br />
_y : (1)<br />
More? <br />
_x : (2 1)<br />
_y : ()</pre><br />
<br />
What is append? Well the first fact establishes that if we append an empty list to something, the result is that something.<br />
The next fact is the meat of the code. You can almost think of this as a recursive relation. Any append relation between some three lists a b c must also fulfill another append relation between the rest of a, b, and the rest of c, respectively. Think about why that will always be true for all inputs. If the example above doesn't make sense to you, see the following:<br />
<br />
<br />
'''Common Pitfalls'''<br />
<br />
'''- One of the biggest issues people have with writing logic programs is that they don't realize that the variables don't work like they do in Scheme or Python. All Scmlog ever does is pattern match:'''<br />
<pre>?- (fact (x (0 1 8)))<br />
?- (fact (x (1 8 4)))<br />
?- (fact (x (9 4 4)))<br />
?- (fact (x (3 0 8)))<br />
?- (? (x _y))<br />
_y : (0 1 8)<br />
More? <br />
_y : (1 8 4)<br />
More? <br />
_y : (9 4 4)<br />
More? <br />
_y : (3 0 8)<br />
?- (? (x (0 1 . _z)))<br />
_z : (8)</pre><br />
<br />
- SCMLOG IS NOT SCHEME. SCMLOG IS NOT PYTHON.<br />
<br />
More Resources:<br />
* http://www-inst.eecs.berkeley.edu/~cs61a/sp12/lectures/prolog/ReadMe<br />
* http://www-inst.eecs.berkeley.edu/~cs61a/sp12/lectures/prolog/<br />
* http://www-inst.eecs.berkeley.edu/~cs61a/sp12/discussion/week14/<br />
* http://www-inst.eecs.berkeley.edu/~cs61a/fa13/slides/31-Logic_6pp.pdf<br />
<br />
=== Logic Mathematics ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=3050 Source: Spring 2014 Piazza (3050)]<br />
<br />
'''Student Question'''<br />
<br />
Can someone explain the intuition behind the implementation of the increments and the addition facts? The following is from Mark's website<br />
<br />
<pre>(fact (increment 0 1))<br />
(fact (increment 1 2))<br />
(fact (increment 2 3))<br />
(fact (increment 3 4))<br />
(fact (increment 4 5))<br />
(fact (increment 5 6))<br />
(fact (increment 6 7))<br />
(fact (increment 7 8))<br />
(fact (increment 8 9))<br />
(fact (increment 9 10))<br />
(fact (increment 10 11))<br />
(fact (increment 11 12))<br />
(fact (increment 12 13))<br />
<br />
(fact (add 1 ?x ?x+1)<br />
(increment ?x ?x+1))<br />
<br />
(fact (add ?x+1 ?y ?z+1)<br />
(increment ?x ?x+1)<br />
(increment ?z ?z+1)<br />
(add ?x ?y ?z))<br />
<br />
(query (add 2 4 6))<br />
; expect Success!</pre><br />
<br />
'''Student Answer'''<br />
<br />
First, we state a bunch of facts that denote relations between a number and the number that follows it (e.g. 1 and 2, 2 and 3, and so on). This is the increment fact.<br />
<br />
Then, we state a "base" fact, which is:<br />
<pre>(fact (add 1 ?x ?x+1)<br />
(increment ?x ?x+1))</pre><br />
In English, this fact states that some value <code>?x</code> added to 1 will give us some value <code>?x+1</code> (<code>x+1</code> is a valid variable name in Logic) '''if and only if''' the fact (<code>increment ?x ?x+1</code>) is true. As an example, <code>(add 1 2 3)</code> is true '''if and only if''' (<code>increment 2 3</code>) is true. This is true because of the increment facts we stated before.<br />
<br />
Now, to deal with additions that aren't just the sum of two numbers in which one number is a 1, we need:<br />
<pre>(fact (add ?x+1 ?y ?z+1)<br />
(increment ?x ?x+1)<br />
(increment ?z ?z+1)<br />
(add ?x ?y ?z))</pre><br />
I will try to give an intuition as to what is happening here.<br />
<br />
We state our fact: that two numbers, <code>?x+1</code> and <code>?y</code>, will add up to some number <code>?z+1</code> if and only if the following 3 hypotheses are true:<br />
* (<code>increment ?x ?x+1</code>) - "There exists some number <code>?x</code> that is 1 less than <code>?x+1</code>, and"<br />
* (<code>increment ?z ?z+1</code>) - "There exists some number <code>?z</code> that is 1 less than <code>?z+1</code>, and"<br />
* (<code>add ?x ?y ?z</code>) - "The numbers <code>?x</code> and <code>?y</code> will add up to <code>?z</code>."<br />
<br />
Consider the example of:<br />
<pre>(query (add 2 4 6))</pre><br />
Here's an idea of what's happening when Logic tries to match the query with the facts you've stated.<br />
* ?x+1 = 2, ?y = 4, ?z+1 = 6<br />
* It finds a match for <code>?x = 1</code>, since (<code>increment ?x ?x+1</code>) gives <code>?x = 1</code> because <code>?x+1 = 2</code><br />
* It finds a match for <code>?z = 5</code>, since (<code>increment ?z ?z+1</code>) gives <code>?z = 5</code> because <code>?z+1 = 6</code><br />
* It then checks for a match for (<code>add ?x ?y ?z</code>) which in this case is (<code>add 1 4 5</code>). This goes to our first "base" fact for add. (<code>add 1 4 5</code>) is a success because (<code>increment 4 5</code>) is a true fact [refer again to the "base fact" to see why this is the case]. This is also where we would get a "Failed.", if it turns out that (<code>increment ?x ?x+1</code>) wasn't actually true!<br />
* Hence, all 3 of our hypotheses are true, and so (<code>query (add 2 4 6)</code>) is a success!<br />
<br />
In this example, we only have to recurse once to get to our "base" fact. In other examples, where <code>?x+1</code> is not 2, but some number greater, such as 5, we will have to recurse 4 whole times to get to 1, at which point our "base" fact is reached. <br />
<br />
This recursion is similar to this idea in mathematical equations:<br />
<pre>x + y = z</pre><br />
is the same as<br />
<pre>(x - 1) + y = (z - 1)</pre><br />
is the same as<br />
<br />
<pre>(x - 2) + y = (z - 2)</pre?<br />
and so on... In Logic, we stop when we find that the first term (x) is 1, and then we use our increment facts to determine if the original statement is true, because all of these equations are equivalent.<br />
<br />
== Python syntax and semantics ==<br />
=== <code>print</code> vs <code>return</code> ===<br />
----<br />
==== Andrew's tips ====<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=779 Source: Spring 2014 Piazza (779)]<br />
<br />
Remember the differences between return and print.<br />
* <code>return</code> can only be used in a <code>def</code> statement. It returns a value from a function. Once Python evaluates a <code>return</code> statement, it immediately exits the function.<br />
* <code>print</code> is a function that displays its argument on the screen. It always returns <code>None</code>.<br />
<br />
Examples:<br />
<pre>def foo1(x):<br />
return x<br />
<br />
def foo2(x):<br />
print(x)<br />
<br />
>>> foo2(1) # In foo2, we print 1 ourselves using the print function<br />
1<br />
>>> foo1(1) # HERE, THE PYTHON INTERPRETER PRINTS THE RETURN VALUE OF FOO1. CANNOT STRESS HOW IMPORTANT TO UNDERSTAND THIS<br />
1<br />
>>> foo1(1) + 1<br />
2<br />
>>> foo2(1) + 1<br />
1<br />
Traceback (most recent call last):<br />
File "<stdin>", line 1, in <module><br />
TypeError: unsupported operand type(s) for +: 'NoneType' and 'int'</pre><br />
<br />
=== Function decorators ===<br />
<br />
----<br />
<br />
==== How function decorators work ====<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=638 Source: Spring 2014 Piazza (638)]<br />
<br />
'''Student Question'''<br />
<br />
I'm having difficulties understanding what exactly a function decorator is. Can someone elaborate and potentially provide me with an example other than the one in the readings?<br />
<br />
'''Instructor Answer'''<br />
<br />
So imagine you wanted your functions to print their arguments before they executed them. Here's one way to do this.<br />
<pre>def loud(fn):<br />
def new_fn(*args):<br />
print(args)<br />
return fn(*args)<br />
return new_fn </pre><br />
Here's a function loud that takes in a function and returns a new function that when called, prints out its arguments, and then does what the old function does.<br />
<br />
For example:<br />
<pre>def sq(x):<br />
return x * x<br />
>>> sq(4)<br />
16<br />
>>> sq = loud(sq) # replace the old square with our loud one.<br />
>>> sq(4)<br />
(4,)<br />
16</pre><br />
A function decorator does the same thing as the above. Assuming loud is defined, we can do this:<br />
<pre>@loud<br />
def sq(x):<br />
return x * x<br />
<br />
>>> sq(4)<br />
(4,)</pre><br />
<br />
== Student guides ==<br />
=== How to learn computer science ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=241 Source: Spring 2014 Piazza (241)]<br />
<br />
If you've never programmed before, or if you've never taken a class quite like 61A before, things right now might be scary. Everything is strange and new and there quite a lot to take in all at once. So if you're having a hard time so far, here are a few articles that might help.<br />
<br />
Note: these articles are pretty long, so feel free to read them in multiple sittings.<br />
<br />
'''At the beginning, everything seems a bit scary in CS'''. Michelle Bu, a Berkeley alum and a crazy good hacker, shares one of her experiences when she was a wee n00b in [http://blog.michellebu.com/2013/03/21-nested-callbacks/ 21 Nested Callbacks].<br />
<br />
'''Start here!''' [http://www.jamesmaa.com/2013/08/26/a-beginners-guide-to-computer-science/ "A Beginner's Guide to Computer Science"] Written by Berkeley's own James Maa. James is known for his killer walkthroughs (check out his Productivity guide). This article gives you some background on learning CS and then provides a practical guide on how to learn effectively.<br />
<br />
'''How do we learn?''' Mark Eichenlaub explains in this [http://www.quora.com/Learning/Do-grad-school-students-remember-everything-they-were-taught-in-college-all-the-time/answer/Mark-Eichenlaub Introduction to Learning Theory]. This is quite possibly the best introduction to Learning Theory.<br />
<br />
'''Sometimes, you're stuck and you end up really, really frustrated.''' Marcus Geduld explains [http://www.quora.com/Why-do-we-get-frustrated-when-learning-something/answer/Marcus-Geduld Why do we get frustrated when learning something?]<br />
<br />
=== Quick guide on getting unstuck ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=1264 Source: Quick Guide on Getting Unstuck (Retrieved June 16th, 2014)]<br />
<br />
A major frustration you might encounter in 61A is when you stare at a homework problem and have no idea where to start. Or you write some code and it doesn't pass the doctests, but now what? You work at it for a while, but next thing you know, you've been stuck for hours on the the same problem and have little to show about it.<br />
<br />
So here's a checklist of things you can do when you're stuck. Experienced programmers do these things almost naturally (because of how much practice they've had being stuck), and so while they get stuck just as much as your or I, they always know what to do next.<br />
<br />
# Do I understand what the problem is asking?<br />
## If not, which part of the problem is confusing me?<br />
### Identify the exact sentences/phrases/words/etc.<br />
## Check the given examples. Do they make sense to me?<br />
## Can I come up with my own examples? '''A good indicator that you understand the question is that you can come up with some nontrivial examples of how the function works.'''<br />
# What concepts should I use here?<br />
## Do I understand the concepts? '''Can I explain the concept in English to one of my friends such that they get it?'''<br />
### If not, go back and relearn the specific concepts that are unclear (through discussion, lab, lecture, etc.) Don't read the entire book in order to solve one problem..<br />
## How do I apply the concept to the given problem?<br />
# Write your code and test it.<br />
## Use doctests, '''BUT ALSO LOAD IT INTERACTIVELY (python3 -i ...)'''<br />
### '''Saying "my function works because the doctests pass" is a lot like saying "this airplane will fly because it has wings."'''<br />
## If your code breaks, ask yourself:<br />
### Does it error? Is it a....<br />
#### Syntax error? If so, find the syntax bug and fix it.<br />
#### Logic error? Is it something weird that you don't understand? (E.g. cannot add integer and tuple)<br />
### Why did it do that? Why didn't it do what I expected? Trace through the code by hand with an example (sample values) you came up with in step 0. '''Add calls to <code>print</code> in order to figure out how your function is handling the arguments.'''<br />
# Am I missing a trick?<br />
## Oftentimes you've never seen this type of problem before. This is expected on homework (and this is why homework can take a long time) because if you see it on the homework, then you will be familiar with it on the exam and when you program for fun and profit. <br />
## The key here is just to learn the trick however you need to.<br />
### Stare at it yourself<br />
### Stare at it with others (peers in the class)<br />
### Ask on PIazza what the approach is.<br />
### Stare at it with the TAs/lab Assistants<br />
## '''Once you figure it out, remember the trick so that you can use it next time.'''<br />
# At any point you identify what you're stuck on, you can begin to resolve it.<br />
## Use the tips above. Try things out on the interpreter. Review the lecture/discussion/labs/etc. Do whatever helps you get a better understanding of the problem.<br />
## Once you have something specific that you're stuck on, you can ask other people in the class.<br />
### '''Don't be afraid to ask. Everyone gets stuck and feels stupid sometimes. However, you get to choose how you react to it.'''<br />
### '''At the same time, it really helps to work with people who are on about the same level in the course.'''<br />
## Look on Piazza. Ask questions if yours hasn't come up yet. Be that awesome guy/girl who helps answer questions.<br />
## You can ask the TA if all else fails. We are here to help you learn!<br />
<br />
Here is an old algorithm for studying for tests (the final in this case), salvaged from the sands of time:<br />
<pre>For each topic on the final, find problems on them and do them.<br />
If you can solve them on your own, move on.<br />
Else if you are stuck, look at the solution and figure out if you<br />
are missing a trick or if you do not understand the concepts.<br />
If the problem is that you are stuck on some random trick,<br />
just learn the trick.<br />
Stare at the solutions, ask Piazza, your TA, etc.<br />
Questions you should ask at this stage:<br />
What is the problem asking me to do?<br />
How was I suppose to follow the instructions<br />
to solve the problem?<br />
What part of the problem do I not understand?<br />
What is the fastest way to clear up that misunderstanding?<br />
Then if you think you are still stuck conceptually, review<br />
and learn the concept, however you learn best.<br />
Suggestions for picking up concepts quickly (~1-2 hours):<br />
Discussion notes typically have a very concise recap of the<br />
thing they are going over.<br />
There are guides for particularly tricky things on Piazza,<br />
like Logic, Pairs and Lists in Scheme, etc.<br />
Find them and go over them.<br />
Ask a TA: "what is the best way to learn X?"<br />
If these do not work and you are still shaky after an hour<br />
or two, it might be worth watching a lecture or reading<br />
the notes.</pre><br />
<br />
== Composition ==<br />
=== General style guidelines from 61A website ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=149 Source: Spring 2014 Piazza (149)]<br />
<br />
'''Student Question'''<br />
<br />
Are we required to add any comments to our code to say what a function does, etc.? And does clarity of code count for this project, in which case should we write comments at the end of not-so-clear statements? Thanks.<br />
<br />
'''Student Answer'''<br />
<br />
Docstrings of each function are already provided. If you add a helper function, you should write a docstring for it.<br />
<br />
The [http://inst.eecs.berkeley.edu/~cs61a/sp14/style_guide.html#comments style guide on the course website] advises: "Your actual code should be self-documenting -- try to make it as obvious as possible what you are doing without resorting to comments. Only use comments if something is not obvious or needs to be explicitly emphasized"<br />
<br />
'''Instructor Answer'''<br />
<br />
You should always aim to make your code "self-documenting," meaning it is clear what your code is doing without the aid of comments. You should try to keep the number of comments to a minimum, but if there are lines which you think are unclear/ambiguous, feel free to add a comment.<br />
<br />
All projects in this class contain a 3 point component that is judged solely on your code "composition" -- i.e. whether your code is clear, concise, and easy to read.<br />
<br />
=== Simplifying code ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=1116 Source: Spring 2014 Piazza (1116)]<br />
<br />
Hi everyone, here's some tips about certain functions in Python that can greatly simplify your code for the Trends project.<br />
<br />
'''Sorting keys'''<br />
<br />
You should be familiar with the max and min functions in python, which can take in many arguments and return the maximum value.<br />
<br />
<pre>>>> max(1,3,2)<br />
3</pre><br />
<br />
These functions can also take in lists:<br />
<br />
<pre>>>> min([1,5,1,6])<br />
1</pre><br />
<br />
(In fact they can take in any ''iterable'' and return the maximum/minimum value)<br />
<br />
These functions work because Python knows how to compare the elements in the list (they are all integers). But what if the elements in the list are not integers? Fortunately, there is a way for you to tell Python how to turn each element of the list into a number that it can understand.<br />
<br />
Lets start with an example. Lets say you have a list of strings, and want to find the shortest string in the list. Here's what you can do:<br />
<br />
<pre>>>> min(['hihi', 'bye', 'a', 'zebra'], key=len)<br />
'a'</pre><br />
<br />
Notice the new keyword argument key we are passing into the min function. key is a function that min applies to each element of the list. In this case, the key is the len function, which returns the length of each string. Applying the key function to each element will return a cooresponding integer, which Python can easily use to find the minimum element.<br />
<br />
You can also use keys in the <code>sorted</code> function too, which returns a sorted list of its inputs, based on the key function passed in.<br />
<br />
<pre>>>> sorted(['hihi', 'bye', 'a', 'zebra'], key=len)<br />
['a', 'bye', 'hihi', 'zebra']</pre><br />
<br />
We can have more complex key functions. Here we sort a list of people by their age, which is the second element in the tuple. A key function, once defined, works for sorted, min and max:<br />
<br />
<pre>>>> names = [('Alice', 19, 'F'), ('Bob', 5, 'M'), ('Charlie', 12, 'M')]<br />
>>> get_age = lambda name: name[1]<br />
>>> sorted(names, key=get_age)<br />
[('Bob', 5, 'M'), ('Charlie', 12, 'M'), ('Alice', 19, 'F')]<br />
>>> max(names, key=get_age)<br />
('Alice', 19, 'F')</pre><br />
<br />
'''Dictionary default values'''<br />
<br />
Suppose we have a dictionary mapping names to counts:<br />
<br />
<pre>>>> d = {'apples': 1, 'pears': 9000}</pre><br />
<br />
If we want to add a new pear to the dictionary, we can use:<br />
<br />
<pre>>>> d['pears'] = d['pears'] + 1<br />
>>> d<br />
{'apples': 1, 'pears': 9001}</pre><br />
However we cannot use the same code to add a new item that is not already in the dictionary.<br />
<br />
<pre>>>> d['oranges'] = d['oranges'] + 1<br />
Traceback (most recent call last):<br />
...<br />
KeyError: 'oranges'</pre><br />
<br />
To solve this problem, we have to use <code>dict.setdefault(key, default)</code>. If <code>key</code> is in <code>dict</code>, it will return <code>dict[key]</code>. If not, it will insert <code>key</code> with a value of <code>default</code> and return <code>default</code>. Now we can write:<br />
<br />
<pre>>>> d['oranges'] = d.setdefault('oranges', 0) + 1<br />
>>> d<br />
{'oranges': 1, 'apples': 1, 'pears': 9001}<br />
>>> d['oranges'] = d.setdefault('oranges', 0) + 1<br />
>>> d<br />
{'oranges': 2, 'apples': 1, 'pears': 9001}</pre><br />
<br />
There's actually a even better way of doing this. If you are curious to find out, look up <code>collections.defaultdict</code>.<br />
<br />
'''For loops'''<br />
<br />
If you are iterating through a list and want to get both the item and the index the item is at, the built-in function enumerate is helpful here.<br />
<br />
<pre>>>> a = ["apple", "pear", "orange"]<br />
>>> for index, fruit in enumerate(a):<br />
... print(index, fruit)<br />
...<br />
0 apple<br />
1 pear<br />
2 orange</pre><br />
<br />
You can iterate through each key-value pair in a dictionary with dictionary.items. This is useful if you want to access both the key and the value at the same time.<br />
<br />
<pre>>>> prices = {"apple": 3, "pear": 5, "orange": 20}<br />
>>> for fruit, price in prices.items():<br />
... print(fruit, price)<br />
...<br />
apple 3<br />
pear 5<br />
orange 20</pre><br />
<br />
Hope this helps for the project!<br />
<br />
=== Programming style in scheme ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=2068 Source: Spring 2014 Piazza (2068)]<br />
<br />
Since Scheme has no rules on whitespace and indentation, you could technically write all your Scheme in one line like this:<br />
<pre>(define (fib n) (if (< n 1) n (+ (fib (- n 1)) (fib (- n 2)))))</pre><br />
<br />
But that would be terrible and everyone who had to read your code would hate you for it. Here is a more reasonable version:<br />
<pre>(define (fib n)<br />
(if (< n 1) ; Arguments to if are flush<br />
n ; Each argument gets a new line<br />
(+ (fib (- n 1)) ; Sometimes it makes to insert a newline<br />
(fib (- n 2))))) ; so that you can see arguments side by side</pre><br />
<br />
Remember that code is primarily for humans to read and incidentally for computers to run.<br />
<br />
Here are some more examples:<br />
<pre>(define (deep-map f lst)<br />
(cond ((null? lst) lst)<br />
((list? (car lst))<br />
(cons (deep-map f (car lst))<br />
(deep-map f (cdr lst))))<br />
(else<br />
(cons (f (car lst))<br />
(deep-map f (cdr lst)))) ))<br />
<br />
STk> (deep-map (lambda (x) (* x x)) (list 1 2 (list (list 3) 4)))<br />
(1 4 ((9) 16))</pre><br />
<br />
<pre>(define (reverse lst)<br />
(define (helper lst result)<br />
(if (null? lst)<br />
result<br />
(helper (cdr lst)<br />
(cons (car lst) result)) ))<br />
(helper lst ()) )<br />
<br />
STk> (reverse (list 1 2 3))<br />
(3 2 1)</pre><br />
<br />
=== ucb.py's <code>trace</code> method ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=3000 Source: Spring 2014 Piazza (3000)]<br />
<br />
'''Student Question'''<br />
<br />
How does the trace in ucb.py actually work?<br />
<br />
'''Student Answer'''<br />
<br />
It's actually very similar to the <code>printed</code> function that was defined in the Hog spec!<br />
<br />
<pre>def printed(fn):<br />
def print_and_return(*args):<br />
result = fn(*args)<br />
print('Result:', result)<br />
return result<br />
return print_and_return<br />
</pre><br />
<br />
The main idea is still the same in <code>trace</code> -- we want to figure out all the arguments and save the result of calling the function with those arguments (so we can print it before returning it). One (sort of) major improvement is <code>**kwds</code>. Just like how <code>*args</code> collected all the "positional arguments", <code>**kwds</code> captures all the "keyword arguments" (the ones of the form <code>param='some_val'</code>). This can be a bit confusing, but a couple of examples might help!<br />
<br />
<pre>&gt;&gt;&gt; def add_three(a, b, c):<br />
... return a + b + c<br />
&gt;&gt;&gt; add_three(1, 2, 3) # all arguments are positional (normal)<br />
&gt;&gt;&gt; add_three(1, 2, c=3) # a, b are positional arguments, c is a keyword argument<br />
<br />
&gt;&gt;&gt; def fn(*args, **kwargs):<br />
... print(args)<br />
... print(kwargs)<br />
&gt;&gt;&gt; fn(1, 2, 3)<br />
(1, 2, 3)<br />
{}<br />
&gt;&gt;&gt; fn(1, k=2)<br />
(1,)<br />
{'k' : 2}<br />
&gt;&gt;&gt; fn(a=1, b=2, c=3)<br />
()<br />
{'a':1, 'b':2, 'c':3}<br />
</pre><br />
<br />
Since there are only two types of arguments, having both <code>*args</code> and <code>**kwds</code> covers all our bases. If we passed <code>printed</code> a keyword argument, it could cause an error!<br />
<br />
Everything else in <code>trace</code> just makes the output prettier and more helpful. <code>trace</code> uses the <code>_PREFIX</code> global variable to keep track of how far to indent the next print statement. It catches exceptions and prints them out, before re-raising that exception. It also uses some Python black magic to figure out the name of the function so we can print <code>some_fn</code> instead of <code>&lt;function some_fn at 0x...&gt;</code>.<br />
<br />
If there's a particular aspect of <code>trace</code> that you're confused about, feel free to post a followup!<br />
<br />
== Debugging ==<br />
== Miscellaneous ==<br />
=== Andrew Huang's tips ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=779 Source: Spring 2014 Piazza (779)]<br />
<br />
Order of evaluation matters. The rules for evaluating call expressions are<br />
<br />
# Evaluate the operator<br />
# Evaluate the operands<br />
# Call the operator on the operands (and draw a new frame...)<br />
<br />
For example:<br />
<pre>def baz():<br />
print("this was first")<br />
def bar(x):<br />
print(x)<br />
return lambda x: x * x<br />
return bar # baz is a function that when called, returns a function named bar<br />
<br />
>>> baz() # the operator is baz, there are no operands<br />
this was first<br />
<function bar at 0x2797e20><br />
>>> baz()("this was second") # the operator is baz(), the operand is "this was second"<br />
this was first<br />
this was second<br />
<function <lambda> at 0x2120e20><br />
>>> baz()("this was second")(3) # the operator is baz()("this was second"), the operand is 3<br />
this was first<br />
this was second<br />
9<br />
>>> def bar(x):<br />
... print(x)<br />
... return 3<br />
... <br />
>>> baz()("this was second")(bar("this was third")) # the operator is baz()("this was second"), the operand is bar("this was third")<br />
this was first<br />
this was second<br />
this was third<br />
9</pre><br />
<br />
In order to solve any problem, you must first understand what the problem is asking. Often times it helps to try to explain it concisely in English. It also helps to come up with small examples. For example:<br />
<br />
<pre>def mouse(n):<br />
if n >= 10:<br />
squeak = n // 100<br />
n = frog(squeak) + n % 10<br />
return n<br />
<br />
def frog(croak):<br />
if croak == 0:<br />
return 1<br />
else:<br />
return 10 * mouse(croak+1)<br />
<br />
mouse(21023508479)</pre><br />
<br />
So the goal is to figure out what <code>mouse(21023508479)</code> evaluates to.<br />
<br />
One way is to just step-by-step evaluate this, as an interpreter would.<br />
<br />
Another way, is to understand what the functions are doing.<br />
<br />
Looking at <code>mouse</code>, we see that it takes in a number and outputs that same number if it is smaller than 10. otherwise, it'll return something weird. In order to understand that weird thing, we have to understand what <code>frog</code> is doing. <code>frog</code> takes in a number and if that number is <code>0</code>, return <code>1</code>. Otherwise, return ten times <code>mouse(croak+1)</code>. Well, this is still confusing. Let's try a small example.<br />
<pre>>>> mouse(357)<br />
47<br />
>>> mouse(123)<br />
23<br />
>>> mouse(1234)<br />
44<br />
>>> mouse(12345)<br />
245</pre><br />
There is a pattern. We notice that the resulting number is composed of every other digit of the original, plus one (except for the last one.)<br />
So <code>21023508479</code> is <code>[2+1][0+1][3+1][0+1][4+1][9] = 314159</code>. Can you see how the code reflects that?<br />
However in this particular example, the pattern is definitely tricky to find here, so it might make more sense to brute force it.<br />
<br />
<br />
Remember for recursion, you always need to find three things:<br />
* One or more base cases<br />
* One or more was to reduce the problem<br />
* A way to solve the problem given solutions to smaller problems<br />
For example, the discussion notes, we asked you to write count_stairs. This function takes in n, the number of steps, and returns all the ways you can climb up them if at each step, you can take either one or two steps.<br />
<br />
* Base cases: if we consider n to be the number of steps left to climb, then it makes sense that if there is 1 step left, then there is exactly one way. If there are two steps left, then there are exactly 2 ways (1 step, 1 step, or two steps). Why do we need two base cases here?<br />
* We can make the problem smaller by reducing the n. At each step, we can take one step (resulting in count_stairs(n-1)) or two steps (count_stairs(n-2)).<br />
* Assuming we get the solutions to the two recursive calls, we should add them together to get all the ways we can climb the stairs.<br />
<br />
Thus we end up with<br />
<pre>def count_stairs(n):<br />
if n <= 2:<br />
return n<br />
else:<br />
return count_stairs(n-1) + count_stars(n-2)</pre><br />
Notice that at each stair step, we either take one step or two steps. This is a common pattern in tree recursion. Look through Discussion 3 for more info.<br />
<br />
=== Y combinators (in Scheme) ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=2450 Source: Spring 2014 Piazza (2450)]<br />
<br />
'''Student Question'''<br />
<br />
Can someone explain this to me?<br />
<pre>scm> (((lambda (f) (lambda (x) (f f x)))<br />
(lambda (f k) (if (zero? k) 1 (* k (f f (- k 1)))))) 5)</pre><br />
I've edited the code as follows:<br />
<br />
<pre>(<br />
(<br />
(lambda (f) <br />
(lambda (x) (f f x))<br />
)<br />
(lambda (f k) <br />
(if (zero? k) 1 <br />
(* k (f f (- k 1)))<br />
)<br />
)<br />
) 5<br />
)</pre><br />
<br />
My understanding is that the second lambda function is passed as the first <code>f</code> in the first lambda function and the <code>5</code> is passed in as <code>x</code>. But does that mean <code>f f x</code> becomes the second lambda function with itself and <code>x</code> passed as the arguments to <code>(f k)</code>?<br />
<br />
'''Student Answer'''<br />
<br />
You're on the right track. The first lambda function is a higher order function that takes in a function, and then returns a function that takes one argument. It's actually the third lambda that is then passed into the first lambda (currying!) and then 5 is then passed into the resulting function.<br />
<br />
In case you're curious, this is the Python equivalent:<br />
<br />
<pre>>>> (lambda f: (lambda x: f(f, x))) (lambda f, k: 1 if k == 0 else (k * f(f, k - 1)))(5)</pre><br />
Which is then equivalent to:<br />
<br />
<pre>>>> def func1(f):<br />
def func2(x):<br />
return f(f, x)<br />
return func2<br />
>>> def func3(f, k):<br />
if k == 0:<br />
return 1<br />
else:<br />
return k * f(f, k - 1)<br />
>>> func1(func3)(5)<br />
120</pre><br />
By the way, this is just a fancy way of recursively calculating the factorial using only lambda functions. If you're still curious as to how this works, you could try this in Python tutor. Except I would recommend calculating 3! instead of 5, because it's a lot of frames.<br />
<br />
'''Instructor Answer'''<br />
<br />
Maybe it will look a little nicer in Python:<br />
<br />
<pre>(lambda f: lambda x: f(f, x))(lambda g, k: 1 if k == 0 else (k * g(g, k-1)))(5)</pre><br />
Or maybe not.<br />
<br />
So the idea is, you define a lambda function that takes a function <code>f</code>, and that returns a lambda function that takes an argument <code>x</code> and returns <code>f(f, x)</code>. Then, you call this lambda function you just defined on another lambda function (let's call this <code>func</code>) that takes a function g and another argument k, and is basically the factorial function. This first call returns the inner lambda of the first part, and when that's called with <code>5</code> you're essentially calling <code>func(func, 5)</code>. The chain of recursive calls then works as follows:<br />
<br />
<pre>func(func, 5) -> 5 * func(func, 4) -> 5 * 4 * func(func, 3) -> ... -> 120</pre><br />
In functional programming theory, this is known as a Y Combinator, and it is how you achieve recursion with just lambda functions. If you're wondering why we need <code>func</code> to take in a function as the first parameter, see what would happen if you took that part out!</div>Jeffreylu017https://www.ocf.berkeley.edu/~shidi/cs61a/wiki/GuidesGuides2014-06-21T01:31:47Z<p>Jeffreylu017: /* Scheme lists */</p>
<hr />
<div>== Higher-order functions ==<br />
== Environment diagrams ==<br />
=== Environment diagram Rules ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=131 Source: Spring 2014 Piazza (131)]<br />
<br />
Environment Diagrams are very important in our understanding of how the computer interprets our code.<br />
<br />
'''We will test you on this in every exam.'''<br />
<br />
'''It will never go away.'''<br />
<br />
'''Given that, master it as quickly as you can! :)'''<br />
<br />
Below are the rules I follow when drawing environment diagrams. If you understand and faithfully follow these rules when drawing them, you'll never get them wrong.<br />
<br />
One thing you haven't learned yet is nonlocal. You can skip that particular step for now (step 2 of Assignment).<br />
<br />
Post here if you have any questions!<br />
<br />
You can also take a look at this link for some examples of environment diagrams: http://albertwu.org/cs61a/notes/environments<br />
<br />
For a different perspective on the rules, check out: http://markmiyashita.com/cs61a/sp14/environment_diagrams/rules_of_environment_diagrams/<br />
<br />
A handout with detailed instructions on drawing environment diagrams is also available here (linked on the bottom of the course homepage): http://inst.eecs.berkeley.edu/~cs61a/sp14/pdfs/environment-diagrams.pdf<br />
<br />
<pre>Environment Diagram Rules<br />
=========================<br />
<br />
Creating a Function<br />
--------------------<br />
1. Draw the func <name>(<arg1>, <arg2>, ...)<br />
2. The parent of the function is wherever the function was defined<br />
(the frame we're currently in, since we're creating the function).<br />
3. If we used def, make a binding of the name to the value in the current frame.<br />
<br />
Calling User Defined Functions<br />
------------------------------<br />
1. Evaluate the operator and operands.<br />
2. Create a new frame; the parent is whatever the operator s parent is.<br />
Now this is the current frame.<br />
3. Bind the formal parameters to the argument values (the evaluated operands).<br />
4. Evaluate the body of the operator in the context of this new frame.<br />
5. After evaluating the body, go back to the frame that called the function.<br />
<br />
Assignment<br />
----------<br />
1. Evaluate the expression to the right of the assignment operator (=).<br />
2. If nonlocal, find the frame that has the variable you re looking for,<br />
starting in the parent frame and ending just before the global frame (via<br />
Lookup rules). Otherwise, use the current frame. Note: If there are multiple<br />
frames that have the same variable, pick the frame closest to the current<br />
frame.<br />
3. Bind the variable name to the value of the expression in the identified<br />
frame. Be sure you override the variable name if it had a previous binding.<br />
<br />
Lookup<br />
------<br />
1. Start at the current frame. Is the variable in this frame?<br />
If yes, that's the answer.<br />
2. If it isn't, go to the parent frame and repeat 1.<br />
3. If you run out of frames (reach the Global frame and it's not there), complain.<br />
<br />
Tips<br />
----<br />
1. You can only bind names to values.<br />
No expressions (like 3+4) allowed on environment diagrams!<br />
2. Frames and Functions both have parents.</pre><br />
<br />
== Sequences ==<br />
=== Reversing tuples ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=639 Source: Spring 2014 Piazza (639)]<br />
<br />
'''Student Question'''<br />
<br />
Why does [::-1] tuple work while the tuple [0:3:-1] doesn't?<br />
<br />
I thought the -1 after the second semicolon meant that the interpreter is going to read the indexes "backwards". <br />
<br />
'''Student Answer'''<br />
<br />
The syntax of slicing is <tt>tup[start:end:step]</tt>:<br />
<ul><li>start from index <tt>start</tt> and end just before index <tt>end</tt>, incrementing the index by <tt>step</tt> each time<br />
</li><li>if no <tt>step</tt> is provided, <tt>step</tt> = 1<br />
</li><li>if <tt>step</tt> is positive, default values if not provided: <tt>start</tt> = 0, <tt>end</tt> = <tt>len(tup)</tt><br />
</li><li>if <tt>step</tt> is negative, default values if not provided: <tt>start</tt> = -1, <tt>end</tt> = one position before the start of the string<br />
</li></ul><br />
<br />
<pre>&gt;&gt;&gt; (1, 2, 3)[::-1] # start at index -1, end one position before the start of the string<br />
(3, 2, 1)<br />
&gt;&gt;&gt; (1, 2, 3)[0:3:-1] # start at 0 and go to 3, but step is negative, so this doesn't make sense and an empty tuple is returned<br />
()<br />
</pre><br />
<br />
This is a helpful visualization from http://en.wikibooks.org/wiki/Python_Programming/Strings#Indexing_and_Slicing:<br />
<blockquote><br />
To understand slices, it's easiest not to count the elements themselves. It is a bit like counting not on your fingers, but in the spaces between them. The list is indexed like this:<br />
<pre>Element: 1 2 3 4<br />
Index: 0 1 2 3 4<br />
-4 -3 -2 -1<br />
</pre><br />
</blockquote><br />
More info about slicing at http://stackoverflow.com/a/13005464/2460890.<br />
<br />
=== Slicing with negative step ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=702 Source: Spring 2014 Piazza (702)]<br />
<br />
'''Student Question'''<br />
<br />
if the third example returns an empty tuple because you can't take negative steps from 0 to 4, shouldn't the second example also return an empty tuple?<br />
<br />
Can someone explain why each example returns the respective answers?<br />
<br />
Thanks<br />
<br />
<pre>>>> x= (1,2,3,4)<br />
>>> x[0::-1]<br />
(1,)<br />
>>> x[::-1]<br />
(4, 3, 2, 1)<br />
>>> x[0:4:-1]<br />
()<br />
>>> x[1::-1]<br />
(2, 1)</pre><br />
<br />
'''Instructor Answer'''<br />
<br />
(For reference, the notation is <tt>x[start:end:step]</tt>)<br />
<br />
Python does something a very strange when the step is negative: if you omit the arguments to start and end, Python will fill them with what makes sense for a negative step. In the simple case of <tt>x[::-1]</tt>, Python fills in the start with <tt>len(x)-1</tt> and the end with <tt>-(len(x)+1)</tt>. The end term is strange, but remember that the end term isn't included. We therefore can't use 0, but we can't use -1 either, since that clearly refers to the last element of the tuple. We need to fully wrap the negative index around, to refer to the element "before" the 0th index. This way, Python will start at the end of the tuple and proceed to the beginning of the tuple.<br />
<br />
That's why <tt>x[0:4:-1]</tt> doesn't make sense: how can we start at 0 and end at 4, if we're proceeding backwards?<br />
<br />
And that's why <tt>x[0::-1]</tt> makes sense (albeit, in a strange way): Python is proceeding from the 0 index to the beginning of the list. It includes the start index, which is why you see a 1 pop up.<br />
<br />
Let me know if that was confusing!<br />
<br />
== Recursion ==<br />
== Data abstraction ==<br />
== Time complexity ==<br />
=== Andrew Huang's guide to order of growth and function runtime ===<br />
[https://docs.google.com/document/d/1TxfKmM3MlH032hjSUh92I0kQDVcvmitTSzYObGMr8Bk Source: Guide to Order of Growth and Function Runtime (Retrieved June 16th, 2014)]<br />
<br />
'''Introduction'''<br />
<br />
Confused by $O$, $\Omega$, and $\Theta$?<br />
<br />
Want to figure out the runtime of that tricky function?<br />
<br />
Read this.<br />
<br />
'''NOTE THAT THIS GUIDE STARTS WITH BIG O, WHICH IS DIFFERENT FROM THETA. IF YOU UNDERSTAND BIG O, THETA IS EASY (IN FACT, IT DEFINES THETA IN TERMS OF BIG O BELOW).'''<br />
<br />
First some math.<br />
<br />
Formal definition of O(Big O):<br />
<br />
Let $f(n)$ and $g(n)$ be functions from positive integers to positive reals. We say $f \in O(g)$ (“f grows no faster than g”) if there is a constant $0 < c < \inf$ <such that $f(n) \leq c \cdot g(n)$.<br />
<br />
(Paraphrased from Dasgupta, Papadimitriou, & Vazirani)<br />
<br />
(You'll see this again in CS 170)<br />
<br />
What the heck does that mean?<br />
<br />
Let’s look at math functions for a second (just a second).<br />
<br />
Say $f(n)=5n$ and $g(n)=n^{2}$<br />
<br />
What does that look like on a graph?<br />
<br />
http://www.wolframalpha.com/input/?i=plot+5n+and+n%5E2+from+0+to+10<br />
<br />
There’s a section where $n$ dominates $n^{2}$, from 0 to 5, but we don’t really care, because after that point, $n^{2}$ is larger, all the way to infinity! By the definition, we could scale $n^{2}$ by 5 and we would span that initial gap.<br />
<br />
Thus we can say $5n \in O(n^{2})$ or $f \in O(g)$.<br />
<br />
Can we say the converse? That is, is $n^{2} \in O(5n)$?<br />
<br />
Not at all! From the graph we see that $n^{2}$ grows too quickly for $n$ to catch up, no matter what constant we scale $n$ by.<br />
<br />
So what if $f(n)=n+1000$ and $g(n)=n^{2}$?<br />
<br />
It turns out $n+1000 \in O(n^{2})$ still, because according to the definition, as long as we can multiply $n^{2}$ by some $c$, such that the gap of 1000 is spanned, we’re good. In the case, $c=1001$.<br />
<br />
'''What about and $\Omega$ and $\Theta$?'''<br />
<br />
If you digested all of the above, the rest isn’t scary! (Note, $a \equiv b$ means $a$ is equivalent to $b$)<br />
<br />
$f \in \Omega(g) \equiv g \in O(f)$ (You'll see this again briefly in CS 170)<br />
<br />
$f \in \Theta(g) (f \in O(g) and g \in O(f))$<br />
<br />
'''This means that if $f$ is Theta of $g$, then there exist some $c_{1}$ and $c_{2}$ such that'''<br />
<br />
'''$c_{1}g > f$ and'''<br />
<br />
'''$c_{2}g < f$'''<br />
<br />
'''for all positive integers.'''<br />
<br />
'''What does that mean for Python functions?'''<br />
<br />
Given a function $f$, we want to find out how fast that function runs. One way of doing this is to take out a stopwatch, and clock the amount of time it takes for $f$ to run on some input. However, there are tons of problems with that (different computers => different speeds; only one fixed input? Maybe $f$ is really fast for that input but slow for everything else; next year, all the measurements need to be redone on new computers; etc.) Instead, we'll count the steps that a function needs to perform as a function of its input. For example, here are some of the functions that take one step regardless of their input:<br />
<br />
<code>mul</code><br />
<br />
<code>add</code><br />
<br />
<code>sub</code><br />
<br />
<code>print</code><br />
<br />
<code>return</code><br />
<br />
<code>...</code><br />
<br />
So for example, <code>(3 + 3 * 8) % 3</code> would be 3 steps--one for the multiply, one of the add, and one for the mod.<br />
<br />
Let's take a simple example:<br />
<pre>def square(x):<br />
return x * x</pre><br />
<code>square</code> is a function that for any input, always takes two steps, one of the multiplication, and one for returning. Using the notation, we can say square ∈ Θ(1).<br />
<br />
Functions with iteration (for loops, recursion, etc.), usually multiply the steps by some factor. For example, consider factorial:<br />
<pre>def factorial(n):<br />
if n == 0:<br />
return 1<br />
else:<br />
return n * factorial(n-1)</pre><br />
factorial ∈ Θ(n). Why? Well given some input <code>n</code>, we do <code>n</code> recursive calls. At each recursive call, we carry out 4 steps, one for if <code>n == 0</code>, one for subtraction, one for multiply, one for return. Plus, we have the base case, which is another 2 steps, one for if and one for return. So <code>factorial(n)</code> takes $4n+2$ steps => ∈ Θ(n).<br />
<br />
As mentioned, we care about how the running time (how long the function takes to run) of the function changes, '''as we increase the size of the argument'''. So if we imagine a graph, then the x-axis represents the size of our input, and the y-axis represents how long the function took to run for each x. As the size of the input increases, the function’s runtime does something on the graph. So when we say something like “$O(n^{2})$ where $n$ is the length of the list”, we are saying as we double the size of the list, the function is expected to run at most four times as long. '''NOTE ALSO THAT I SAID WHAT $n$ IS! ALWAYS GIVE YOUR UNITS.'''<br />
<br />
This means that when we compare two functions A and B, A may be overall slower than B as we increase the size of their arguments. However, it’s possible at some specific arguments, the A may run faster (like the $f(n)=5n$ and $g(n)=n^{2}$ example above.)<br />
<br />
'''This also means we do not care about the time taken of any particular input! This implies that all those constant-time base cases all those functions don’t really matter, because they don’t scale. That is, only one specific input causes the base case to be reached, and if we increased the size of the argument, $O(1)$ doesn't necessarily hold.'''<br />
<br />
'''Brief “What runs faster than what”'''<br />
<br />
Sorted from fastest to slowest. This is by no means comprehensive.<br />
* $\Theta(1)$<br />
* $\Theta(\log(n))$<br />
* $\Theta(n)$<br />
* $\Theta(n \log(n))$<br />
* $\Theta(n^{2})$<br />
* $\Theta(n^{3})$<br />
* $\Theta(2^{n})$<br />
* (Anything past this point is kind of ridiculous)<br />
* $\Theta(n!)$<br />
* $\Theta(n^{n})$<br />
<br />
'''So we know about the math and the motivation, now how do we actually assign runtimes to real Python functions?'''<br />
<br />
What you must understand, is that there is no one method for finding the runtime. You MUST look at a function holistically or you won’t get the right answer. What does this mean? '''In order to get the correct runtime, you first must understand what the function is doing! You cannot pattern-match your way to becoming good at this.'''<br />
<br />
'''This cannot be stressed enough: UNITS MATTER, if you say O((n)), you must tell us what $n$ is.'''<br />
<br />
General tips<br />
# UNDERSTAND WHAT THE FUNCTION IS DOING!!!<br />
# Try some sample input. That is, pretend you’re the interpreter and execute the code with some small inputs. What is the function doing with the input? Having concrete examples lets you do tip 1 better. You can also graph how the runtime increases as the argument size increases.<br />
# If applicable, draw a picture of the tree of function calls. This shows you the "growth" of the function or how the function is getting "bigger", which will help you do tip 1 better.<br />
# If applicable, draw a picture of how the input is being modified through the function calls. For example, if your input is a list and your function recursively does something to that list, draw out a list, then draw out parts of the list underneath it that are called during the recursion. Helps with tip 1.<br />
# See tip 1.<br />
<br />
Anyways, let's examine some common runtimes (keep scrolling). Remember, '''this is in no way a comprehensive list, NOR IS IT TRYING TO TEACH YOU HOW TO FIND THEM.''' This post is just to give you a starting point into orders of growth by showing you some examples and basic details about each runtime.<br />
<br />
'''Constant $\Theta(1)$'''<br />
<br />
''What it looks like:''<br />
<br />
http://www.wolframalpha.com/input/?i=plot+5<br />
<br />
''Example:''<br />
<br />
<pre>def add(x, y):<br />
return x + y</pre><br />
$add \in \Theta(1)$, where 1 is.. well a constant...<br />
<br />
''Approach:''<br />
<br />
The key behind constant time functions is that regardless of the size of the input, they always run the same number of instructions.<br />
<br />
''Don’t fall for this Trap:''<br />
<br />
<pre>def bar(n):<br />
if n % 7 == 0:<br />
return "Bzzst"<br />
else:<br />
return bar(n -1)</pre><br />
$\mathtt{bar} \in \Theta(1)$. Why?<br />
<br />
'''Logarithmic $\Theta(\log(n))$'''<br />
<br />
''What it looks like:''<br />
<br />
http://www.wolframalpha.com/input/?i=plot+4log3n+from+0+to+10<br />
<br />
''Example:''<br />
<br />
<pre>def binary_search(sorted_L, n):<br />
""" sorted_L is a list of numbers sorted from<br />
smallest to largest<br />
"""<br />
if sorted_L == []:<br />
return False<br />
mid_num = sorted_L[len(sorted_L) // 2]<br />
if n == mid_num:<br />
return True<br />
elif n < mid_num:<br />
return binary_search(sorted_L[:mid_num], n)<br />
else:<br />
return binary_search(sorted_L[mid_num:], n)</pre><br />
$\mathtt{binary\_search} \in \Theta(log(n))$, where $n$ is the number of elements in <code>sorted_L</code>.<br />
<br />
''Approach:''<br />
<br />
Logarithmic functions scale down the size of the problem by some constant every iteration (either with a recursive loop, a for loop, or a while loop). Also, logarithmic functions do not branch out--they generally do not make more than one call to themselves per recursion.<br />
<br />
'''Linear $\Theta(n)$'''<br />
<br />
''What it looks like:''<br />
<br />
http://www.wolframalpha.com/input/?i=plot+8n+from+0+to+10<br />
<br />
''Examples:''<br />
<br />
<pre>def sum_list(L):<br />
sum = 0<br />
for e in L:<br />
sum += e<br />
return sum</pre><br />
$\mathtt{sum\_list} \in \Theta(n)$, where $n$ is the number of elements in $L$.<br />
<br />
</pre>def countdown(n):<br />
if n > 0:<br />
print(n)<br />
countdown(n - 1)<br />
else:<br />
print("Blast off!")</pre><br />
$\mathtt{countdown} \in \Theta(n)$, where $n$ is n.<br />
<br />
''Approach:''<br />
<br />
Linear functions usually act on sequences or other collections of data. In that case, the function will go through the elements once or twice or ''k'' times, where $k<<n$. If the function acts on a number, the number usually gets smaller by a constant each iteration.<br />
<br />
Don't fall for this trap:<br />
<pre>def two_for_loops(n):<br />
for a in range(n):<br />
if n == 4:<br />
for y in range(n):<br />
print("Admiral Ackbar")<br />
else:<br />
print("It's a trap!")</pre><br />
$\mathtt{two\_for\_loops} \in \Theta(n)$, where $n$ is n. Why?<br />
<br />
'''Loglinear/Linearithmic $\Theta(n \log(n))$'''<br />
<br />
''What it looks like:''<br />
<br />
http://www.wolframalpha.com/input/?i=plot+nlog%28n%29+from+0+to+10<br />
<br />
''Example:''<br />
<br />
<pre>def merge(s1, s2):<br />
if len(s1) == 0:<br />
return s2<br />
elif len(s2) == 0:<br />
return s1<br />
elif s1[0] < s2[0]:<br />
return [s1[0]] + merge(s1[1:], s2)<br />
else:<br />
return [s2[0]] + merge(s1, s2[1:])<br />
<br />
def mergesort(lst):<br />
if len(lst) <= 1:<br />
return lst<br />
else:<br />
middle = len(lst) // 2<br />
return merge(mergesort(lst[:middle]), \<br />
mergesort(lst[middle:]))</pre><br />
$\mathtt{mergesort} \in \Theta(n \log(n))$, where $n$ is the number of elements in <code>lst</code>.<br />
<br />
Approach:<br />
These functions tend to make two recursive calls, each making the problem smaller by a half. There's a neat way to see this. For example in mergesort, start with an entire line, which represents mergesort called on the initial list. From there, the list gets split in half by the two recursive calls to mergesort in the code, so draw the another line right below the first, of the same length, but with a small gap in the middle to represent the split. Repeat until you're tired. At the end, you get a rectangle that's nwide and (n)tall!<br />
<pre><nowiki><br />
---------------<br />
------- -------<br />
--- --- --- ---<br />
- - - - - - - -<br />
</nowiki></pre><br />
The total area is the runtime, $\Theta(n \log(n))$<br />
<br />
''Don’t fall for this trap:''<br />
<br />
Don’t confuse functions that have an average running time of n(n)(like quicksort) with functions that are in (n(n))<br />
<br />
'''Polynomial $\Theta(n^{2})$,$\Theta(n^{3})$, etc.'''<br />
<br />
''What it looks like:''<br />
<br />
http://www.wolframalpha.com/input/?i=plot+n%5E2%2B3+from+0+to+10<br />
<br />
''Example:''<br />
<br />
<pre>def print_a_grid(n):<br />
for _ in range(n):<br />
for _ in range(n):<br />
print("+", end="")<br />
print("")</pre><br />
$\mathtt{print\_a\_grid} \in \Theta(n^{2})$, where $n$ is n.<br />
<br />
''Approach:''<br />
<br />
Polynomial functions will examine each element of an input many, many times, as opposed to linear functions, which examine some constant number of times.<br />
<br />
''Don’t fall into this trap:''<br />
<br />
Don’t get polynomial confused with exponential (below).<br />
<br />
'''Exponential'''<br />
<br />
''What it looks like:''<br />
<br />
http://www.wolframalpha.com/input/?i=plot+2%5En+from+0+to+10<br />
<br />
''Example:''<br />
<br />
<pre>(define (strange-add x)<br />
(if (zero? x)<br />
1<br />
(+ (strange-add (- x 1))<br />
(strange-add (- x 1)) )))<br />
<br />
def strange_add(x):<br />
if x == 0:<br />
return 1<br />
else:<br />
return strange_add(x - 1) + strange_add(x - 1)</pre><br />
$\mathtt{strange\_add} \in \Theta(2^{n})$, where $n$ is x.<br />
<br />
''Approach:''<br />
<br />
Exponential functions tend to branch out as you get deeper and deeper into their call tree, and each call only makes the work smaller by a little bit. For example, <code>(strange-add 8)</code> calls <code>(strange-add 7)</code> and <code>(strange-add 7)</code>. Those two calls each make two calls, <code>(strange-add 6)</code>, <code>(strange-add 6)</code>, <code>(strange-add 6)</code>, and <code>(strange-add 6)</code> respectively, and so on.<br />
<br />
== Mutability ==<br />
=== Michelle Chang's guide to immutability and mutability ===<br />
[https://d1b10bmlvqabco.cloudfront.net/attach/hoxc5uu6sud761/gozdkhgdUbT/htdlpko411i0/Python__Immutable_vs_Mutable.pdf Source: What You Should Know about Immutability vs Mutability]<br />
<br />
== Mutable data-structures ==<br />
== Object-oriented programming ==<br />
=== Inheritance and class vs instance attributes ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=1413 Source: Spring 2014 Piazza (1413)]<br />
<br />
'''Student Question'''<br />
<br />
I'm confused on how Classes and Inheritance work.<br />
<br />
If there's a Parent class and a Child class, when coding in the Child class, when do you write <code>Parent.attribute</code>, when do you write <code>Child.attribute</code>, and when do you write <code>self.attribute</code>?<br />
<br />
Also, I'm also confused as to when to put <code>self</code> into the parentheses as well.<br />
<br />
'''Instructor Answer'''<br />
<br />
<code>Parent.attribute</code> and <code>Child.attribute</code> would both be ways of accessing a'''class variable'''. These are variables that can be accessed without creating new '''instances''' of the that class.<br />
<br />
<code>self.attribute</code> would be used in '''methods''' to access an '''instance variable''' (an attribute specific to an instance).<br />
<br />
So for example, <code>Insect.watersafe</code> is <code>False</code>, but <code>Bee.watersafe</code> is <code>True</code>. These are class attributes because you don't have to create an Insect object or a Bee object in order to say <code>Insect.watersafe</code> or <code>Bee.watersafe</code>.<br />
<br />
However it wouldn't make any sense to say <code>Bee.armor</code>, since armor is an '''instance variable'''. You have to first create a new Bee before you could ask it for it's armor. If you created a second Bee after that, the second Bee would also have its own armor.<br />
<br />
There's a lot of vocab (in bold) that might trip you up. Try reading Discussion 6 and posting a followup if you're still unsure!<br />
<br />
== Iterables, iterators and generators ==<br />
== Scheme ==<br />
=== Scheme lists ===<br />
----<br />
==== Using <code>cons</code> ====<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=3004 Source: Spring 2014 Piazza (3004)]<br />
<br />
'''Student Question<br />
<br />
What's the difference between the following in Scheme?<br />
<br />
(cons 1 2)<br />
(cons 1 . 2)<br />
<br />
(cons 1 (cons 2 (cons 3 nil)))<br />
(cons 1 . (cons 2 . (cons 3 . 4)))<br />
<br />
Why does putting a dot before "(cons" cause it to be a malformed list? But when you put in (cons 1 2) it returns (1 . 2)? Is the dot something that only the interpreter returns, and that the user can't use in defining a list?<br />
<br />
'''Student Answer'''<br />
<br />
I struggled with this a bit as well. It helps to know the difference between a list containing a "." and one that doesn't. <br />
<br />
First off, scheme lists love to be recursive, kind of like rlists. That is, if you did cdr on the list, you would keep getting a list until you finally get an empty list/nil.<br />
<pre>STk&gt; (define a '(1 2 3))<br />
a<br />
STk&gt; a<br />
(1 2 3)<br />
STk&gt; (cdr a)<br />
(2 3)<br />
STk&gt; (cddr a)<br />
(3)<br />
STk&gt; (cdddr a)<br />
()</pre><br />
Note how each call of cdr returns a list. Even (3) is a list. It is just a list containing one member. So what happens with something like (1 2 . 3)?<br />
<pre>STk&gt; (define a '(1 2 . 3))<br />
a<br />
STk&gt; a<br />
(1 2 . 3)<br />
STk&gt; (cdr a)<br />
(2 . 3)<br />
STk&gt; (cddr a)<br />
3</pre><br />
Notice how the last <code>cdr</code> returns a simple 3. Running <code>cdddr</code> would throw an error, because the list stops at 3. A list containing a "." is known as an <em>improper </em>list.<br />
<br />
As for the ".", you won't be using it unless you use it in combination with a quote, otherwise it will always return a malformed list and throw an error. For example:<br />
<pre>STk&gt; (define a (1 2 . 3))<br />
*** Error:<br />
eval: malformed list: (1 2 . 3)<br />
Current eval stack:<br />
__________________<br />
0 (1 2 . 3)<br />
1 (define a (1 2 . 3))<br />
STk&gt; (define a '(1 2 . 3))<br />
a</pre><br />
Basically, the "." is seen in output, but not input. The only exception is the quote. Think of scheme as having two stages. First, it interprets your commands to construct the lists etc. Next, it will simplify the expression. Think of the quote as skipping straight to the second stage.<br />
<br />
Finally, the only way to get the standard lists is to end the list with nil or to use the "list" function (or to use a quote). If the list doesn't end with nil, then it will become an improper list. That is why you <em>can't</em> do things like <code>(1 . 2 . 3 . 4)</code> to make <code>(1 2 3 4)</code>. In order to form a "proper" list, each element must be represented by a list. You <em>can</em> do <code>(1 . (2 . (3 . (4))))</code> because you are treating each element like a list. When in doubt, just test some output:<br />
<pre>STk&gt; (cons 1 2)<br />
(1 . 2) ; doesn't end in an empty list/nil<br />
STk&gt; (cons 1 (cons 2 '()))<br />
(1 2)<br />
STk&gt; (cons 1 (cons 2 (3)))<br />
; ERROR<br />
STk&gt; (cons 1 (cons 2 (list 3)))<br />
(1 2 3) ; lists are formed with an nil at the end, so this works<br />
STk&gt; '(1 . (2 . (3 . 4)))<br />
(1 2 3 . 4)<br />
STk&gt; '(1 . (2 . (3 . (4)))<br />
(1 2 3 4)<br />
STk&gt; '(1 (2 . 3) 4)<br />
(1 (2 . 3) 4) ; still works, but this has 3 elements: (1), (2 . 3), and (4)<br />
STk&gt; (cons 1 (cons (cons 2 3) (cons 4 nil))) <br />
(1 (2 . 3) 4) ; equivalent to above, without quote</pre><br />
<br />
==== <code>append</code> vs <code>cons</code> vs <code>list</code> ====<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=2067 Source: Spring 2014 Piazza (2067)]<br />
<br />
This post isn't meant to be comprehensive. '''Ask questions in lab or as a followup here if you're confused.'''<br />
One of its major flaws is that it doesn't cover box and pointers. '''LEARN BOX AND POINTERS. '''<br />
<br />
Here is a beautiful web based Scheme interpreter that will draw box and pointer diagrams for you. Run through the examples below with this thing:<br />
http://xuanji.appspot.com/js-scheme-stk/index.html<br />
<br />
In order to understand these three procedures, you first have to understand a little about Pairs and Lists.<br />
<br />
'''Pairs''' are data structures that have two slots. You can put different stuff in these slots, like numbers or words or sentences or booleans--pretty much anything. You make a pair using cons.<br />
<pre>STk> (cons 'foo 'bar)<br />
(foo . bar)<br />
<br />
STk> (cons 1 'ring)<br />
(1 . ring)<br />
<br />
STk> (cons (+ 1 2 3) (member? 3 '(the 3 stooges)))<br />
(6 . #t)</pre><br />
<br />
In order to get stuff from a pair that you have made, you use <code>car</code> and <code>cdr</code>. <code>car</code> gets the thing in the first slot. <code>cdr</code> gets the thing in the second slot.<br />
<br />
<pre>STk> (define foo (cons 'x 'y))<br />
foo<br />
<br />
STk> foo<br />
(x . y)<br />
<br />
STk> (car foo)<br />
x<br />
<br />
STk> (cdr foo)<br />
y</pre><br />
That was straightforward. Now for the trippy part:<br />
'''You can put pairs inside of pairs:'''<br />
<pre>STk> (define foo (cons (cons 3 4) 5))<br />
foo<br />
<br />
STk> foo<br />
((3 . 4) . 5)<br />
<br />
STk> (car foo)<br />
(3 . 4)<br />
<br />
STk> (car (car foo))<br />
3<br />
<br />
STk> (caar foo) ; functionally equivalent as above.<br />
3<br />
<br />
STk> (cdr foo)<br />
5<br />
<br />
STk> (cdr (car foo))<br />
4<br />
<br />
STk> (cdar foo) ; functionally equivalent as above.<br />
4</pre> <br />
<br />
There's a certain style of pair nesting that is especially useful&mdash;'''Lists.'''<br />
<br />
Each list has these properties:<br />
* Every list is a pair or the empty list (denoted by '() or nil).<br />
* The car of a nonempty list is some item.<br />
* The cdr of a nonempty list must be another list.<br />
<pre>STk> (cons 1 (cons 2 (cons 3 '()))) ; list of numbers<br />
(1 2 3)<br />
<br />
STk> (define stooges (cons 'larry (cons 'curly (cons 'moe nil))))<br />
stooges<br />
<br />
STk> stooges<br />
(larry curly moe)<br />
<br />
STk> (car stooges)<br />
larry<br />
<br />
STk> (cdr stooges) ; Calling cdr on a non-empty list gives you another list!<br />
(curly moe)<br />
<br />
STk> (cadr stooges)<br />
curly<br />
<br />
STk> (cdar stooges) ; Why does this break?<br />
*** Error:<br />
cdar: bad list: (moe larry curly)<br />
Current eval stack:<br />
__________________<br />
0 (cdar stooges)<br />
<br />
STk> (define not-a-list (cons 'foo (cons 'bar 'baz))) ; This is not a list.<br />
not-a-list<br />
<br />
STk> not-a-list ; What property does this break?</pre><br />
Notice how Scheme knew that we were making lists. Before we had <code>parens</code> and periods which organized our items. Scheme now recognizes that we're making a list and does away with the periods and some of the <code>parens</code>.<br />
<br />
If you stare a bit at the list rules above, you can notice we used a recursive definition to define lists. Recursion... '''on data'''!<br />
<br />
Let's talk about <code>list</code>. <code>list</code> takes a bunch of stuff and makes a list out of them. The stuff can be anything. Words, numbers, pairs, other lists. <code>list</code> doesn't care. [picture of a honey badger]<br />
<pre>STk> (list 'foo 'bar' 'baz) ; Lists takes anything and makes a list out of it.<br />
(foo bar baz)<br />
<br />
STk> (list 'foo ((lambda (x) (+ x 4)) 8) #f (cons 1 (cons 3 4)) (cons 1 (cons 2 nil)) (list 1 2 3)) ; ANYTHING <br />
(foo 12 #f (1 3 . 4) (1 2) (1 2 3))<br />
<br />
STk> (list 'x 'y 'z)<br />
(x y z)<br />
<br />
STk> '(x y z) ; Sometimes you can get away with using quote to make literal lists. Yes, sentences are secretly lists.<br />
(x y z)</pre><br />
Now we can talk about <code>append</code>:<br />
<pre>STk> (append '(a b c) '(d e f) '(g h i)) ; Append takes in lists and appends them together.<br />
(a b c d e f g h i)<br />
<br />
STk> (append 'foo '(1 2 3)) ; foo is not a list. Stuff will break.<br />
*** Error:<br />
append: argument is not a list: foo<br />
Current eval stack:<br />
__________________<br />
0 (append (quote foo) (quote (1 2 3)))</pre><br />
You know that <code>cons</code> makes a pair. You also know that you can make a list out of pairs. You can abuse <code>cons</code> for your own maniacal purposes.<br />
<pre>STk> (cons 'joe stooges) ; Put stuff at the beginning of a list!<br />
(joe larry curly moe)</pre><br />
'''The following only applies to the STk interpreter.'''<br />
<pre>STk> (append '(1 2 3) 'foo) ; Wait... what?<br />
(1 2 3 . foo)<br />
<br />
STk> (append '(1 2 3) (cons 4 5)) ; The plot thickens!<br />
(1 2 3 4 . 5)<br />
<br />
STk> (append stooges 'shemp) ; You should really figure out why this works.<br />
(larry curly moe . shemp)</pre><br />
<br />
To summarize:<br />
* append takes in lists and outputs a big list.<br />
* cons takes in things and makes a pair out of them. However, we know that lists are made of pairs, so we can throw together a list if we use cons a certain way<br />
* list takes in things and makes a list out of those things, regardless of what they are.<br />
<br />
=== Tail recursion ===<br />
----<br />
==== Mark Miyashita's guide on tail recursion ====<br />
[http://markmiyashita.com/cs61a/sp14/tail_recursion/tail_recursion_and_tail_optimized_calls/ Source: Tail Recursion and Tail Optimized Calls]<br />
<br />
First off, I think this is an excellent article to read about tail recursion and tail calls in Python: [http://paulbutler.org/archives/tail-recursion-in-python/ here]<br />
<br />
Basically, you can write tail recursive functions in any language. Tail recursion, in one sentence, is where you return the answer in the final frame instead of following the frames back up to the original frame. For example, we have factorial which is normally not tail recursive:<br />
<br />
<pre>def factorial(n):<br />
if n == 1:<br />
return 1<br />
return n * factorial(n - 1)</pre><br />
<br />
because it needs to keep track of the <code>n *</code> at each level of recursion.<br />
<br />
The following implementation of <code>factorial</code>, is tail recursive because at the end of the last frame, it can return the answer, instead of going back up through all the frames to multiply and compute the answer:<br />
<br />
<pre>def factorial(n):<br />
def helper(n, total):<br />
if n == 1:<br />
return total<br />
return helper(n - 1, total * n)<br />
return helper(n, 1)</pre><br />
<br />
You can’t have tail optimized calls in Python – at least, not like the code that we defined above. You can define your own sort of tail optimized way of evaluating the functions by using lambdas, and I believe the article linked at the top of this post goes into detail about how to implement this if you are interested. In Scheme, the language detects when you have something like the helper function in the example above where your return statement consists of only the recursive call. In the first example, we have the <code>n *</code> and the recursive call which means it cannot be tail optimized because it needs to keep track of all of the frames that it creates. In a tail optimized call, Scheme will get rid of the frames that are no longer necessary.<br />
<br />
'''tl;dr''' – Tail recursion can be done in any language where the basic idea is that you return the answer in the final frame of recursion. Tail optimized calls are a Scheme (and some other languages, not including Python) feature where it will get rid of the frames above, if certain conditions are met – such as where the return statement is only the recursive call and nothing else. The cases in which Scheme uses a tail optimized call are located on the lecture slides located [http://www-inst.eecs.berkeley.edu/~cs61a/sp13/slides/35-TailCalls_6pp.pdf here].<br />
<br />
==== Tail recursion in Python ====<br />
[http://kylem.net/programming/tailcall.html Source: http://kylem.net/programming/tailcall.html (Retrieved June 16th, 2014)]<br />
<br />
In this page, we’re going to look at tail call recursion and see how to force Python to let us eliminate tail calls by using a trampoline. We will go through two iterations of the design: first to get it to work, and second to try to make the syntax seem reasonable. I would not consider this a useful technique in itself, but I do think it’s a good example which shows off some of the power of decorators.<br />
<br />
The first thing we should be clear about is the definition of a tail call. The “call” part means that we are considering function calls, and the “tail” part means that, of those, we are considering calls which are the last thing a function does before it returns. In the following example, the recursive call to f is a tail call (the use of the variable <code>ret</code> is immaterial because it just connects the result of the call to <code>f</code> to the return statement), and the call to <code>g</code> is not a tail call because the operation of adding one is done after <code>g</code> returns (so it’s not in “tail position”).<br />
<br />
<pre>def f(n) :<br />
if n > 0 :<br />
n -= 1<br />
ret = f(n)<br />
return ret<br />
else :<br />
ret = g(n)<br />
return ret + 1</pre><br />
<br />
'''1. Why tail calls matter'''<br />
<br />
Recursive tail calls can be replaced by jumps. This is called “tail call eliminination,” and is a transformation that can help limit the maximum stack depth used by a recursive function, with the benefit of reducing memory traffic by not having to allocate stack frames. Sometimes, recursive function which wouldn’t ordinarily be able to run due to stack overflow are transformed into function which can.<br />
<br />
Because of the benefits, some compilers (like <code>gcc</code>) perform tail call elimination[1], replacing recursive tail calls with jumps (and, depending on the language and circumstances, tail calls to other functions can sometimes be replaced with stack massaging and a jump). In the following example, we will eliminate the tail calls in a piece of code which does a binary search. It has two recursive tail calls.<br />
<br />
<pre>def binary_search(x, lst, low=None, high=None) :<br />
if low == None : low = 0<br />
if high == None : high = len(lst)-1<br />
mid = low + (high - low) // 2<br />
if low > high :<br />
return None<br />
elif lst[mid] == x :<br />
return mid<br />
elif lst[mid] > x :<br />
return binary_search(x, lst, low, mid-1)<br />
else :<br />
return binary_search(x, lst, mid+1, high)</pre><br />
<br />
Supposing Python had a <code>goto</code> statement, we could replace the tail calls with a jump to the beginning of the function, modifying the arguments at the call sites appropriately:<br />
<br />
<pre>def binary_search(x, lst, low=None, high=None) :<br />
start:<br />
if low == None : low = 0<br />
if high == None : high = len(lst)-1<br />
mid = low + (high - low) // 2<br />
if low > high :<br />
return None<br />
elif lst[mid] == x :<br />
return mid<br />
elif lst[mid] > x :<br />
(x, lst, low, high) = (x, lst, low, mid-1)<br />
goto start<br />
else :<br />
(x, lst, low, high) = (x, lst, mid+1, high)<br />
goto start</pre><br />
<br />
which, one can observe, can be written in actual Python as<br />
<br />
<pre>def binary_search(x, lst, low=None, high=None) :<br />
if low == None : low = 0<br />
if high == None : high = len(lst)-1<br />
while True :<br />
mid = low + (high - low) // 2<br />
if low > high :<br />
return None<br />
elif lst[mid] == x :<br />
return mid<br />
elif lst[mid] > x :<br />
high = mid - 1<br />
else :<br />
low = mid + 1</pre><br />
<br />
I haven’t tested the speed difference between this iterative version and the original recursive version, but I would expect it to be quite a bit faster because of there being much, much less memory traffic.<br />
<br />
Unfortunately, the transformation makes it harder to prove the binary search is correct in the resulting code. With the original recursive algorithm, it is almost trivial by induction.<br />
<br />
Programming languages like Scheme depend on tail calls being eliminated for control flow, and it’s also necessary for continuation passing style.[2]<br />
<br />
'''2. A first attempt'''<br />
<br />
Our running example is going to be the factorial function (a classic), written with an accumulator argument so that its recursive call is a tail call:<br />
<br />
<pre>def fact(n, r=1) :<br />
if n <= 1 :<br />
return r<br />
else :<br />
return fact(n-1, n*r)</pre><br />
<br />
If <code>n</code> is too large, then this recursive function will overflow the stack, despite the fact that Python can deal with really big integers. On my machine, it can compute <code>fact(999)</code>, but <code>fact(1000)</code> results in a sad <code>RuntimeError: Maximum recursion depth exceeded</code>.<br />
<br />
One solution is to modify fact to return objects which represent tail calls and then to build a trampoline underneath fact which executes these tail calls after fact returns. This way, the stack depth will only contain two stack frames: one for the trampoline and another for each call to fact.<br />
<br />
First, we define a tail call object which reifies the concept of a tail call:<br />
<br />
<pre>class TailCall(object) :<br />
def __init__(self, call, *args, **kwargs) :<br />
self.call = call<br />
self.args = args<br />
self.kwargs = kwargs<br />
def handle(self) :<br />
return self.call(*self.args, **self.kwargs)</pre><br />
<br />
This is basically just the thunk <code>lambda : call(*args, **kwargs)</code>, but we don’t use a thunk because we would like to be able to differentiate between a tail call and returning a function as a value.<br />
<br />
The next ingredient is a function which wraps a trampoline around an arbitrary function:<br />
<br />
<pre>def t(f) :<br />
def _f(*args, **kwargs) :<br />
ret = f(*args, **kwargs)<br />
while type(ret) is TailCall :<br />
ret = ret.handle()<br />
return ret<br />
return _f</pre><br />
<br />
Then, we modify fact to be<br />
<br />
<pre>def fact(n, r=1) :<br />
if n <= 1 :<br />
return r<br />
else :<br />
return TailCall(fact, n-1, n*r)</pre><br />
<br />
Now, instead of calling <code>fact(n)</code>, we must instead invoke <code>t(fact)(n)</code> (otherwise we’d just get a TailCall object).<br />
<br />
This isn’t that bad: we can get tail calls of arbitrary depth, and it’s Pythonic in the sense that the user must explicitly label the tail calls, limiting the amount of unexpected magic. But, can we eliminate the need to wrap t around the initial call? I myself find it unclean to have to write that <code>t</code> because it makes calling fact different from calling a normal function (which is how it was before the transformation).<br />
<br />
'''3. A second attempt'''<br />
<br />
The basic idea is that we will redefine fact to roughly be <code>t(fact)</code>. It’s tempting to just use <code>t</code> as a decorator:<br />
<br />
<pre>@t<br />
def fact(n, r=1) :<br />
if n <= 1 :<br />
return r<br />
else :<br />
return TailCall(fact, n-1, n*r)</pre><br />
<br />
(which, if you aren’t familiar with decorator syntax, is equivalent to writing <code>fact = t(fact)</code> right after the function definition). However, there is a problem with this in that the fact in the returned tail call is bound to <code>t(fact)</code>, so the trampoline will recursively call the trampoline, completely defeating the purpose of our work. In fact, the situation is now worse than before: on my machine, <code>fact(333)</code> causes a <code>RuntimeError</code>!<br />
<br />
For this solution, the first ingredient is the following class, which defines the trampoline as before, but wraps it in a new type so we can distinguish a trampolined function from a plain old function:<br />
<br />
<pre>class TailCaller(object) :<br />
def __init__(self, f) :<br />
self.f = f<br />
def __call__(self, *args, **kwargs) :<br />
ret = self.f(*args, **kwargs)<br />
while type(ret) is TailCall :<br />
ret = ret.handle()<br />
return ret</pre><br />
<br />
and then we modify <code>TailCall</code> to be aware of <code>TailCallers</code>:<br />
<br />
<pre>class TailCall(object) :<br />
def __init__(self, call, *args, **kwargs) :<br />
self.call = call<br />
self.args = args<br />
self.kwargs = kwargs<br />
def handle(self) :<br />
if type(self.call) is TailCaller :<br />
return self.call.f(*self.args, **self.kwargs)<br />
else :<br />
return self.call(*self.args, **self.kwargs)</pre><br />
<br />
Since classes are function-like and return their constructed object, we can just decorate our factorial function with <code>TailCaller</code>:<br />
<br />
<pre>@TailCaller<br />
def fact(n, r=1) :<br />
if n <= 1 :<br />
return r<br />
else :<br />
return TailCall(fact, n-1, n*r)</pre><br />
<br />
And then we can call fact directly with large numbers!<br />
<br />
Also, unlike in the first attempt, we can now have mutually recursive functions which all perform tail calls. The first-called <code>TailCall</code> object will handle all the trampolining.<br />
<br />
If we wanted, we could also define the following function to make the argument lists for tail calls be more consistent with those for normal function calls:[3]<br />
<br />
<pre>def tailcall(f) :<br />
def _f(*args, **kwargs) :<br />
return TailCall(f, *args, **kwargs)<br />
return _f</pre><br />
<br />
and then fact could be rewritten as<br />
<br />
<pre>@TailCaller<br />
def fact(n, r=1) :<br />
if n <= 1 :<br />
return r<br />
else :<br />
return tailcall(fact)(n-1, n*r)</pre><br />
<br />
One would hope that marking the tail calls manually could just be done away with, but I can’t think of any way to detect whether a call is a tail call without inspecting the source code. Perhaps an idea for further work is to convince Guido von Rossum that Python should support tail recursion (which is quite unlikely to happen).<br />
<br />
[1] This is compiler-writer speak. For some reason, “elimination” is what you do when you replace a computation with something equivalent. In this case, it’s true that the call is being eliminated, but in its place there’s a jump. The same is true for “common subexpression elimination” (known as CSE), which takes, for instance,<br />
<br />
<pre>a = b + c<br />
d = (b + c) + e<br />
and replaces it with<br />
a = b + c<br />
d = a + e</pre><br />
<br />
Sure, the <code>b+c</code> is eliminated from the second statement, but it’s not really gone...<br />
The optimization known as “dead code elimination” actually eliminates something, but that’s because dead code has no effect, and so it can be removed (that is, be replaced with nothing).<br />
<br />
[2] In Scheme, all loops are written as recursive functions since tail calls are the pure way of redefining variables (this is the same technique Haskell uses). For instance, to print the numbers from 1 to 100, you’d write<br />
<br />
<pre>(let next ((n 1))<br />
(if (<= n 100)<br />
(begin<br />
(display n)<br />
(newline)<br />
(next (+ n 1)))))</pre><br />
<br />
where next is bound to be a one-argument function which takes one argument, <code>n</code>, and which has the body of the <code>let</code> statement as its body. If that <code>100</code> were some arbitrarily large number, the tail call to next had better be handled as a jump, otherwise the stack would overflow! And there’s no other reasonable way to write such a loop!<br />
<br />
Continuation passing style is commonly used to handle exceptions and backtracking. You write functions of the form<br />
<br />
<pre>(define (f cont)<br />
(let ((cont2 (lambda ... (cont ...) ...)))<br />
(g cont2)))</pre><br />
<br />
along with functions which take multiple such f’s and combines them into another function which also takes a single cont argument. I’ll probably talk about this more in another page, but for now notice how the call to g is in the tail position.<br />
<br />
[3] This is basically a curried[4] version of <code>TailCall</code>.<br />
<br />
[4] That is, ''Schönfinkelized''.<br />
<br />
=== Miscellaneous ===<br />
----<br />
==== Useful Scheme Procedures ====<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=2226 Source: Spring 2014 Piazza (2226)]<br />
<br />
Here is a short list of Scheme procedures that you might use in writing your programs:<br />
<pre>; define - defines a variable or a procedure<br />
(define my-variable 4)<br />
(define (square x)<br />
(* x x))<br />
<br />
; if - conditional branching akin to if ... else<br />
(define (fib n)<br />
(if (< n 2)<br />
n<br />
(+ (fib (- n 1)) (fib (- n 2))) ))<br />
<br />
; conditional branching akin to if ... elif ... elif ... else<br />
(define (deep-map f lst)<br />
(cond ((null? lst) lst)<br />
((list? (car lst))<br />
(cons (deep-map f (car lst))<br />
(deep-map f (cdr lst))))<br />
(else<br />
(cons (f (car lst))<br />
(deep-map f (cdr lst)))) ))<br />
<br />
; and - outputs the the rightmost value if all of the arguments evaluate to #t. Outputs #f otherwise.<br />
STk> (and 0 1 2 3)<br />
3<br />
<br />
; or - outputs the the first value that evaluates to #t. Outputs #f otherwise.<br />
STk> (or 0 1 2 3)<br />
0<br />
<br />
; equal - tests if symbols are the same<br />
STk> (equal? 'foo 'bar)<br />
#f<br />
STk> (equal? 'foo 'foo)<br />
#t<br />
<br />
STk> (list? 'foo)<br />
#f<br />
STk> (list? '(1 2 3))<br />
#t<br />
STk> (list? '())<br />
#t<br />
<br />
STk> (null? '(1 2 3))<br />
#f<br />
STk> (null? ())<br />
#t<br />
<br />
; member? - tests if a symbol is in a list EDIT: NOT BUILT IN (BUT SUPER USEFUL SEE FOLLOWUP)<br />
STk> (member? 'quick '(the quick brown fox jumped over the lazy dog))<br />
#t<br />
<br />
; number? - checks if input is a number<br />
STk> (number? 42)<br />
#t<br />
STk> (number? #t)<br />
#f<br />
<br />
; remainder - computes the remainder of the first number divided by the second<br />
STk> (remainder 100 21)<br />
16</pre><br />
<br />
== Streams ==<br />
== Logic ==<br />
=== Quick Guide to Logic Programming ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=2524 Source: Spring 2014 Piazza (2524)]<br />
''Note: Someone should convert this from scmlog to Logic notation''<br />
<br />
'''Here's something I wrote a long time ago. The logic interpreter scmlog still exists and you should be able to access it using your cs61a-xx account. Post a followup if you have any questions. Hope this helps!'''<br />
<br />
<br />
'''Introduction:''' logic programming is a completely different way to think about telling computers to do stuff. Instead of telling the computer what to compute, you give the computer facts and ask it questions. The computer does its own thinking given the facts and the question, and then returns an answer.<br />
<br />
'''Table of Contents'''<br />
- Introduction<br />
- What is Logic Programming<br />
- - Giving Facts and Asking Questions<br />
- - More Complicated Facts and Questions<br />
- How to write Logic Programs<br />
- - Common Pitfalls<br />
- More Resources<br />
<br />
<br />
'''What is Logic Programming?'''<br />
Logic Programming is a way to ask the computer questions and get answers without telling it explicitly how to reach the conclusion. Kind of like this: http://youtu.be/tpKx7Oi0oeM<br />
<br />
Anyways, we have a logic programming interpreter called Scmlog. It interprets a simpler version of the logic programming language, Prolog. The first few things to understand about it is that '''SCMLOG IS NOT SCHEME'''. It just happens to look like it. Scmlog is its own language and so it has its own rules. You can't program in Scmlog like you would in Scheme or Python, so it might be good to forget what you know about those languages for a second.<br />
<br />
When you fire up scmlog (type scmlog into a terminal on the school computers), you get this prompt:<br />
<pre>star [501] ~ # scmlog<br />
scmlog (Prolog in Scheme), v. 0.2<br />
Type 'help' for help; 'quit' to exit.<br />
?- </pre><br />
You can interact with this prompt in two ways:<br />
# Give facts - "Let's tell the computer some things it should know!"<br />
# Ask questions - "Let's ask the computer questions about the things we told it about!"<br />
<br />
'''Giving Facts and Asking Questions'''<br />
A basic fact takes this form: <code>(fact (assertion))</code>, '''Where each assertion is simply a relation between things.'''<br />
<br />
For example: <code>(fact (likes potstickers brian))</code> relates three ideas, liking something, potstickers, and some guy name Brian. "Brian likes potstickers."<br />
Note that we put the relation first, and then the parties the relation acts upon. In this example, "likes" is the relation, it hooks up "brian" and "potstickers". Here's some more:<br />
<pre>?- (fact (likes potstickers brian)) <br />
?- (fact (likes potstickers andrew)) <br />
?- (fact (likes the_beatles brian)) <br />
?- (fact (likes the_beatles andrew)) <br />
?- (fact (likes led_zeppelin andrew)) <br />
?- (fact (dislikes led_zeppelin brian))</pre><br />
Now that we've given the computer a bunch of facts, how do we ask questions about them? Just replace "fact" with a "?", and replace any part of the relation (besides the relation itself) with a variable prefixed by an underscore. This is called "querying":<br />
<pre>?- (? (likes _what brian))<br />
_what : potstickers<br />
More? <br />
_what : the_beatles<br />
More? <br />
?- (? (likes potstickers _who))<br />
_who : brian<br />
More? <br />
_who : andrew<br />
More? <br />
?- (? (dislikes _what brian))<br />
_what : led_zeppelin<br />
More?<br />
?- (? (likes led_zeppelin andrew))<br />
Yes.<br />
?- (? (dislikes the_beatles _who))<br />
No.</pre><br />
Notice:<br />
* We can query any part of the assertion, besides the relation itself (can't replace "likes" with a variable).<br />
* All possible answers to the question show up.<br />
* If Scmlog couldn't find a fact that matched your query, it'll say "No."<br />
* Asking a question without any variables essentially asks if that fact exists. To which Scmlog will answer "Yes." or "No."<br />
<br />
'''More Complicated Facts and Questions'''<br />
Now this isn't the whole picture. We also have the ability to make more powerful assertions via variables, hypotheses, and conclusions:<br />
<br />
<code>(fact (ancestor _x _y) (parent _x _y))</code> "X is an ancestor of Y if X is a parent of Y"<br />
<br />
Here _x and _y are variables like usual. However, we have two parts to this fact, the conclusion ("X is an ancestor of Y") and the hypothesis ("X is a parent of Y"). We can have more than one hypotheses, and they can be any kind of query. Here's ancestor in action:<br />
<pre>?- (fact (parent george paul))<br />
?- (fact (parent martin george))<br />
?- (fact (parent martin martin_jr))<br />
?- (fact (parent martin donald))<br />
?- (fact (parent george ann))<br />
?- (fact (ancestor _X _Y) (parent _X _Y))<br />
?- (fact (ancestor _X _Y) (parent _X _Z) (ancestor _Z _Y))<br />
?- (? (ancestor paul george))<br />
No.<br />
?- (? (ancestor george paul))<br />
Yes.<br />
?- (? (ancestor george george))<br />
No.<br />
?- (? (ancestor martin paul))<br />
Yes.</pre><br />
Now there's one more thing you need to know about Scmlog. Scmlog knows about pairs and lists:<br />
<pre>?- (fact (lst (1 2 3)))<br />
?- (? (lst _x))<br />
_x : (1 2 3)<br />
More?<br />
?- (? (lst (1 . _x)))<br />
_x : (2 3)<br />
More? <br />
?- (? (lst (1 2 . _x)))<br />
_x : (3)<br />
More? <br />
?- (? (lst (_x . _y)))<br />
_x : 1<br />
_y : (2 3)<br />
?- (fact (my_pair (2 . 3)))<br />
?- (? (my_pair _y))<br />
_y : (2 . 3)<br />
?- (? (my_pair (2 . _x)))<br />
_x : 3</pre><br />
<br />
'''How to write Logic Programs'''<br />
<br />
The trick behind writing logic programs is to forget everything you know about programming. You instead want to focus on the relation you're trying to establish. Take append for example:<br />
<pre>?- (fact (append () _b _b))<br />
?- (fact (append (_x . _rest) _b (_x . _z)) (append _rest _b _z))<br />
?- (? (append (1 2 3) (3 2 1) _answer))<br />
_answer : (1 2 3 3 2 1)<br />
?- (append (1 2) 3 (1 2 3))<br />
Huh?<br />
?- (? (append (1 2) 3 (1 2 3))<br />
)<br />
No.<br />
?- (? (append (1 2) (3) (1 2 3))<br />
)<br />
Yes.<br />
?- (? (append (1 2) (3) (1 2 3)))<br />
Yes.<br />
?- (? (append _x (3 4 9) (1 0 3 2 3 4 9)))<br />
_x : (1 0 3 2)<br />
More? <br />
?- (? (append _x _y (2 1)))<br />
_x : ()<br />
_y : (2 1)<br />
More? <br />
_x : (2)<br />
_y : (1)<br />
More? <br />
_x : (2 1)<br />
_y : ()</pre><br />
<br />
What is append? Well the first fact establishes that if we append an empty list to something, the result is that something.<br />
The next fact is the meat of the code. You can almost think of this as a recursive relation. Any append relation between some three lists a b c must also fulfill another append relation between the rest of a, b, and the rest of c, respectively. Think about why that will always be true for all inputs. If the example above doesn't make sense to you, see the following:<br />
<br />
<br />
'''Common Pitfalls'''<br />
<br />
'''- One of the biggest issues people have with writing logic programs is that they don't realize that the variables don't work like they do in Scheme or Python. All Scmlog ever does is pattern match:'''<br />
<pre>?- (fact (x (0 1 8)))<br />
?- (fact (x (1 8 4)))<br />
?- (fact (x (9 4 4)))<br />
?- (fact (x (3 0 8)))<br />
?- (? (x _y))<br />
_y : (0 1 8)<br />
More? <br />
_y : (1 8 4)<br />
More? <br />
_y : (9 4 4)<br />
More? <br />
_y : (3 0 8)<br />
?- (? (x (0 1 . _z)))<br />
_z : (8)</pre><br />
<br />
- SCMLOG IS NOT SCHEME. SCMLOG IS NOT PYTHON.<br />
<br />
More Resources:<br />
* http://www-inst.eecs.berkeley.edu/~cs61a/sp12/lectures/prolog/ReadMe<br />
* http://www-inst.eecs.berkeley.edu/~cs61a/sp12/lectures/prolog/<br />
* http://www-inst.eecs.berkeley.edu/~cs61a/sp12/discussion/week14/<br />
* http://www-inst.eecs.berkeley.edu/~cs61a/fa13/slides/31-Logic_6pp.pdf<br />
<br />
=== Logic Mathematics ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=3050 Source: Spring 2014 Piazza (3050)]<br />
<br />
'''Student Question'''<br />
<br />
Can someone explain the intuition behind the implementation of the increments and the addition facts? The following is from Mark's website<br />
<br />
<pre>(fact (increment 0 1))<br />
(fact (increment 1 2))<br />
(fact (increment 2 3))<br />
(fact (increment 3 4))<br />
(fact (increment 4 5))<br />
(fact (increment 5 6))<br />
(fact (increment 6 7))<br />
(fact (increment 7 8))<br />
(fact (increment 8 9))<br />
(fact (increment 9 10))<br />
(fact (increment 10 11))<br />
(fact (increment 11 12))<br />
(fact (increment 12 13))<br />
<br />
(fact (add 1 ?x ?x+1)<br />
(increment ?x ?x+1))<br />
<br />
(fact (add ?x+1 ?y ?z+1)<br />
(increment ?x ?x+1)<br />
(increment ?z ?z+1)<br />
(add ?x ?y ?z))<br />
<br />
(query (add 2 4 6))<br />
; expect Success!</pre><br />
<br />
'''Student Answer'''<br />
<br />
First, we state a bunch of facts that denote relations between a number and the number that follows it (e.g. 1 and 2, 2 and 3, and so on). This is the increment fact.<br />
<br />
Then, we state a "base" fact, which is:<br />
<pre>(fact (add 1 ?x ?x+1)<br />
(increment ?x ?x+1))</pre><br />
In English, this fact states that some value <code>?x</code> added to 1 will give us some value <code>?x+1</code> (<code>x+1</code> is a valid variable name in Logic) '''if and only if''' the fact (<code>increment ?x ?x+1</code>) is true. As an example, <code>(add 1 2 3)</code> is true '''if and only if''' (<code>increment 2 3</code>) is true. This is true because of the increment facts we stated before.<br />
<br />
Now, to deal with additions that aren't just the sum of two numbers in which one number is a 1, we need:<br />
<pre>(fact (add ?x+1 ?y ?z+1)<br />
(increment ?x ?x+1)<br />
(increment ?z ?z+1)<br />
(add ?x ?y ?z))</pre><br />
I will try to give an intuition as to what is happening here.<br />
<br />
We state our fact: that two numbers, <code>?x+1</code> and <code>?y</code>, will add up to some number <code>?z+1</code> if and only if the following 3 hypotheses are true:<br />
* (<code>increment ?x ?x+1</code>) - "There exists some number <code>?x</code> that is 1 less than <code>?x+1</code>, and"<br />
* (<code>increment ?z ?z+1</code>) - "There exists some number <code>?z</code> that is 1 less than <code>?z+1</code>, and"<br />
* (<code>add ?x ?y ?z</code>) - "The numbers <code>?x</code> and <code>?y</code> will add up to <code>?z</code>."<br />
<br />
Consider the example of:<br />
<pre>(query (add 2 4 6))</pre><br />
Here's an idea of what's happening when Logic tries to match the query with the facts you've stated.<br />
* ?x+1 = 2, ?y = 4, ?z+1 = 6<br />
* It finds a match for <code>?x = 1</code>, since (<code>increment ?x ?x+1</code>) gives <code>?x = 1</code> because <code>?x+1 = 2</code><br />
* It finds a match for <code>?z = 5</code>, since (<code>increment ?z ?z+1</code>) gives <code>?z = 5</code> because <code>?z+1 = 6</code><br />
* It then checks for a match for (<code>add ?x ?y ?z</code>) which in this case is (<code>add 1 4 5</code>). This goes to our first "base" fact for add. (<code>add 1 4 5</code>) is a success because (<code>increment 4 5</code>) is a true fact [refer again to the "base fact" to see why this is the case]. This is also where we would get a "Failed.", if it turns out that (<code>increment ?x ?x+1</code>) wasn't actually true!<br />
* Hence, all 3 of our hypotheses are true, and so (<code>query (add 2 4 6)</code>) is a success!<br />
<br />
In this example, we only have to recurse once to get to our "base" fact. In other examples, where <code>?x+1</code> is not 2, but some number greater, such as 5, we will have to recurse 4 whole times to get to 1, at which point our "base" fact is reached. <br />
<br />
This recursion is similar to this idea in mathematical equations:<br />
<pre>x + y = z</pre><br />
is the same as<br />
<pre>(x - 1) + y = (z - 1)</pre><br />
is the same as<br />
<br />
<pre>(x - 2) + y = (z - 2)</pre?<br />
and so on... In Logic, we stop when we find that the first term (x) is 1, and then we use our increment facts to determine if the original statement is true, because all of these equations are equivalent.<br />
<br />
== Python syntax and semantics ==<br />
=== <code>print</code> vs <code>return</code> ===<br />
----<br />
==== Andrew's tips ====<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=779 Source: Spring 2014 Piazza (779)]<br />
<br />
Remember the differences between return and print.<br />
* <code>return</code> can only be used in a <code>def</code> statement. It returns a value from a function. Once Python evaluates a <code>return</code> statement, it immediately exits the function.<br />
* <code>print</code> is a function that displays its argument on the screen. It always returns <code>None</code>.<br />
<br />
Examples:<br />
<pre>def foo1(x):<br />
return x<br />
<br />
def foo2(x):<br />
print(x)<br />
<br />
>>> foo2(1) # In foo2, we print 1 ourselves using the print function<br />
1<br />
>>> foo1(1) # HERE, THE PYTHON INTERPRETER PRINTS THE RETURN VALUE OF FOO1. CANNOT STRESS HOW IMPORTANT TO UNDERSTAND THIS<br />
1<br />
>>> foo1(1) + 1<br />
2<br />
>>> foo2(1) + 1<br />
1<br />
Traceback (most recent call last):<br />
File "<stdin>", line 1, in <module><br />
TypeError: unsupported operand type(s) for +: 'NoneType' and 'int'</pre><br />
<br />
=== Function decorators ===<br />
<br />
----<br />
<br />
==== How function decorators work ====<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=638 Source: Spring 2014 Piazza (638)]<br />
<br />
'''Student Question'''<br />
<br />
I'm having difficulties understanding what exactly a function decorator is. Can someone elaborate and potentially provide me with an example other than the one in the readings?<br />
<br />
'''Instructor Answer'''<br />
<br />
So imagine you wanted your functions to print their arguments before they executed them. Here's one way to do this.<br />
<pre>def loud(fn):<br />
def new_fn(*args):<br />
print(args)<br />
return fn(*args)<br />
return new_fn </pre><br />
Here's a function loud that takes in a function and returns a new function that when called, prints out its arguments, and then does what the old function does.<br />
<br />
For example:<br />
<pre>def sq(x):<br />
return x * x<br />
>>> sq(4)<br />
16<br />
>>> sq = loud(sq) # replace the old square with our loud one.<br />
>>> sq(4)<br />
(4,)<br />
16</pre><br />
A function decorator does the same thing as the above. Assuming loud is defined, we can do this:<br />
<pre>@loud<br />
def sq(x):<br />
return x * x<br />
<br />
>>> sq(4)<br />
(4,)</pre><br />
<br />
== Student guides ==<br />
=== How to learn computer science ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=241 Source: Spring 2014 Piazza (241)]<br />
<br />
If you've never programmed before, or if you've never taken a class quite like 61A before, things right now might be scary. Everything is strange and new and there quite a lot to take in all at once. So if you're having a hard time so far, here are a few articles that might help.<br />
<br />
Note: these articles are pretty long, so feel free to read them in multiple sittings.<br />
<br />
'''At the beginning, everything seems a bit scary in CS'''. Michelle Bu, a Berkeley alum and a crazy good hacker, shares one of her experiences when she was a wee n00b in [http://blog.michellebu.com/2013/03/21-nested-callbacks/ 21 Nested Callbacks].<br />
<br />
'''Start here!''' [http://www.jamesmaa.com/2013/08/26/a-beginners-guide-to-computer-science/ "A Beginner's Guide to Computer Science"] Written by Berkeley's own James Maa. James is known for his killer walkthroughs (check out his Productivity guide). This article gives you some background on learning CS and then provides a practical guide on how to learn effectively.<br />
<br />
'''How do we learn?''' Mark Eichenlaub explains in this [http://www.quora.com/Learning/Do-grad-school-students-remember-everything-they-were-taught-in-college-all-the-time/answer/Mark-Eichenlaub Introduction to Learning Theory]. This is quite possibly the best introduction to Learning Theory.<br />
<br />
'''Sometimes, you're stuck and you end up really, really frustrated.''' Marcus Geduld explains [http://www.quora.com/Why-do-we-get-frustrated-when-learning-something/answer/Marcus-Geduld Why do we get frustrated when learning something?]<br />
<br />
=== Quick guide on getting unstuck ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=1264 Source: Quick Guide on Getting Unstuck (Retrieved June 16th, 2014)]<br />
<br />
A major frustration you might encounter in 61A is when you stare at a homework problem and have no idea where to start. Or you write some code and it doesn't pass the doctests, but now what? You work at it for a while, but next thing you know, you've been stuck for hours on the the same problem and have little to show about it.<br />
<br />
So here's a checklist of things you can do when you're stuck. Experienced programmers do these things almost naturally (because of how much practice they've had being stuck), and so while they get stuck just as much as your or I, they always know what to do next.<br />
<br />
# Do I understand what the problem is asking?<br />
## If not, which part of the problem is confusing me?<br />
### Identify the exact sentences/phrases/words/etc.<br />
## Check the given examples. Do they make sense to me?<br />
## Can I come up with my own examples? '''A good indicator that you understand the question is that you can come up with some nontrivial examples of how the function works.'''<br />
# What concepts should I use here?<br />
## Do I understand the concepts? '''Can I explain the concept in English to one of my friends such that they get it?'''<br />
### If not, go back and relearn the specific concepts that are unclear (through discussion, lab, lecture, etc.) Don't read the entire book in order to solve one problem..<br />
## How do I apply the concept to the given problem?<br />
# Write your code and test it.<br />
## Use doctests, '''BUT ALSO LOAD IT INTERACTIVELY (python3 -i ...)'''<br />
### '''Saying "my function works because the doctests pass" is a lot like saying "this airplane will fly because it has wings."'''<br />
## If your code breaks, ask yourself:<br />
### Does it error? Is it a....<br />
#### Syntax error? If so, find the syntax bug and fix it.<br />
#### Logic error? Is it something weird that you don't understand? (E.g. cannot add integer and tuple)<br />
### Why did it do that? Why didn't it do what I expected? Trace through the code by hand with an example (sample values) you came up with in step 0. '''Add calls to <code>print</code> in order to figure out how your function is handling the arguments.'''<br />
# Am I missing a trick?<br />
## Oftentimes you've never seen this type of problem before. This is expected on homework (and this is why homework can take a long time) because if you see it on the homework, then you will be familiar with it on the exam and when you program for fun and profit. <br />
## The key here is just to learn the trick however you need to.<br />
### Stare at it yourself<br />
### Stare at it with others (peers in the class)<br />
### Ask on PIazza what the approach is.<br />
### Stare at it with the TAs/lab Assistants<br />
## '''Once you figure it out, remember the trick so that you can use it next time.'''<br />
# At any point you identify what you're stuck on, you can begin to resolve it.<br />
## Use the tips above. Try things out on the interpreter. Review the lecture/discussion/labs/etc. Do whatever helps you get a better understanding of the problem.<br />
## Once you have something specific that you're stuck on, you can ask other people in the class.<br />
### '''Don't be afraid to ask. Everyone gets stuck and feels stupid sometimes. However, you get to choose how you react to it.'''<br />
### '''At the same time, it really helps to work with people who are on about the same level in the course.'''<br />
## Look on Piazza. Ask questions if yours hasn't come up yet. Be that awesome guy/girl who helps answer questions.<br />
## You can ask the TA if all else fails. We are here to help you learn!<br />
<br />
Here is an old algorithm for studying for tests (the final in this case), salvaged from the sands of time:<br />
<pre>For each topic on the final, find problems on them and do them.<br />
If you can solve them on your own, move on.<br />
Else if you are stuck, look at the solution and figure out if you<br />
are missing a trick or if you do not understand the concepts.<br />
If the problem is that you are stuck on some random trick,<br />
just learn the trick.<br />
Stare at the solutions, ask Piazza, your TA, etc.<br />
Questions you should ask at this stage:<br />
What is the problem asking me to do?<br />
How was I suppose to follow the instructions<br />
to solve the problem?<br />
What part of the problem do I not understand?<br />
What is the fastest way to clear up that misunderstanding?<br />
Then if you think you are still stuck conceptually, review<br />
and learn the concept, however you learn best.<br />
Suggestions for picking up concepts quickly (~1-2 hours):<br />
Discussion notes typically have a very concise recap of the<br />
thing they are going over.<br />
There are guides for particularly tricky things on Piazza,<br />
like Logic, Pairs and Lists in Scheme, etc.<br />
Find them and go over them.<br />
Ask a TA: "what is the best way to learn X?"<br />
If these do not work and you are still shaky after an hour<br />
or two, it might be worth watching a lecture or reading<br />
the notes.</pre><br />
<br />
== Composition ==<br />
=== General style guidelines from 61A website ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=149 Source: Spring 2014 Piazza (149)]<br />
<br />
'''Student Question'''<br />
<br />
Are we required to add any comments to our code to say what a function does, etc.? And does clarity of code count for this project, in which case should we write comments at the end of not-so-clear statements? Thanks.<br />
<br />
'''Student Answer'''<br />
<br />
Docstrings of each function are already provided. If you add a helper function, you should write a docstring for it.<br />
<br />
The [http://inst.eecs.berkeley.edu/~cs61a/sp14/style_guide.html#comments style guide on the course website] advises: "Your actual code should be self-documenting -- try to make it as obvious as possible what you are doing without resorting to comments. Only use comments if something is not obvious or needs to be explicitly emphasized"<br />
<br />
'''Instructor Answer'''<br />
<br />
You should always aim to make your code "self-documenting," meaning it is clear what your code is doing without the aid of comments. You should try to keep the number of comments to a minimum, but if there are lines which you think are unclear/ambiguous, feel free to add a comment.<br />
<br />
All projects in this class contain a 3 point component that is judged solely on your code "composition" -- i.e. whether your code is clear, concise, and easy to read.<br />
<br />
=== Simplifying code ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=1116 Source: Spring 2014 Piazza (1116)]<br />
<br />
Hi everyone, here's some tips about certain functions in Python that can greatly simplify your code for the Trends project.<br />
<br />
'''Sorting keys'''<br />
<br />
You should be familiar with the max and min functions in python, which can take in many arguments and return the maximum value.<br />
<br />
<pre>>>> max(1,3,2)<br />
3</pre><br />
<br />
These functions can also take in lists:<br />
<br />
<pre>>>> min([1,5,1,6])<br />
1</pre><br />
<br />
(In fact they can take in any ''iterable'' and return the maximum/minimum value)<br />
<br />
These functions work because Python knows how to compare the elements in the list (they are all integers). But what if the elements in the list are not integers? Fortunately, there is a way for you to tell Python how to turn each element of the list into a number that it can understand.<br />
<br />
Lets start with an example. Lets say you have a list of strings, and want to find the shortest string in the list. Here's what you can do:<br />
<br />
<pre>>>> min(['hihi', 'bye', 'a', 'zebra'], key=len)<br />
'a'</pre><br />
<br />
Notice the new keyword argument key we are passing into the min function. key is a function that min applies to each element of the list. In this case, the key is the len function, which returns the length of each string. Applying the key function to each element will return a cooresponding integer, which Python can easily use to find the minimum element.<br />
<br />
You can also use keys in the <code>sorted</code> function too, which returns a sorted list of its inputs, based on the key function passed in.<br />
<br />
<pre>>>> sorted(['hihi', 'bye', 'a', 'zebra'], key=len)<br />
['a', 'bye', 'hihi', 'zebra']</pre><br />
<br />
We can have more complex key functions. Here we sort a list of people by their age, which is the second element in the tuple. A key function, once defined, works for sorted, min and max:<br />
<br />
<pre>>>> names = [('Alice', 19, 'F'), ('Bob', 5, 'M'), ('Charlie', 12, 'M')]<br />
>>> get_age = lambda name: name[1]<br />
>>> sorted(names, key=get_age)<br />
[('Bob', 5, 'M'), ('Charlie', 12, 'M'), ('Alice', 19, 'F')]<br />
>>> max(names, key=get_age)<br />
('Alice', 19, 'F')</pre><br />
<br />
'''Dictionary default values'''<br />
<br />
Suppose we have a dictionary mapping names to counts:<br />
<br />
<pre>>>> d = {'apples': 1, 'pears': 9000}</pre><br />
<br />
If we want to add a new pear to the dictionary, we can use:<br />
<br />
<pre>>>> d['pears'] = d['pears'] + 1<br />
>>> d<br />
{'apples': 1, 'pears': 9001}</pre><br />
However we cannot use the same code to add a new item that is not already in the dictionary.<br />
<br />
<pre>>>> d['oranges'] = d['oranges'] + 1<br />
Traceback (most recent call last):<br />
...<br />
KeyError: 'oranges'</pre><br />
<br />
To solve this problem, we have to use <code>dict.setdefault(key, default)</code>. If <code>key</code> is in <code>dict</code>, it will return <code>dict[key]</code>. If not, it will insert <code>key</code> with a value of <code>default</code> and return <code>default</code>. Now we can write:<br />
<br />
<pre>>>> d['oranges'] = d.setdefault('oranges', 0) + 1<br />
>>> d<br />
{'oranges': 1, 'apples': 1, 'pears': 9001}<br />
>>> d['oranges'] = d.setdefault('oranges', 0) + 1<br />
>>> d<br />
{'oranges': 2, 'apples': 1, 'pears': 9001}</pre><br />
<br />
There's actually a even better way of doing this. If you are curious to find out, look up <code>collections.defaultdict</code>.<br />
<br />
'''For loops'''<br />
<br />
If you are iterating through a list and want to get both the item and the index the item is at, the built-in function enumerate is helpful here.<br />
<br />
<pre>>>> a = ["apple", "pear", "orange"]<br />
>>> for index, fruit in enumerate(a):<br />
... print(index, fruit)<br />
...<br />
0 apple<br />
1 pear<br />
2 orange</pre><br />
<br />
You can iterate through each key-value pair in a dictionary with dictionary.items. This is useful if you want to access both the key and the value at the same time.<br />
<br />
<pre>>>> prices = {"apple": 3, "pear": 5, "orange": 20}<br />
>>> for fruit, price in prices.items():<br />
... print(fruit, price)<br />
...<br />
apple 3<br />
pear 5<br />
orange 20</pre><br />
<br />
Hope this helps for the project!<br />
<br />
=== Programming style in scheme ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=2068 Source: Spring 2014 Piazza (2068)]<br />
<br />
Since Scheme has no rules on whitespace and indentation, you could technically write all your Scheme in one line like this:<br />
<pre>(define (fib n) (if (< n 1) n (+ (fib (- n 1)) (fib (- n 2)))))</pre><br />
<br />
But that would be terrible and everyone who had to read your code would hate you for it. Here is a more reasonable version:<br />
<pre>(define (fib n)<br />
(if (< n 1) ; Arguments to if are flush<br />
n ; Each argument gets a new line<br />
(+ (fib (- n 1)) ; Sometimes it makes to insert a newline<br />
(fib (- n 2))))) ; so that you can see arguments side by side</pre><br />
<br />
Remember that code is primarily for humans to read and incidentally for computers to run.<br />
<br />
Here are some more examples:<br />
<pre>(define (deep-map f lst)<br />
(cond ((null? lst) lst)<br />
((list? (car lst))<br />
(cons (deep-map f (car lst))<br />
(deep-map f (cdr lst))))<br />
(else<br />
(cons (f (car lst))<br />
(deep-map f (cdr lst)))) ))<br />
<br />
STk> (deep-map (lambda (x) (* x x)) (list 1 2 (list (list 3) 4)))<br />
(1 4 ((9) 16))</pre><br />
<br />
<pre>(define (reverse lst)<br />
(define (helper lst result)<br />
(if (null? lst)<br />
result<br />
(helper (cdr lst)<br />
(cons (car lst) result)) ))<br />
(helper lst ()) )<br />
<br />
STk> (reverse (list 1 2 3))<br />
(3 2 1)</pre><br />
<br />
=== ucb.py's <code>trace</code> method ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=3000 Source: Spring 2014 Piazza (3000)]<br />
<br />
'''Student Question'''<br />
<br />
How does the trace in ucb.py actually work?<br />
<br />
'''Student Answer'''<br />
<br />
It's actually very similar to the <code>printed</code> function that was defined in the Hog spec!<br />
<br />
<pre>def printed(fn):<br />
def print_and_return(*args):<br />
result = fn(*args)<br />
print('Result:', result)<br />
return result<br />
return print_and_return<br />
</pre><br />
<br />
The main idea is still the same in <code>trace</code> -- we want to figure out all the arguments and save the result of calling the function with those arguments (so we can print it before returning it). One (sort of) major improvement is <code>**kwds</code>. Just like how <code>*args</code> collected all the "positional arguments", <code>**kwds</code> captures all the "keyword arguments" (the ones of the form <code>param='some_val'</code>). This can be a bit confusing, but a couple of examples might help!<br />
<br />
<pre>&gt;&gt;&gt; def add_three(a, b, c):<br />
... return a + b + c<br />
&gt;&gt;&gt; add_three(1, 2, 3) # all arguments are positional (normal)<br />
&gt;&gt;&gt; add_three(1, 2, c=3) # a, b are positional arguments, c is a keyword argument<br />
<br />
&gt;&gt;&gt; def fn(*args, **kwargs):<br />
... print(args)<br />
... print(kwargs)<br />
&gt;&gt;&gt; fn(1, 2, 3)<br />
(1, 2, 3)<br />
{}<br />
&gt;&gt;&gt; fn(1, k=2)<br />
(1,)<br />
{'k' : 2}<br />
&gt;&gt;&gt; fn(a=1, b=2, c=3)<br />
()<br />
{'a':1, 'b':2, 'c':3}<br />
</pre><br />
<br />
Since there are only two types of arguments, having both <code>*args</code> and <code>**kwds</code> covers all our bases. If we passed <code>printed</code> a keyword argument, it could cause an error!<br />
<br />
Everything else in <code>trace</code> just makes the output prettier and more helpful. <code>trace</code> uses the <code>_PREFIX</code> global variable to keep track of how far to indent the next print statement. It catches exceptions and prints them out, before re-raising that exception. It also uses some Python black magic to figure out the name of the function so we can print <code>some_fn</code> instead of <code>&lt;function some_fn at 0x...&gt;</code>.<br />
<br />
If there's a particular aspect of <code>trace</code> that you're confused about, feel free to post a followup!<br />
<br />
== Debugging ==<br />
== Miscellaneous ==<br />
=== Andrew Huang's tips ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=779 Source: Spring 2014 Piazza (779)]<br />
<br />
Order of evaluation matters. The rules for evaluating call expressions are<br />
<br />
# Evaluate the operator<br />
# Evaluate the operands<br />
# Call the operator on the operands (and draw a new frame...)<br />
<br />
For example:<br />
<pre>def baz():<br />
print("this was first")<br />
def bar(x):<br />
print(x)<br />
return lambda x: x * x<br />
return bar # baz is a function that when called, returns a function named bar<br />
<br />
>>> baz() # the operator is baz, there are no operands<br />
this was first<br />
<function bar at 0x2797e20><br />
>>> baz()("this was second") # the operator is baz(), the operand is "this was second"<br />
this was first<br />
this was second<br />
<function <lambda> at 0x2120e20><br />
>>> baz()("this was second")(3) # the operator is baz()("this was second"), the operand is 3<br />
this was first<br />
this was second<br />
9<br />
>>> def bar(x):<br />
... print(x)<br />
... return 3<br />
... <br />
>>> baz()("this was second")(bar("this was third")) # the operator is baz()("this was second"), the operand is bar("this was third")<br />
this was first<br />
this was second<br />
this was third<br />
9</pre><br />
<br />
In order to solve any problem, you must first understand what the problem is asking. Often times it helps to try to explain it concisely in English. It also helps to come up with small examples. For example:<br />
<br />
<pre>def mouse(n):<br />
if n >= 10:<br />
squeak = n // 100<br />
n = frog(squeak) + n % 10<br />
return n<br />
<br />
def frog(croak):<br />
if croak == 0:<br />
return 1<br />
else:<br />
return 10 * mouse(croak+1)<br />
<br />
mouse(21023508479)</pre><br />
<br />
So the goal is to figure out what <code>mouse(21023508479)</code> evaluates to.<br />
<br />
One way is to just step-by-step evaluate this, as an interpreter would.<br />
<br />
Another way, is to understand what the functions are doing.<br />
<br />
Looking at <code>mouse</code>, we see that it takes in a number and outputs that same number if it is smaller than 10. otherwise, it'll return something weird. In order to understand that weird thing, we have to understand what <code>frog</code> is doing. <code>frog</code> takes in a number and if that number is <code>0</code>, return <code>1</code>. Otherwise, return ten times <code>mouse(croak+1)</code>. Well, this is still confusing. Let's try a small example.<br />
<pre>>>> mouse(357)<br />
47<br />
>>> mouse(123)<br />
23<br />
>>> mouse(1234)<br />
44<br />
>>> mouse(12345)<br />
245</pre><br />
There is a pattern. We notice that the resulting number is composed of every other digit of the original, plus one (except for the last one.)<br />
So <code>21023508479</code> is <code>[2+1][0+1][3+1][0+1][4+1][9] = 314159</code>. Can you see how the code reflects that?<br />
However in this particular example, the pattern is definitely tricky to find here, so it might make more sense to brute force it.<br />
<br />
<br />
Remember for recursion, you always need to find three things:<br />
* One or more base cases<br />
* One or more was to reduce the problem<br />
* A way to solve the problem given solutions to smaller problems<br />
For example, the discussion notes, we asked you to write count_stairs. This function takes in n, the number of steps, and returns all the ways you can climb up them if at each step, you can take either one or two steps.<br />
<br />
* Base cases: if we consider n to be the number of steps left to climb, then it makes sense that if there is 1 step left, then there is exactly one way. If there are two steps left, then there are exactly 2 ways (1 step, 1 step, or two steps). Why do we need two base cases here?<br />
* We can make the problem smaller by reducing the n. At each step, we can take one step (resulting in count_stairs(n-1)) or two steps (count_stairs(n-2)).<br />
* Assuming we get the solutions to the two recursive calls, we should add them together to get all the ways we can climb the stairs.<br />
<br />
Thus we end up with<br />
<pre>def count_stairs(n):<br />
if n <= 2:<br />
return n<br />
else:<br />
return count_stairs(n-1) + count_stars(n-2)</pre><br />
Notice that at each stair step, we either take one step or two steps. This is a common pattern in tree recursion. Look through Discussion 3 for more info.<br />
<br />
=== Y combinators (in Scheme) ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=2450 Source: Spring 2014 Piazza (2450)]<br />
<br />
'''Student Question'''<br />
<br />
Can someone explain this to me?<br />
<pre>scm> (((lambda (f) (lambda (x) (f f x)))<br />
(lambda (f k) (if (zero? k) 1 (* k (f f (- k 1)))))) 5)</pre><br />
I've edited the code as follows:<br />
<br />
<pre>(<br />
(<br />
(lambda (f) <br />
(lambda (x) (f f x))<br />
)<br />
(lambda (f k) <br />
(if (zero? k) 1 <br />
(* k (f f (- k 1)))<br />
)<br />
)<br />
) 5<br />
)</pre><br />
<br />
My understanding is that the second lambda function is passed as the first <code>f</code> in the first lambda function and the <code>5</code> is passed in as <code>x</code>. But does that mean <code>f f x</code> becomes the second lambda function with itself and <code>x</code> passed as the arguments to <code>(f k)</code>?<br />
<br />
'''Student Answer'''<br />
<br />
You're on the right track. The first lambda function is a higher order function that takes in a function, and then returns a function that takes one argument. It's actually the third lambda that is then passed into the first lambda (currying!) and then 5 is then passed into the resulting function.<br />
<br />
In case you're curious, this is the Python equivalent:<br />
<br />
<pre>>>> (lambda f: (lambda x: f(f, x))) (lambda f, k: 1 if k == 0 else (k * f(f, k - 1)))(5)</pre><br />
Which is then equivalent to:<br />
<br />
<pre>>>> def func1(f):<br />
def func2(x):<br />
return f(f, x)<br />
return func2<br />
>>> def func3(f, k):<br />
if k == 0:<br />
return 1<br />
else:<br />
return k * f(f, k - 1)<br />
>>> func1(func3)(5)<br />
120</pre><br />
By the way, this is just a fancy way of recursively calculating the factorial using only lambda functions. If you're still curious as to how this works, you could try this in Python tutor. Except I would recommend calculating 3! instead of 5, because it's a lot of frames.<br />
<br />
'''Instructor Answer'''<br />
<br />
Maybe it will look a little nicer in Python:<br />
<br />
<pre>(lambda f: lambda x: f(f, x))(lambda g, k: 1 if k == 0 else (k * g(g, k-1)))(5)</pre><br />
Or maybe not.<br />
<br />
So the idea is, you define a lambda function that takes a function <code>f</code>, and that returns a lambda function that takes an argument <code>x</code> and returns <code>f(f, x)</code>. Then, you call this lambda function you just defined on another lambda function (let's call this <code>func</code>) that takes a function g and another argument k, and is basically the factorial function. This first call returns the inner lambda of the first part, and when that's called with <code>5</code> you're essentially calling <code>func(func, 5)</code>. The chain of recursive calls then works as follows:<br />
<br />
<pre>func(func, 5) -> 5 * func(func, 4) -> 5 * 4 * func(func, 3) -> ... -> 120</pre><br />
In functional programming theory, this is known as a Y Combinator, and it is how you achieve recursion with just lambda functions. If you're wondering why we need <code>func</code> to take in a function as the first parameter, see what would happen if you took that part out!</div>Jeffreylu017https://www.ocf.berkeley.edu/~shidi/cs61a/wiki/GuidesGuides2014-06-20T23:49:50Z<p>Jeffreylu017: /* Scheme */</p>
<hr />
<div>== Higher-order functions ==<br />
== Environment diagrams ==<br />
=== Environment diagram Rules ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=131 Source: Spring 2014 Piazza (131)]<br />
<br />
Environment Diagrams are very important in our understanding of how the computer interprets our code.<br />
<br />
'''We will test you on this in every exam.'''<br />
<br />
'''It will never go away.'''<br />
<br />
'''Given that, master it as quickly as you can! :)'''<br />
<br />
Below are the rules I follow when drawing environment diagrams. If you understand and faithfully follow these rules when drawing them, you'll never get them wrong.<br />
<br />
One thing you haven't learned yet is nonlocal. You can skip that particular step for now (step 2 of Assignment).<br />
<br />
Post here if you have any questions!<br />
<br />
You can also take a look at this link for some examples of environment diagrams: http://albertwu.org/cs61a/notes/environments<br />
<br />
For a different perspective on the rules, check out: http://markmiyashita.com/cs61a/sp14/environment_diagrams/rules_of_environment_diagrams/<br />
<br />
A handout with detailed instructions on drawing environment diagrams is also available here (linked on the bottom of the course homepage): http://inst.eecs.berkeley.edu/~cs61a/sp14/pdfs/environment-diagrams.pdf<br />
<br />
<pre>Environment Diagram Rules<br />
=========================<br />
<br />
Creating a Function<br />
--------------------<br />
1. Draw the func <name>(<arg1>, <arg2>, ...)<br />
2. The parent of the function is wherever the function was defined<br />
(the frame we're currently in, since we're creating the function).<br />
3. If we used def, make a binding of the name to the value in the current frame.<br />
<br />
Calling User Defined Functions<br />
------------------------------<br />
1. Evaluate the operator and operands.<br />
2. Create a new frame; the parent is whatever the operator s parent is.<br />
Now this is the current frame.<br />
3. Bind the formal parameters to the argument values (the evaluated operands).<br />
4. Evaluate the body of the operator in the context of this new frame.<br />
5. After evaluating the body, go back to the frame that called the function.<br />
<br />
Assignment<br />
----------<br />
1. Evaluate the expression to the right of the assignment operator (=).<br />
2. If nonlocal, find the frame that has the variable you re looking for,<br />
starting in the parent frame and ending just before the global frame (via<br />
Lookup rules). Otherwise, use the current frame. Note: If there are multiple<br />
frames that have the same variable, pick the frame closest to the current<br />
frame.<br />
3. Bind the variable name to the value of the expression in the identified<br />
frame. Be sure you override the variable name if it had a previous binding.<br />
<br />
Lookup<br />
------<br />
1. Start at the current frame. Is the variable in this frame?<br />
If yes, that's the answer.<br />
2. If it isn't, go to the parent frame and repeat 1.<br />
3. If you run out of frames (reach the Global frame and it's not there), complain.<br />
<br />
Tips<br />
----<br />
1. You can only bind names to values.<br />
No expressions (like 3+4) allowed on environment diagrams!<br />
2. Frames and Functions both have parents.</pre><br />
<br />
== Sequences ==<br />
=== Reversing tuples ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=639 Source: Spring 2014 Piazza (639)]<br />
<br />
'''Student Question'''<br />
<br />
Why does [::-1] tuple work while the tuple [0:3:-1] doesn't?<br />
<br />
I thought the -1 after the second semicolon meant that the interpreter is going to read the indexes "backwards". <br />
<br />
'''Student Answer'''<br />
<br />
The syntax of slicing is <tt>tup[start:end:step]</tt>:<br />
<ul><li>start from index <tt>start</tt> and end just before index <tt>end</tt>, incrementing the index by <tt>step</tt> each time<br />
</li><li>if no <tt>step</tt> is provided, <tt>step</tt> = 1<br />
</li><li>if <tt>step</tt> is positive, default values if not provided: <tt>start</tt> = 0, <tt>end</tt> = <tt>len(tup)</tt><br />
</li><li>if <tt>step</tt> is negative, default values if not provided: <tt>start</tt> = -1, <tt>end</tt> = one position before the start of the string<br />
</li></ul><br />
<br />
<pre>&gt;&gt;&gt; (1, 2, 3)[::-1] # start at index -1, end one position before the start of the string<br />
(3, 2, 1)<br />
&gt;&gt;&gt; (1, 2, 3)[0:3:-1] # start at 0 and go to 3, but step is negative, so this doesn't make sense and an empty tuple is returned<br />
()<br />
</pre><br />
<br />
This is a helpful visualization from http://en.wikibooks.org/wiki/Python_Programming/Strings#Indexing_and_Slicing:<br />
<blockquote><br />
To understand slices, it's easiest not to count the elements themselves. It is a bit like counting not on your fingers, but in the spaces between them. The list is indexed like this:<br />
<pre>Element: 1 2 3 4<br />
Index: 0 1 2 3 4<br />
-4 -3 -2 -1<br />
</pre><br />
</blockquote><br />
More info about slicing at http://stackoverflow.com/a/13005464/2460890.<br />
<br />
=== Slicing with negative step ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=702 Source: Spring 2014 Piazza (702)]<br />
<br />
'''Student Question'''<br />
<br />
if the third example returns an empty tuple because you can't take negative steps from 0 to 4, shouldn't the second example also return an empty tuple?<br />
<br />
Can someone explain why each example returns the respective answers?<br />
<br />
Thanks<br />
<br />
<pre>>>> x= (1,2,3,4)<br />
>>> x[0::-1]<br />
(1,)<br />
>>> x[::-1]<br />
(4, 3, 2, 1)<br />
>>> x[0:4:-1]<br />
()<br />
>>> x[1::-1]<br />
(2, 1)</pre><br />
<br />
'''Instructor Answer'''<br />
<br />
(For reference, the notation is <tt>x[start:end:step]</tt>)<br />
<br />
Python does something a very strange when the step is negative: if you omit the arguments to start and end, Python will fill them with what makes sense for a negative step. In the simple case of <tt>x[::-1]</tt>, Python fills in the start with <tt>len(x)-1</tt> and the end with <tt>-(len(x)+1)</tt>. The end term is strange, but remember that the end term isn't included. We therefore can't use 0, but we can't use -1 either, since that clearly refers to the last element of the tuple. We need to fully wrap the negative index around, to refer to the element "before" the 0th index. This way, Python will start at the end of the tuple and proceed to the beginning of the tuple.<br />
<br />
That's why <tt>x[0:4:-1]</tt> doesn't make sense: how can we start at 0 and end at 4, if we're proceeding backwards?<br />
<br />
And that's why <tt>x[0::-1]</tt> makes sense (albeit, in a strange way): Python is proceeding from the 0 index to the beginning of the list. It includes the start index, which is why you see a 1 pop up.<br />
<br />
Let me know if that was confusing!<br />
<br />
== Recursion ==<br />
== Data abstraction ==<br />
== Time complexity ==<br />
=== Andrew Huang's guide to order of growth and function runtime ===<br />
[https://docs.google.com/document/d/1TxfKmM3MlH032hjSUh92I0kQDVcvmitTSzYObGMr8Bk Source: Guide to Order of Growth and Function Runtime (Retrieved June 16th, 2014)]<br />
<br />
'''Introduction'''<br />
<br />
Confused by $O$, $\Omega$, and $\Theta$?<br />
<br />
Want to figure out the runtime of that tricky function?<br />
<br />
Read this.<br />
<br />
'''NOTE THAT THIS GUIDE STARTS WITH BIG O, WHICH IS DIFFERENT FROM THETA. IF YOU UNDERSTAND BIG O, THETA IS EASY (IN FACT, IT DEFINES THETA IN TERMS OF BIG O BELOW).'''<br />
<br />
First some math.<br />
<br />
Formal definition of O(Big O):<br />
<br />
Let $f(n)$ and $g(n)$ be functions from positive integers to positive reals. We say $f \in O(g)$ (“f grows no faster than g”) if there is a constant $0 < c < \inf$ <such that $f(n) \leq c \cdot g(n)$.<br />
<br />
(Paraphrased from Dasgupta, Papadimitriou, & Vazirani)<br />
<br />
(You'll see this again in CS 170)<br />
<br />
What the heck does that mean?<br />
<br />
Let’s look at math functions for a second (just a second).<br />
<br />
Say $f(n)=5n$ and $g(n)=n^{2}$<br />
<br />
What does that look like on a graph?<br />
<br />
http://www.wolframalpha.com/input/?i=plot+5n+and+n%5E2+from+0+to+10<br />
<br />
There’s a section where $n$ dominates $n^{2}$, from 0 to 5, but we don’t really care, because after that point, $n^{2}$ is larger, all the way to infinity! By the definition, we could scale $n^{2}$ by 5 and we would span that initial gap.<br />
<br />
Thus we can say $5n \in O(n^{2})$ or $f \in O(g)$.<br />
<br />
Can we say the converse? That is, is $n^{2} \in O(5n)$?<br />
<br />
Not at all! From the graph we see that $n^{2}$ grows too quickly for $n$ to catch up, no matter what constant we scale $n$ by.<br />
<br />
So what if $f(n)=n+1000$ and $g(n)=n^{2}$?<br />
<br />
It turns out $n+1000 \in O(n^{2})$ still, because according to the definition, as long as we can multiply $n^{2}$ by some $c$, such that the gap of 1000 is spanned, we’re good. In the case, $c=1001$.<br />
<br />
'''What about and $\Omega$ and $\Theta$?'''<br />
<br />
If you digested all of the above, the rest isn’t scary! (Note, $a \equiv b$ means $a$ is equivalent to $b$)<br />
<br />
$f \in \Omega(g) \equiv g \in O(f)$ (You'll see this again briefly in CS 170)<br />
<br />
$f \in \Theta(g) (f \in O(g) and g \in O(f))$<br />
<br />
'''This means that if $f$ is Theta of $g$, then there exist some $c_{1}$ and $c_{2}$ such that'''<br />
<br />
'''$c_{1}g > f$ and'''<br />
<br />
'''$c_{2}g < f$'''<br />
<br />
'''for all positive integers.'''<br />
<br />
'''What does that mean for Python functions?'''<br />
<br />
Given a function $f$, we want to find out how fast that function runs. One way of doing this is to take out a stopwatch, and clock the amount of time it takes for $f$ to run on some input. However, there are tons of problems with that (different computers => different speeds; only one fixed input? Maybe $f$ is really fast for that input but slow for everything else; next year, all the measurements need to be redone on new computers; etc.) Instead, we'll count the steps that a function needs to perform as a function of its input. For example, here are some of the functions that take one step regardless of their input:<br />
<br />
<code>mul</code><br />
<br />
<code>add</code><br />
<br />
<code>sub</code><br />
<br />
<code>print</code><br />
<br />
<code>return</code><br />
<br />
<code>...</code><br />
<br />
So for example, <code>(3 + 3 * 8) % 3</code> would be 3 steps--one for the multiply, one of the add, and one for the mod.<br />
<br />
Let's take a simple example:<br />
<pre>def square(x):<br />
return x * x</pre><br />
<code>square</code> is a function that for any input, always takes two steps, one of the multiplication, and one for returning. Using the notation, we can say square ∈ Θ(1).<br />
<br />
Functions with iteration (for loops, recursion, etc.), usually multiply the steps by some factor. For example, consider factorial:<br />
<pre>def factorial(n):<br />
if n == 0:<br />
return 1<br />
else:<br />
return n * factorial(n-1)</pre><br />
factorial ∈ Θ(n). Why? Well given some input <code>n</code>, we do <code>n</code> recursive calls. At each recursive call, we carry out 4 steps, one for if <code>n == 0</code>, one for subtraction, one for multiply, one for return. Plus, we have the base case, which is another 2 steps, one for if and one for return. So <code>factorial(n)</code> takes $4n+2$ steps => ∈ Θ(n).<br />
<br />
As mentioned, we care about how the running time (how long the function takes to run) of the function changes, '''as we increase the size of the argument'''. So if we imagine a graph, then the x-axis represents the size of our input, and the y-axis represents how long the function took to run for each x. As the size of the input increases, the function’s runtime does something on the graph. So when we say something like “$O(n^{2})$ where $n$ is the length of the list”, we are saying as we double the size of the list, the function is expected to run at most four times as long. '''NOTE ALSO THAT I SAID WHAT $n$ IS! ALWAYS GIVE YOUR UNITS.'''<br />
<br />
This means that when we compare two functions A and B, A may be overall slower than B as we increase the size of their arguments. However, it’s possible at some specific arguments, the A may run faster (like the $f(n)=5n$ and $g(n)=n^{2}$ example above.)<br />
<br />
'''This also means we do not care about the time taken of any particular input! This implies that all those constant-time base cases all those functions don’t really matter, because they don’t scale. That is, only one specific input causes the base case to be reached, and if we increased the size of the argument, $O(1)$ doesn't necessarily hold.'''<br />
<br />
'''Brief “What runs faster than what”'''<br />
<br />
Sorted from fastest to slowest. This is by no means comprehensive.<br />
* $\Theta(1)$<br />
* $\Theta(\log(n))$<br />
* $\Theta(n)$<br />
* $\Theta(n \log(n))$<br />
* $\Theta(n^{2})$<br />
* $\Theta(n^{3})$<br />
* $\Theta(2^{n})$<br />
* (Anything past this point is kind of ridiculous)<br />
* $\Theta(n!)$<br />
* $\Theta(n^{n})$<br />
<br />
'''So we know about the math and the motivation, now how do we actually assign runtimes to real Python functions?'''<br />
<br />
What you must understand, is that there is no one method for finding the runtime. You MUST look at a function holistically or you won’t get the right answer. What does this mean? '''In order to get the correct runtime, you first must understand what the function is doing! You cannot pattern-match your way to becoming good at this.'''<br />
<br />
'''This cannot be stressed enough: UNITS MATTER, if you say O((n)), you must tell us what $n$ is.'''<br />
<br />
General tips<br />
# UNDERSTAND WHAT THE FUNCTION IS DOING!!!<br />
# Try some sample input. That is, pretend you’re the interpreter and execute the code with some small inputs. What is the function doing with the input? Having concrete examples lets you do tip 1 better. You can also graph how the runtime increases as the argument size increases.<br />
# If applicable, draw a picture of the tree of function calls. This shows you the "growth" of the function or how the function is getting "bigger", which will help you do tip 1 better.<br />
# If applicable, draw a picture of how the input is being modified through the function calls. For example, if your input is a list and your function recursively does something to that list, draw out a list, then draw out parts of the list underneath it that are called during the recursion. Helps with tip 1.<br />
# See tip 1.<br />
<br />
Anyways, let's examine some common runtimes (keep scrolling). Remember, '''this is in no way a comprehensive list, NOR IS IT TRYING TO TEACH YOU HOW TO FIND THEM.''' This post is just to give you a starting point into orders of growth by showing you some examples and basic details about each runtime.<br />
<br />
'''Constant $\Theta(1)$'''<br />
<br />
''What it looks like:''<br />
<br />
http://www.wolframalpha.com/input/?i=plot+5<br />
<br />
''Example:''<br />
<br />
<pre>def add(x, y):<br />
return x + y</pre><br />
$add \in \Theta(1)$, where 1 is.. well a constant...<br />
<br />
''Approach:''<br />
<br />
The key behind constant time functions is that regardless of the size of the input, they always run the same number of instructions.<br />
<br />
''Don’t fall for this Trap:''<br />
<br />
<pre>def bar(n):<br />
if n % 7 == 0:<br />
return "Bzzst"<br />
else:<br />
return bar(n -1)</pre><br />
$\mathtt{bar} \in \Theta(1)$. Why?<br />
<br />
'''Logarithmic $\Theta(\log(n))$'''<br />
<br />
''What it looks like:''<br />
<br />
http://www.wolframalpha.com/input/?i=plot+4log3n+from+0+to+10<br />
<br />
''Example:''<br />
<br />
<pre>def binary_search(sorted_L, n):<br />
""" sorted_L is a list of numbers sorted from<br />
smallest to largest<br />
"""<br />
if sorted_L == []:<br />
return False<br />
mid_num = sorted_L[len(sorted_L) // 2]<br />
if n == mid_num:<br />
return True<br />
elif n < mid_num:<br />
return binary_search(sorted_L[:mid_num], n)<br />
else:<br />
return binary_search(sorted_L[mid_num:], n)</pre><br />
$\mathtt{binary\_search} \in \Theta(log(n))$, where $n$ is the number of elements in <code>sorted_L</code>.<br />
<br />
''Approach:''<br />
<br />
Logarithmic functions scale down the size of the problem by some constant every iteration (either with a recursive loop, a for loop, or a while loop). Also, logarithmic functions do not branch out--they generally do not make more than one call to themselves per recursion.<br />
<br />
'''Linear $\Theta(n)$'''<br />
<br />
''What it looks like:''<br />
<br />
http://www.wolframalpha.com/input/?i=plot+8n+from+0+to+10<br />
<br />
''Examples:''<br />
<br />
<pre>def sum_list(L):<br />
sum = 0<br />
for e in L:<br />
sum += e<br />
return sum</pre><br />
$\mathtt{sum\_list} \in \Theta(n)$, where $n$ is the number of elements in $L$.<br />
<br />
</pre>def countdown(n):<br />
if n > 0:<br />
print(n)<br />
countdown(n - 1)<br />
else:<br />
print("Blast off!")</pre><br />
$\mathtt{countdown} \in \Theta(n)$, where $n$ is n.<br />
<br />
''Approach:''<br />
<br />
Linear functions usually act on sequences or other collections of data. In that case, the function will go through the elements once or twice or ''k'' times, where $k<<n$. If the function acts on a number, the number usually gets smaller by a constant each iteration.<br />
<br />
Don't fall for this trap:<br />
<pre>def two_for_loops(n):<br />
for a in range(n):<br />
if n == 4:<br />
for y in range(n):<br />
print("Admiral Ackbar")<br />
else:<br />
print("It's a trap!")</pre><br />
$\mathtt{two\_for\_loops} \in \Theta(n)$, where $n$ is n. Why?<br />
<br />
'''Loglinear/Linearithmic $\Theta(n \log(n))$'''<br />
<br />
''What it looks like:''<br />
<br />
http://www.wolframalpha.com/input/?i=plot+nlog%28n%29+from+0+to+10<br />
<br />
''Example:''<br />
<br />
<pre>def merge(s1, s2):<br />
if len(s1) == 0:<br />
return s2<br />
elif len(s2) == 0:<br />
return s1<br />
elif s1[0] < s2[0]:<br />
return [s1[0]] + merge(s1[1:], s2)<br />
else:<br />
return [s2[0]] + merge(s1, s2[1:])<br />
<br />
def mergesort(lst):<br />
if len(lst) <= 1:<br />
return lst<br />
else:<br />
middle = len(lst) // 2<br />
return merge(mergesort(lst[:middle]), \<br />
mergesort(lst[middle:]))</pre><br />
$\mathtt{mergesort} \in \Theta(n \log(n))$, where $n$ is the number of elements in <code>lst</code>.<br />
<br />
Approach:<br />
These functions tend to make two recursive calls, each making the problem smaller by a half. There's a neat way to see this. For example in mergesort, start with an entire line, which represents mergesort called on the initial list. From there, the list gets split in half by the two recursive calls to mergesort in the code, so draw the another line right below the first, of the same length, but with a small gap in the middle to represent the split. Repeat until you're tired. At the end, you get a rectangle that's nwide and (n)tall!<br />
<pre><nowiki><br />
---------------<br />
------- -------<br />
--- --- --- ---<br />
- - - - - - - -<br />
</nowiki></pre><br />
The total area is the runtime, $\Theta(n \log(n))$<br />
<br />
''Don’t fall for this trap:''<br />
<br />
Don’t confuse functions that have an average running time of n(n)(like quicksort) with functions that are in (n(n))<br />
<br />
'''Polynomial $\Theta(n^{2})$,$\Theta(n^{3})$, etc.'''<br />
<br />
''What it looks like:''<br />
<br />
http://www.wolframalpha.com/input/?i=plot+n%5E2%2B3+from+0+to+10<br />
<br />
''Example:''<br />
<br />
<pre>def print_a_grid(n):<br />
for _ in range(n):<br />
for _ in range(n):<br />
print("+", end="")<br />
print("")</pre><br />
$\mathtt{print\_a\_grid} \in \Theta(n^{2})$, where $n$ is n.<br />
<br />
''Approach:''<br />
<br />
Polynomial functions will examine each element of an input many, many times, as opposed to linear functions, which examine some constant number of times.<br />
<br />
''Don’t fall into this trap:''<br />
<br />
Don’t get polynomial confused with exponential (below).<br />
<br />
'''Exponential'''<br />
<br />
''What it looks like:''<br />
<br />
http://www.wolframalpha.com/input/?i=plot+2%5En+from+0+to+10<br />
<br />
''Example:''<br />
<br />
<pre>(define (strange-add x)<br />
(if (zero? x)<br />
1<br />
(+ (strange-add (- x 1))<br />
(strange-add (- x 1)) )))<br />
<br />
def strange_add(x):<br />
if x == 0:<br />
return 1<br />
else:<br />
return strange_add(x - 1) + strange_add(x - 1)</pre><br />
$\mathtt{strange\_add} \in \Theta(2^{n})$, where $n$ is x.<br />
<br />
''Approach:''<br />
<br />
Exponential functions tend to branch out as you get deeper and deeper into their call tree, and each call only makes the work smaller by a little bit. For example, <code>(strange-add 8)</code> calls <code>(strange-add 7)</code> and <code>(strange-add 7)</code>. Those two calls each make two calls, <code>(strange-add 6)</code>, <code>(strange-add 6)</code>, <code>(strange-add 6)</code>, and <code>(strange-add 6)</code> respectively, and so on.<br />
<br />
== Mutability ==<br />
=== Michelle Chang's guide to immutability and mutability ===<br />
[https://d1b10bmlvqabco.cloudfront.net/attach/hoxc5uu6sud761/gozdkhgdUbT/htdlpko411i0/Python__Immutable_vs_Mutable.pdf Source: What You Should Know about Immutability vs Mutability]<br />
<br />
== Mutable data-structures ==<br />
== Object-oriented programming ==<br />
=== Inheritance and class vs instance attributes ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=1413 Source: Spring 2014 Piazza (1413)]<br />
<br />
'''Student Question'''<br />
<br />
I'm confused on how Classes and Inheritance work.<br />
<br />
If there's a Parent class and a Child class, when coding in the Child class, when do you write <code>Parent.attribute</code>, when do you write <code>Child.attribute</code>, and when do you write <code>self.attribute</code>?<br />
<br />
Also, I'm also confused as to when to put <code>self</code> into the parentheses as well.<br />
<br />
'''Instructor Answer'''<br />
<br />
<code>Parent.attribute</code> and <code>Child.attribute</code> would both be ways of accessing a'''class variable'''. These are variables that can be accessed without creating new '''instances''' of the that class.<br />
<br />
<code>self.attribute</code> would be used in '''methods''' to access an '''instance variable''' (an attribute specific to an instance).<br />
<br />
So for example, <code>Insect.watersafe</code> is <code>False</code>, but <code>Bee.watersafe</code> is <code>True</code>. These are class attributes because you don't have to create an Insect object or a Bee object in order to say <code>Insect.watersafe</code> or <code>Bee.watersafe</code>.<br />
<br />
However it wouldn't make any sense to say <code>Bee.armor</code>, since armor is an '''instance variable'''. You have to first create a new Bee before you could ask it for it's armor. If you created a second Bee after that, the second Bee would also have its own armor.<br />
<br />
There's a lot of vocab (in bold) that might trip you up. Try reading Discussion 6 and posting a followup if you're still unsure!<br />
<br />
== Iterables, iterators and generators ==<br />
== Scheme ==<br />
=== Scheme lists ===<br />
----<br />
==== <code>append</code> vs <code>cons</code> vs <code>list</code> ====<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=2067 Source: Spring 2014 Piazza (2067)]<br />
<br />
This post isn't meant to be comprehensive. '''Ask questions in lab or as a followup here if you're confused.'''<br />
One of its major flaws is that it doesn't cover box and pointers. '''LEARN BOX AND POINTERS. '''<br />
<br />
Here is a beautiful web based Scheme interpreter that will draw box and pointer diagrams for you. Run through the examples below with this thing:<br />
http://xuanji.appspot.com/js-scheme-stk/index.html<br />
<br />
In order to understand these three procedures, you first have to understand a little about Pairs and Lists.<br />
<br />
'''Pairs''' are data structures that have two slots. You can put different stuff in these slots, like numbers or words or sentences or booleans--pretty much anything. You make a pair using cons.<br />
<pre>STk> (cons 'foo 'bar)<br />
(foo . bar)<br />
<br />
STk> (cons 1 'ring)<br />
(1 . ring)<br />
<br />
STk> (cons (+ 1 2 3) (member? 3 '(the 3 stooges)))<br />
(6 . #t)</pre><br />
<br />
In order to get stuff from a pair that you have made, you use <code>car</code> and <code>cdr</code>. <code>car</code> gets the thing in the first slot. <code>cdr</code> gets the thing in the second slot.<br />
<br />
<pre>STk> (define foo (cons 'x 'y))<br />
foo<br />
<br />
STk> foo<br />
(x . y)<br />
<br />
STk> (car foo)<br />
x<br />
<br />
STk> (cdr foo)<br />
y</pre><br />
That was straightforward. Now for the trippy part:<br />
'''You can put pairs inside of pairs:'''<br />
<pre>STk> (define foo (cons (cons 3 4) 5))<br />
foo<br />
<br />
STk> foo<br />
((3 . 4) . 5)<br />
<br />
STk> (car foo)<br />
(3 . 4)<br />
<br />
STk> (car (car foo))<br />
3<br />
<br />
STk> (caar foo) ; functionally equivalent as above.<br />
3<br />
<br />
STk> (cdr foo)<br />
5<br />
<br />
STk> (cdr (car foo))<br />
4<br />
<br />
STk> (cdar foo) ; functionally equivalent as above.<br />
4</pre> <br />
<br />
There's a certain style of pair nesting that is especially useful&mdash;'''Lists.'''<br />
<br />
Each list has these properties:<br />
* Every list is a pair or the empty list (denoted by '() or nil).<br />
* The car of a nonempty list is some item.<br />
* The cdr of a nonempty list must be another list.<br />
<pre>STk> (cons 1 (cons 2 (cons 3 '()))) ; list of numbers<br />
(1 2 3)<br />
<br />
STk> (define stooges (cons 'larry (cons 'curly (cons 'moe nil))))<br />
stooges<br />
<br />
STk> stooges<br />
(larry curly moe)<br />
<br />
STk> (car stooges)<br />
larry<br />
<br />
STk> (cdr stooges) ; Calling cdr on a non-empty list gives you another list!<br />
(curly moe)<br />
<br />
STk> (cadr stooges)<br />
curly<br />
<br />
STk> (cdar stooges) ; Why does this break?<br />
*** Error:<br />
cdar: bad list: (moe larry curly)<br />
Current eval stack:<br />
__________________<br />
0 (cdar stooges)<br />
<br />
STk> (define not-a-list (cons 'foo (cons 'bar 'baz))) ; This is not a list.<br />
not-a-list<br />
<br />
STk> not-a-list ; What property does this break?</pre><br />
Notice how Scheme knew that we were making lists. Before we had <code>parens</code> and periods which organized our items. Scheme now recognizes that we're making a list and does away with the periods and some of the <code>parens</code>.<br />
<br />
If you stare a bit at the list rules above, you can notice we used a recursive definition to define lists. Recursion... '''on data'''!<br />
<br />
Let's talk about <code>list</code>. <code>list</code> takes a bunch of stuff and makes a list out of them. The stuff can be anything. Words, numbers, pairs, other lists. <code>list</code> doesn't care. [picture of a honey badger]<br />
<pre>STk> (list 'foo 'bar' 'baz) ; Lists takes anything and makes a list out of it.<br />
(foo bar baz)<br />
<br />
STk> (list 'foo ((lambda (x) (+ x 4)) 8) #f (cons 1 (cons 3 4)) (cons 1 (cons 2 nil)) (list 1 2 3)) ; ANYTHING <br />
(foo 12 #f (1 3 . 4) (1 2) (1 2 3))<br />
<br />
STk> (list 'x 'y 'z)<br />
(x y z)<br />
<br />
STk> '(x y z) ; Sometimes you can get away with using quote to make literal lists. Yes, sentences are secretly lists.<br />
(x y z)</pre><br />
Now we can talk about <code>append</code>:<br />
<pre>STk> (append '(a b c) '(d e f) '(g h i)) ; Append takes in lists and appends them together.<br />
(a b c d e f g h i)<br />
<br />
STk> (append 'foo '(1 2 3)) ; foo is not a list. Stuff will break.<br />
*** Error:<br />
append: argument is not a list: foo<br />
Current eval stack:<br />
__________________<br />
0 (append (quote foo) (quote (1 2 3)))</pre><br />
You know that <code>cons</code> makes a pair. You also know that you can make a list out of pairs. You can abuse <code>cons</code> for your own maniacal purposes.<br />
<pre>STk> (cons 'joe stooges) ; Put stuff at the beginning of a list!<br />
(joe larry curly moe)</pre><br />
'''The following only applies to the STk interpreter.'''<br />
<pre>STk> (append '(1 2 3) 'foo) ; Wait... what?<br />
(1 2 3 . foo)<br />
<br />
STk> (append '(1 2 3) (cons 4 5)) ; The plot thickens!<br />
(1 2 3 4 . 5)<br />
<br />
STk> (append stooges 'shemp) ; You should really figure out why this works.<br />
(larry curly moe . shemp)</pre><br />
<br />
To summarize:<br />
* append takes in lists and outputs a big list.<br />
* cons takes in things and makes a pair out of them. However, we know that lists are made of pairs, so we can throw together a list if we use cons a certain way<br />
* list takes in things and makes a list out of those things, regardless of what they are.<br />
<br />
=== Tail recursion ===<br />
----<br />
==== Mark Miyashita's guide on tail recursion ====<br />
[http://markmiyashita.com/cs61a/sp14/tail_recursion/tail_recursion_and_tail_optimized_calls/ Source: Tail Recursion and Tail Optimized Calls]<br />
<br />
First off, I think this is an excellent article to read about tail recursion and tail calls in Python: [http://paulbutler.org/archives/tail-recursion-in-python/ here]<br />
<br />
Basically, you can write tail recursive functions in any language. Tail recursion, in one sentence, is where you return the answer in the final frame instead of following the frames back up to the original frame. For example, we have factorial which is normally not tail recursive:<br />
<br />
<pre>def factorial(n):<br />
if n == 1:<br />
return 1<br />
return n * factorial(n - 1)</pre><br />
<br />
because it needs to keep track of the <code>n *</code> at each level of recursion.<br />
<br />
The following implementation of <code>factorial</code>, is tail recursive because at the end of the last frame, it can return the answer, instead of going back up through all the frames to multiply and compute the answer:<br />
<br />
<pre>def factorial(n):<br />
def helper(n, total):<br />
if n == 1:<br />
return total<br />
return helper(n - 1, total * n)<br />
return helper(n, 1)</pre><br />
<br />
You can’t have tail optimized calls in Python – at least, not like the code that we defined above. You can define your own sort of tail optimized way of evaluating the functions by using lambdas, and I believe the article linked at the top of this post goes into detail about how to implement this if you are interested. In Scheme, the language detects when you have something like the helper function in the example above where your return statement consists of only the recursive call. In the first example, we have the <code>n *</code> and the recursive call which means it cannot be tail optimized because it needs to keep track of all of the frames that it creates. In a tail optimized call, Scheme will get rid of the frames that are no longer necessary.<br />
<br />
'''tl;dr''' – Tail recursion can be done in any language where the basic idea is that you return the answer in the final frame of recursion. Tail optimized calls are a Scheme (and some other languages, not including Python) feature where it will get rid of the frames above, if certain conditions are met – such as where the return statement is only the recursive call and nothing else. The cases in which Scheme uses a tail optimized call are located on the lecture slides located [http://www-inst.eecs.berkeley.edu/~cs61a/sp13/slides/35-TailCalls_6pp.pdf here].<br />
<br />
==== Tail recursion in Python ====<br />
[http://kylem.net/programming/tailcall.html Source: http://kylem.net/programming/tailcall.html (Retrieved June 16th, 2014)]<br />
<br />
In this page, we’re going to look at tail call recursion and see how to force Python to let us eliminate tail calls by using a trampoline. We will go through two iterations of the design: first to get it to work, and second to try to make the syntax seem reasonable. I would not consider this a useful technique in itself, but I do think it’s a good example which shows off some of the power of decorators.<br />
<br />
The first thing we should be clear about is the definition of a tail call. The “call” part means that we are considering function calls, and the “tail” part means that, of those, we are considering calls which are the last thing a function does before it returns. In the following example, the recursive call to f is a tail call (the use of the variable <code>ret</code> is immaterial because it just connects the result of the call to <code>f</code> to the return statement), and the call to <code>g</code> is not a tail call because the operation of adding one is done after <code>g</code> returns (so it’s not in “tail position”).<br />
<br />
<pre>def f(n) :<br />
if n > 0 :<br />
n -= 1<br />
ret = f(n)<br />
return ret<br />
else :<br />
ret = g(n)<br />
return ret + 1</pre><br />
<br />
'''1. Why tail calls matter'''<br />
<br />
Recursive tail calls can be replaced by jumps. This is called “tail call eliminination,” and is a transformation that can help limit the maximum stack depth used by a recursive function, with the benefit of reducing memory traffic by not having to allocate stack frames. Sometimes, recursive function which wouldn’t ordinarily be able to run due to stack overflow are transformed into function which can.<br />
<br />
Because of the benefits, some compilers (like <code>gcc</code>) perform tail call elimination[1], replacing recursive tail calls with jumps (and, depending on the language and circumstances, tail calls to other functions can sometimes be replaced with stack massaging and a jump). In the following example, we will eliminate the tail calls in a piece of code which does a binary search. It has two recursive tail calls.<br />
<br />
<pre>def binary_search(x, lst, low=None, high=None) :<br />
if low == None : low = 0<br />
if high == None : high = len(lst)-1<br />
mid = low + (high - low) // 2<br />
if low > high :<br />
return None<br />
elif lst[mid] == x :<br />
return mid<br />
elif lst[mid] > x :<br />
return binary_search(x, lst, low, mid-1)<br />
else :<br />
return binary_search(x, lst, mid+1, high)</pre><br />
<br />
Supposing Python had a <code>goto</code> statement, we could replace the tail calls with a jump to the beginning of the function, modifying the arguments at the call sites appropriately:<br />
<br />
<pre>def binary_search(x, lst, low=None, high=None) :<br />
start:<br />
if low == None : low = 0<br />
if high == None : high = len(lst)-1<br />
mid = low + (high - low) // 2<br />
if low > high :<br />
return None<br />
elif lst[mid] == x :<br />
return mid<br />
elif lst[mid] > x :<br />
(x, lst, low, high) = (x, lst, low, mid-1)<br />
goto start<br />
else :<br />
(x, lst, low, high) = (x, lst, mid+1, high)<br />
goto start</pre><br />
<br />
which, one can observe, can be written in actual Python as<br />
<br />
<pre>def binary_search(x, lst, low=None, high=None) :<br />
if low == None : low = 0<br />
if high == None : high = len(lst)-1<br />
while True :<br />
mid = low + (high - low) // 2<br />
if low > high :<br />
return None<br />
elif lst[mid] == x :<br />
return mid<br />
elif lst[mid] > x :<br />
high = mid - 1<br />
else :<br />
low = mid + 1</pre><br />
<br />
I haven’t tested the speed difference between this iterative version and the original recursive version, but I would expect it to be quite a bit faster because of there being much, much less memory traffic.<br />
<br />
Unfortunately, the transformation makes it harder to prove the binary search is correct in the resulting code. With the original recursive algorithm, it is almost trivial by induction.<br />
<br />
Programming languages like Scheme depend on tail calls being eliminated for control flow, and it’s also necessary for continuation passing style.[2]<br />
<br />
'''2. A first attempt'''<br />
<br />
Our running example is going to be the factorial function (a classic), written with an accumulator argument so that its recursive call is a tail call:<br />
<br />
<pre>def fact(n, r=1) :<br />
if n <= 1 :<br />
return r<br />
else :<br />
return fact(n-1, n*r)</pre><br />
<br />
If <code>n</code> is too large, then this recursive function will overflow the stack, despite the fact that Python can deal with really big integers. On my machine, it can compute <code>fact(999)</code>, but <code>fact(1000)</code> results in a sad <code>RuntimeError: Maximum recursion depth exceeded</code>.<br />
<br />
One solution is to modify fact to return objects which represent tail calls and then to build a trampoline underneath fact which executes these tail calls after fact returns. This way, the stack depth will only contain two stack frames: one for the trampoline and another for each call to fact.<br />
<br />
First, we define a tail call object which reifies the concept of a tail call:<br />
<br />
<pre>class TailCall(object) :<br />
def __init__(self, call, *args, **kwargs) :<br />
self.call = call<br />
self.args = args<br />
self.kwargs = kwargs<br />
def handle(self) :<br />
return self.call(*self.args, **self.kwargs)</pre><br />
<br />
This is basically just the thunk <code>lambda : call(*args, **kwargs)</code>, but we don’t use a thunk because we would like to be able to differentiate between a tail call and returning a function as a value.<br />
<br />
The next ingredient is a function which wraps a trampoline around an arbitrary function:<br />
<br />
<pre>def t(f) :<br />
def _f(*args, **kwargs) :<br />
ret = f(*args, **kwargs)<br />
while type(ret) is TailCall :<br />
ret = ret.handle()<br />
return ret<br />
return _f</pre><br />
<br />
Then, we modify fact to be<br />
<br />
<pre>def fact(n, r=1) :<br />
if n <= 1 :<br />
return r<br />
else :<br />
return TailCall(fact, n-1, n*r)</pre><br />
<br />
Now, instead of calling <code>fact(n)</code>, we must instead invoke <code>t(fact)(n)</code> (otherwise we’d just get a TailCall object).<br />
<br />
This isn’t that bad: we can get tail calls of arbitrary depth, and it’s Pythonic in the sense that the user must explicitly label the tail calls, limiting the amount of unexpected magic. But, can we eliminate the need to wrap t around the initial call? I myself find it unclean to have to write that <code>t</code> because it makes calling fact different from calling a normal function (which is how it was before the transformation).<br />
<br />
'''3. A second attempt'''<br />
<br />
The basic idea is that we will redefine fact to roughly be <code>t(fact)</code>. It’s tempting to just use <code>t</code> as a decorator:<br />
<br />
<pre>@t<br />
def fact(n, r=1) :<br />
if n <= 1 :<br />
return r<br />
else :<br />
return TailCall(fact, n-1, n*r)</pre><br />
<br />
(which, if you aren’t familiar with decorator syntax, is equivalent to writing <code>fact = t(fact)</code> right after the function definition). However, there is a problem with this in that the fact in the returned tail call is bound to <code>t(fact)</code>, so the trampoline will recursively call the trampoline, completely defeating the purpose of our work. In fact, the situation is now worse than before: on my machine, <code>fact(333)</code> causes a <code>RuntimeError</code>!<br />
<br />
For this solution, the first ingredient is the following class, which defines the trampoline as before, but wraps it in a new type so we can distinguish a trampolined function from a plain old function:<br />
<br />
<pre>class TailCaller(object) :<br />
def __init__(self, f) :<br />
self.f = f<br />
def __call__(self, *args, **kwargs) :<br />
ret = self.f(*args, **kwargs)<br />
while type(ret) is TailCall :<br />
ret = ret.handle()<br />
return ret</pre><br />
<br />
and then we modify <code>TailCall</code> to be aware of <code>TailCallers</code>:<br />
<br />
<pre>class TailCall(object) :<br />
def __init__(self, call, *args, **kwargs) :<br />
self.call = call<br />
self.args = args<br />
self.kwargs = kwargs<br />
def handle(self) :<br />
if type(self.call) is TailCaller :<br />
return self.call.f(*self.args, **self.kwargs)<br />
else :<br />
return self.call(*self.args, **self.kwargs)</pre><br />
<br />
Since classes are function-like and return their constructed object, we can just decorate our factorial function with <code>TailCaller</code>:<br />
<br />
<pre>@TailCaller<br />
def fact(n, r=1) :<br />
if n <= 1 :<br />
return r<br />
else :<br />
return TailCall(fact, n-1, n*r)</pre><br />
<br />
And then we can call fact directly with large numbers!<br />
<br />
Also, unlike in the first attempt, we can now have mutually recursive functions which all perform tail calls. The first-called <code>TailCall</code> object will handle all the trampolining.<br />
<br />
If we wanted, we could also define the following function to make the argument lists for tail calls be more consistent with those for normal function calls:[3]<br />
<br />
<pre>def tailcall(f) :<br />
def _f(*args, **kwargs) :<br />
return TailCall(f, *args, **kwargs)<br />
return _f</pre><br />
<br />
and then fact could be rewritten as<br />
<br />
<pre>@TailCaller<br />
def fact(n, r=1) :<br />
if n <= 1 :<br />
return r<br />
else :<br />
return tailcall(fact)(n-1, n*r)</pre><br />
<br />
One would hope that marking the tail calls manually could just be done away with, but I can’t think of any way to detect whether a call is a tail call without inspecting the source code. Perhaps an idea for further work is to convince Guido von Rossum that Python should support tail recursion (which is quite unlikely to happen).<br />
<br />
[1] This is compiler-writer speak. For some reason, “elimination” is what you do when you replace a computation with something equivalent. In this case, it’s true that the call is being eliminated, but in its place there’s a jump. The same is true for “common subexpression elimination” (known as CSE), which takes, for instance,<br />
<br />
<pre>a = b + c<br />
d = (b + c) + e<br />
and replaces it with<br />
a = b + c<br />
d = a + e</pre><br />
<br />
Sure, the <code>b+c</code> is eliminated from the second statement, but it’s not really gone...<br />
The optimization known as “dead code elimination” actually eliminates something, but that’s because dead code has no effect, and so it can be removed (that is, be replaced with nothing).<br />
<br />
[2] In Scheme, all loops are written as recursive functions since tail calls are the pure way of redefining variables (this is the same technique Haskell uses). For instance, to print the numbers from 1 to 100, you’d write<br />
<br />
<pre>(let next ((n 1))<br />
(if (<= n 100)<br />
(begin<br />
(display n)<br />
(newline)<br />
(next (+ n 1)))))</pre><br />
<br />
where next is bound to be a one-argument function which takes one argument, <code>n</code>, and which has the body of the <code>let</code> statement as its body. If that <code>100</code> were some arbitrarily large number, the tail call to next had better be handled as a jump, otherwise the stack would overflow! And there’s no other reasonable way to write such a loop!<br />
<br />
Continuation passing style is commonly used to handle exceptions and backtracking. You write functions of the form<br />
<br />
<pre>(define (f cont)<br />
(let ((cont2 (lambda ... (cont ...) ...)))<br />
(g cont2)))</pre><br />
<br />
along with functions which take multiple such f’s and combines them into another function which also takes a single cont argument. I’ll probably talk about this more in another page, but for now notice how the call to g is in the tail position.<br />
<br />
[3] This is basically a curried[4] version of <code>TailCall</code>.<br />
<br />
[4] That is, ''Schönfinkelized''.<br />
<br />
=== Miscellaneous ===<br />
----<br />
==== Useful Scheme Procedures ====<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=2226 Source: Spring 2014 Piazza (2226)]<br />
<br />
Here is a short list of Scheme procedures that you might use in writing your programs:<br />
<pre>; define - defines a variable or a procedure<br />
(define my-variable 4)<br />
(define (square x)<br />
(* x x))<br />
<br />
; if - conditional branching akin to if ... else<br />
(define (fib n)<br />
(if (< n 2)<br />
n<br />
(+ (fib (- n 1)) (fib (- n 2))) ))<br />
<br />
; conditional branching akin to if ... elif ... elif ... else<br />
(define (deep-map f lst)<br />
(cond ((null? lst) lst)<br />
((list? (car lst))<br />
(cons (deep-map f (car lst))<br />
(deep-map f (cdr lst))))<br />
(else<br />
(cons (f (car lst))<br />
(deep-map f (cdr lst)))) ))<br />
<br />
; and - outputs the the rightmost value if all of the arguments evaluate to #t. Outputs #f otherwise.<br />
STk> (and 0 1 2 3)<br />
3<br />
<br />
; or - outputs the the first value that evaluates to #t. Outputs #f otherwise.<br />
STk> (or 0 1 2 3)<br />
0<br />
<br />
; equal - tests if symbols are the same<br />
STk> (equal? 'foo 'bar)<br />
#f<br />
STk> (equal? 'foo 'foo)<br />
#t<br />
<br />
STk> (list? 'foo)<br />
#f<br />
STk> (list? '(1 2 3))<br />
#t<br />
STk> (list? '())<br />
#t<br />
<br />
STk> (null? '(1 2 3))<br />
#f<br />
STk> (null? ())<br />
#t<br />
<br />
; member? - tests if a symbol is in a list EDIT: NOT BUILT IN (BUT SUPER USEFUL SEE FOLLOWUP)<br />
STk> (member? 'quick '(the quick brown fox jumped over the lazy dog))<br />
#t<br />
<br />
; number? - checks if input is a number<br />
STk> (number? 42)<br />
#t<br />
STk> (number? #t)<br />
#f<br />
<br />
; remainder - computes the remainder of the first number divided by the second<br />
STk> (remainder 100 21)<br />
16</pre><br />
<br />
== Streams ==<br />
== Logic ==<br />
=== Quick Guide to Logic Programming ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=2524 Source: Spring 2014 Piazza (2524)]<br />
''Note: Someone should convert this from scmlog to Logic notation''<br />
<br />
'''Here's something I wrote a long time ago. The logic interpreter scmlog still exists and you should be able to access it using your cs61a-xx account. Post a followup if you have any questions. Hope this helps!'''<br />
<br />
<br />
'''Introduction:''' logic programming is a completely different way to think about telling computers to do stuff. Instead of telling the computer what to compute, you give the computer facts and ask it questions. The computer does its own thinking given the facts and the question, and then returns an answer.<br />
<br />
'''Table of Contents'''<br />
- Introduction<br />
- What is Logic Programming<br />
- - Giving Facts and Asking Questions<br />
- - More Complicated Facts and Questions<br />
- How to write Logic Programs<br />
- - Common Pitfalls<br />
- More Resources<br />
<br />
<br />
'''What is Logic Programming?'''<br />
Logic Programming is a way to ask the computer questions and get answers without telling it explicitly how to reach the conclusion. Kind of like this: http://youtu.be/tpKx7Oi0oeM<br />
<br />
Anyways, we have a logic programming interpreter called Scmlog. It interprets a simpler version of the logic programming language, Prolog. The first few things to understand about it is that '''SCMLOG IS NOT SCHEME'''. It just happens to look like it. Scmlog is its own language and so it has its own rules. You can't program in Scmlog like you would in Scheme or Python, so it might be good to forget what you know about those languages for a second.<br />
<br />
When you fire up scmlog (type scmlog into a terminal on the school computers), you get this prompt:<br />
<pre>star [501] ~ # scmlog<br />
scmlog (Prolog in Scheme), v. 0.2<br />
Type 'help' for help; 'quit' to exit.<br />
?- </pre><br />
You can interact with this prompt in two ways:<br />
# Give facts - "Let's tell the computer some things it should know!"<br />
# Ask questions - "Let's ask the computer questions about the things we told it about!"<br />
<br />
'''Giving Facts and Asking Questions'''<br />
A basic fact takes this form: <code>(fact (assertion))</code>, '''Where each assertion is simply a relation between things.'''<br />
<br />
For example: <code>(fact (likes potstickers brian))</code> relates three ideas, liking something, potstickers, and some guy name Brian. "Brian likes potstickers."<br />
Note that we put the relation first, and then the parties the relation acts upon. In this example, "likes" is the relation, it hooks up "brian" and "potstickers". Here's some more:<br />
<pre>?- (fact (likes potstickers brian)) <br />
?- (fact (likes potstickers andrew)) <br />
?- (fact (likes the_beatles brian)) <br />
?- (fact (likes the_beatles andrew)) <br />
?- (fact (likes led_zeppelin andrew)) <br />
?- (fact (dislikes led_zeppelin brian))</pre><br />
Now that we've given the computer a bunch of facts, how do we ask questions about them? Just replace "fact" with a "?", and replace any part of the relation (besides the relation itself) with a variable prefixed by an underscore. This is called "querying":<br />
<pre>?- (? (likes _what brian))<br />
_what : potstickers<br />
More? <br />
_what : the_beatles<br />
More? <br />
?- (? (likes potstickers _who))<br />
_who : brian<br />
More? <br />
_who : andrew<br />
More? <br />
?- (? (dislikes _what brian))<br />
_what : led_zeppelin<br />
More?<br />
?- (? (likes led_zeppelin andrew))<br />
Yes.<br />
?- (? (dislikes the_beatles _who))<br />
No.</pre><br />
Notice:<br />
* We can query any part of the assertion, besides the relation itself (can't replace "likes" with a variable).<br />
* All possible answers to the question show up.<br />
* If Scmlog couldn't find a fact that matched your query, it'll say "No."<br />
* Asking a question without any variables essentially asks if that fact exists. To which Scmlog will answer "Yes." or "No."<br />
<br />
'''More Complicated Facts and Questions'''<br />
Now this isn't the whole picture. We also have the ability to make more powerful assertions via variables, hypotheses, and conclusions:<br />
<br />
<code>(fact (ancestor _x _y) (parent _x _y))</code> "X is an ancestor of Y if X is a parent of Y"<br />
<br />
Here _x and _y are variables like usual. However, we have two parts to this fact, the conclusion ("X is an ancestor of Y") and the hypothesis ("X is a parent of Y"). We can have more than one hypotheses, and they can be any kind of query. Here's ancestor in action:<br />
<pre>?- (fact (parent george paul))<br />
?- (fact (parent martin george))<br />
?- (fact (parent martin martin_jr))<br />
?- (fact (parent martin donald))<br />
?- (fact (parent george ann))<br />
?- (fact (ancestor _X _Y) (parent _X _Y))<br />
?- (fact (ancestor _X _Y) (parent _X _Z) (ancestor _Z _Y))<br />
?- (? (ancestor paul george))<br />
No.<br />
?- (? (ancestor george paul))<br />
Yes.<br />
?- (? (ancestor george george))<br />
No.<br />
?- (? (ancestor martin paul))<br />
Yes.</pre><br />
Now there's one more thing you need to know about Scmlog. Scmlog knows about pairs and lists:<br />
<pre>?- (fact (lst (1 2 3)))<br />
?- (? (lst _x))<br />
_x : (1 2 3)<br />
More?<br />
?- (? (lst (1 . _x)))<br />
_x : (2 3)<br />
More? <br />
?- (? (lst (1 2 . _x)))<br />
_x : (3)<br />
More? <br />
?- (? (lst (_x . _y)))<br />
_x : 1<br />
_y : (2 3)<br />
?- (fact (my_pair (2 . 3)))<br />
?- (? (my_pair _y))<br />
_y : (2 . 3)<br />
?- (? (my_pair (2 . _x)))<br />
_x : 3</pre><br />
<br />
'''How to write Logic Programs'''<br />
<br />
The trick behind writing logic programs is to forget everything you know about programming. You instead want to focus on the relation you're trying to establish. Take append for example:<br />
<pre>?- (fact (append () _b _b))<br />
?- (fact (append (_x . _rest) _b (_x . _z)) (append _rest _b _z))<br />
?- (? (append (1 2 3) (3 2 1) _answer))<br />
_answer : (1 2 3 3 2 1)<br />
?- (append (1 2) 3 (1 2 3))<br />
Huh?<br />
?- (? (append (1 2) 3 (1 2 3))<br />
)<br />
No.<br />
?- (? (append (1 2) (3) (1 2 3))<br />
)<br />
Yes.<br />
?- (? (append (1 2) (3) (1 2 3)))<br />
Yes.<br />
?- (? (append _x (3 4 9) (1 0 3 2 3 4 9)))<br />
_x : (1 0 3 2)<br />
More? <br />
?- (? (append _x _y (2 1)))<br />
_x : ()<br />
_y : (2 1)<br />
More? <br />
_x : (2)<br />
_y : (1)<br />
More? <br />
_x : (2 1)<br />
_y : ()</pre><br />
<br />
What is append? Well the first fact establishes that if we append an empty list to something, the result is that something.<br />
The next fact is the meat of the code. You can almost think of this as a recursive relation. Any append relation between some three lists a b c must also fulfill another append relation between the rest of a, b, and the rest of c, respectively. Think about why that will always be true for all inputs. If the example above doesn't make sense to you, see the following:<br />
<br />
<br />
'''Common Pitfalls'''<br />
<br />
'''- One of the biggest issues people have with writing logic programs is that they don't realize that the variables don't work like they do in Scheme or Python. All Scmlog ever does is pattern match:'''<br />
<pre>?- (fact (x (0 1 8)))<br />
?- (fact (x (1 8 4)))<br />
?- (fact (x (9 4 4)))<br />
?- (fact (x (3 0 8)))<br />
?- (? (x _y))<br />
_y : (0 1 8)<br />
More? <br />
_y : (1 8 4)<br />
More? <br />
_y : (9 4 4)<br />
More? <br />
_y : (3 0 8)<br />
?- (? (x (0 1 . _z)))<br />
_z : (8)</pre><br />
<br />
- SCMLOG IS NOT SCHEME. SCMLOG IS NOT PYTHON.<br />
<br />
More Resources:<br />
* http://www-inst.eecs.berkeley.edu/~cs61a/sp12/lectures/prolog/ReadMe<br />
* http://www-inst.eecs.berkeley.edu/~cs61a/sp12/lectures/prolog/<br />
* http://www-inst.eecs.berkeley.edu/~cs61a/sp12/discussion/week14/<br />
* http://www-inst.eecs.berkeley.edu/~cs61a/fa13/slides/31-Logic_6pp.pdf<br />
<br />
=== Logic Mathematics ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=3050 Source: Spring 2014 Piazza (3050)]<br />
<br />
'''Student Question'''<br />
<br />
Can someone explain the intuition behind the implementation of the increments and the addition facts? The following is from Mark's website<br />
<br />
<pre>(fact (increment 0 1))<br />
(fact (increment 1 2))<br />
(fact (increment 2 3))<br />
(fact (increment 3 4))<br />
(fact (increment 4 5))<br />
(fact (increment 5 6))<br />
(fact (increment 6 7))<br />
(fact (increment 7 8))<br />
(fact (increment 8 9))<br />
(fact (increment 9 10))<br />
(fact (increment 10 11))<br />
(fact (increment 11 12))<br />
(fact (increment 12 13))<br />
<br />
(fact (add 1 ?x ?x+1)<br />
(increment ?x ?x+1))<br />
<br />
(fact (add ?x+1 ?y ?z+1)<br />
(increment ?x ?x+1)<br />
(increment ?z ?z+1)<br />
(add ?x ?y ?z))<br />
<br />
(query (add 2 4 6))<br />
; expect Success!</pre><br />
<br />
'''Student Answer'''<br />
<br />
First, we state a bunch of facts that denote relations between a number and the number that follows it (e.g. 1 and 2, 2 and 3, and so on). This is the increment fact.<br />
<br />
Then, we state a "base" fact, which is:<br />
<pre>(fact (add 1 ?x ?x+1)<br />
(increment ?x ?x+1))</pre><br />
In English, this fact states that some value <code>?x</code> added to 1 will give us some value <code>?x+1</code> (<code>x+1</code> is a valid variable name in Logic) '''if and only if''' the fact (<code>increment ?x ?x+1</code>) is true. As an example, <code>(add 1 2 3)</code> is true '''if and only if''' (<code>increment 2 3</code>) is true. This is true because of the increment facts we stated before.<br />
<br />
Now, to deal with additions that aren't just the sum of two numbers in which one number is a 1, we need:<br />
<pre>(fact (add ?x+1 ?y ?z+1)<br />
(increment ?x ?x+1)<br />
(increment ?z ?z+1)<br />
(add ?x ?y ?z))</pre><br />
I will try to give an intuition as to what is happening here.<br />
<br />
We state our fact: that two numbers, <code>?x+1</code> and <code>?y</code>, will add up to some number <code>?z+1</code> if and only if the following 3 hypotheses are true:<br />
* (<code>increment ?x ?x+1</code>) - "There exists some number <code>?x</code> that is 1 less than <code>?x+1</code>, and"<br />
* (<code>increment ?z ?z+1</code>) - "There exists some number <code>?z</code> that is 1 less than <code>?z+1</code>, and"<br />
* (<code>add ?x ?y ?z</code>) - "The numbers <code>?x</code> and <code>?y</code> will add up to <code>?z</code>."<br />
<br />
Consider the example of:<br />
<pre>(query (add 2 4 6))</pre><br />
Here's an idea of what's happening when Logic tries to match the query with the facts you've stated.<br />
* ?x+1 = 2, ?y = 4, ?z+1 = 6<br />
* It finds a match for <code>?x = 1</code>, since (<code>increment ?x ?x+1</code>) gives <code>?x = 1</code> because <code>?x+1 = 2</code><br />
* It finds a match for <code>?z = 5</code>, since (<code>increment ?z ?z+1</code>) gives <code>?z = 5</code> because <code>?z+1 = 6</code><br />
* It then checks for a match for (<code>add ?x ?y ?z</code>) which in this case is (<code>add 1 4 5</code>). This goes to our first "base" fact for add. (<code>add 1 4 5</code>) is a success because (<code>increment 4 5</code>) is a true fact [refer again to the "base fact" to see why this is the case]. This is also where we would get a "Failed.", if it turns out that (<code>increment ?x ?x+1</code>) wasn't actually true!<br />
* Hence, all 3 of our hypotheses are true, and so (<code>query (add 2 4 6)</code>) is a success!<br />
<br />
In this example, we only have to recurse once to get to our "base" fact. In other examples, where <code>?x+1</code> is not 2, but some number greater, such as 5, we will have to recurse 4 whole times to get to 1, at which point our "base" fact is reached. <br />
<br />
This recursion is similar to this idea in mathematical equations:<br />
<pre>x + y = z</pre><br />
is the same as<br />
<pre>(x - 1) + y = (z - 1)</pre><br />
is the same as<br />
<br />
<pre>(x - 2) + y = (z - 2)</pre?<br />
and so on... In Logic, we stop when we find that the first term (x) is 1, and then we use our increment facts to determine if the original statement is true, because all of these equations are equivalent.<br />
<br />
== Python syntax and semantics ==<br />
=== <code>print</code> vs <code>return</code> ===<br />
----<br />
==== Andrew's tips ====<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=779 Source: Spring 2014 Piazza (779)]<br />
<br />
Remember the differences between return and print.<br />
* <code>return</code> can only be used in a <code>def</code> statement. It returns a value from a function. Once Python evaluates a <code>return</code> statement, it immediately exits the function.<br />
* <code>print</code> is a function that displays its argument on the screen. It always returns <code>None</code>.<br />
<br />
Examples:<br />
<pre>def foo1(x):<br />
return x<br />
<br />
def foo2(x):<br />
print(x)<br />
<br />
>>> foo2(1) # In foo2, we print 1 ourselves using the print function<br />
1<br />
>>> foo1(1) # HERE, THE PYTHON INTERPRETER PRINTS THE RETURN VALUE OF FOO1. CANNOT STRESS HOW IMPORTANT TO UNDERSTAND THIS<br />
1<br />
>>> foo1(1) + 1<br />
2<br />
>>> foo2(1) + 1<br />
1<br />
Traceback (most recent call last):<br />
File "<stdin>", line 1, in <module><br />
TypeError: unsupported operand type(s) for +: 'NoneType' and 'int'</pre><br />
<br />
=== Function decorators ===<br />
<br />
----<br />
<br />
==== How function decorators work ====<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=638 Source: Spring 2014 Piazza (638)]<br />
<br />
'''Student Question'''<br />
<br />
I'm having difficulties understanding what exactly a function decorator is. Can someone elaborate and potentially provide me with an example other than the one in the readings?<br />
<br />
'''Instructor Answer'''<br />
<br />
So imagine you wanted your functions to print their arguments before they executed them. Here's one way to do this.<br />
<pre>def loud(fn):<br />
def new_fn(*args):<br />
print(args)<br />
return fn(*args)<br />
return new_fn </pre><br />
Here's a function loud that takes in a function and returns a new function that when called, prints out its arguments, and then does what the old function does.<br />
<br />
For example:<br />
<pre>def sq(x):<br />
return x * x<br />
>>> sq(4)<br />
16<br />
>>> sq = loud(sq) # replace the old square with our loud one.<br />
>>> sq(4)<br />
(4,)<br />
16</pre><br />
A function decorator does the same thing as the above. Assuming loud is defined, we can do this:<br />
<pre>@loud<br />
def sq(x):<br />
return x * x<br />
<br />
>>> sq(4)<br />
(4,)</pre><br />
<br />
== Student guides ==<br />
=== How to learn computer science ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=241 Source: Spring 2014 Piazza (241)]<br />
<br />
If you've never programmed before, or if you've never taken a class quite like 61A before, things right now might be scary. Everything is strange and new and there quite a lot to take in all at once. So if you're having a hard time so far, here are a few articles that might help.<br />
<br />
Note: these articles are pretty long, so feel free to read them in multiple sittings.<br />
<br />
'''At the beginning, everything seems a bit scary in CS'''. Michelle Bu, a Berkeley alum and a crazy good hacker, shares one of her experiences when she was a wee n00b in [http://blog.michellebu.com/2013/03/21-nested-callbacks/ 21 Nested Callbacks].<br />
<br />
'''Start here!''' [http://www.jamesmaa.com/2013/08/26/a-beginners-guide-to-computer-science/ "A Beginner's Guide to Computer Science"] Written by Berkeley's own James Maa. James is known for his killer walkthroughs (check out his Productivity guide). This article gives you some background on learning CS and then provides a practical guide on how to learn effectively.<br />
<br />
'''How do we learn?''' Mark Eichenlaub explains in this [http://www.quora.com/Learning/Do-grad-school-students-remember-everything-they-were-taught-in-college-all-the-time/answer/Mark-Eichenlaub Introduction to Learning Theory]. This is quite possibly the best introduction to Learning Theory.<br />
<br />
'''Sometimes, you're stuck and you end up really, really frustrated.''' Marcus Geduld explains [http://www.quora.com/Why-do-we-get-frustrated-when-learning-something/answer/Marcus-Geduld Why do we get frustrated when learning something?]<br />
<br />
=== Quick guide on getting unstuck ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=1264 Source: Quick Guide on Getting Unstuck (Retrieved June 16th, 2014)]<br />
<br />
A major frustration you might encounter in 61A is when you stare at a homework problem and have no idea where to start. Or you write some code and it doesn't pass the doctests, but now what? You work at it for a while, but next thing you know, you've been stuck for hours on the the same problem and have little to show about it.<br />
<br />
So here's a checklist of things you can do when you're stuck. Experienced programmers do these things almost naturally (because of how much practice they've had being stuck), and so while they get stuck just as much as your or I, they always know what to do next.<br />
<br />
# Do I understand what the problem is asking?<br />
## If not, which part of the problem is confusing me?<br />
### Identify the exact sentences/phrases/words/etc.<br />
## Check the given examples. Do they make sense to me?<br />
## Can I come up with my own examples? '''A good indicator that you understand the question is that you can come up with some nontrivial examples of how the function works.'''<br />
# What concepts should I use here?<br />
## Do I understand the concepts? '''Can I explain the concept in English to one of my friends such that they get it?'''<br />
### If not, go back and relearn the specific concepts that are unclear (through discussion, lab, lecture, etc.) Don't read the entire book in order to solve one problem..<br />
## How do I apply the concept to the given problem?<br />
# Write your code and test it.<br />
## Use doctests, '''BUT ALSO LOAD IT INTERACTIVELY (python3 -i ...)'''<br />
### '''Saying "my function works because the doctests pass" is a lot like saying "this airplane will fly because it has wings."'''<br />
## If your code breaks, ask yourself:<br />
### Does it error? Is it a....<br />
#### Syntax error? If so, find the syntax bug and fix it.<br />
#### Logic error? Is it something weird that you don't understand? (E.g. cannot add integer and tuple)<br />
### Why did it do that? Why didn't it do what I expected? Trace through the code by hand with an example (sample values) you came up with in step 0. '''Add calls to <code>print</code> in order to figure out how your function is handling the arguments.'''<br />
# Am I missing a trick?<br />
## Oftentimes you've never seen this type of problem before. This is expected on homework (and this is why homework can take a long time) because if you see it on the homework, then you will be familiar with it on the exam and when you program for fun and profit. <br />
## The key here is just to learn the trick however you need to.<br />
### Stare at it yourself<br />
### Stare at it with others (peers in the class)<br />
### Ask on PIazza what the approach is.<br />
### Stare at it with the TAs/lab Assistants<br />
## '''Once you figure it out, remember the trick so that you can use it next time.'''<br />
# At any point you identify what you're stuck on, you can begin to resolve it.<br />
## Use the tips above. Try things out on the interpreter. Review the lecture/discussion/labs/etc. Do whatever helps you get a better understanding of the problem.<br />
## Once you have something specific that you're stuck on, you can ask other people in the class.<br />
### '''Don't be afraid to ask. Everyone gets stuck and feels stupid sometimes. However, you get to choose how you react to it.'''<br />
### '''At the same time, it really helps to work with people who are on about the same level in the course.'''<br />
## Look on Piazza. Ask questions if yours hasn't come up yet. Be that awesome guy/girl who helps answer questions.<br />
## You can ask the TA if all else fails. We are here to help you learn!<br />
<br />
Here is an old algorithm for studying for tests (the final in this case), salvaged from the sands of time:<br />
<pre>For each topic on the final, find problems on them and do them.<br />
If you can solve them on your own, move on.<br />
Else if you are stuck, look at the solution and figure out if you<br />
are missing a trick or if you do not understand the concepts.<br />
If the problem is that you are stuck on some random trick,<br />
just learn the trick.<br />
Stare at the solutions, ask Piazza, your TA, etc.<br />
Questions you should ask at this stage:<br />
What is the problem asking me to do?<br />
How was I suppose to follow the instructions<br />
to solve the problem?<br />
What part of the problem do I not understand?<br />
What is the fastest way to clear up that misunderstanding?<br />
Then if you think you are still stuck conceptually, review<br />
and learn the concept, however you learn best.<br />
Suggestions for picking up concepts quickly (~1-2 hours):<br />
Discussion notes typically have a very concise recap of the<br />
thing they are going over.<br />
There are guides for particularly tricky things on Piazza,<br />
like Logic, Pairs and Lists in Scheme, etc.<br />
Find them and go over them.<br />
Ask a TA: "what is the best way to learn X?"<br />
If these do not work and you are still shaky after an hour<br />
or two, it might be worth watching a lecture or reading<br />
the notes.</pre><br />
<br />
== Composition ==<br />
=== General style guidelines from 61A website ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=149 Source: Spring 2014 Piazza (149)]<br />
<br />
'''Student Question'''<br />
<br />
Are we required to add any comments to our code to say what a function does, etc.? And does clarity of code count for this project, in which case should we write comments at the end of not-so-clear statements? Thanks.<br />
<br />
'''Student Answer'''<br />
<br />
Docstrings of each function are already provided. If you add a helper function, you should write a docstring for it.<br />
<br />
The [http://inst.eecs.berkeley.edu/~cs61a/sp14/style_guide.html#comments style guide on the course website] advises: "Your actual code should be self-documenting -- try to make it as obvious as possible what you are doing without resorting to comments. Only use comments if something is not obvious or needs to be explicitly emphasized"<br />
<br />
'''Instructor Answer'''<br />
<br />
You should always aim to make your code "self-documenting," meaning it is clear what your code is doing without the aid of comments. You should try to keep the number of comments to a minimum, but if there are lines which you think are unclear/ambiguous, feel free to add a comment.<br />
<br />
All projects in this class contain a 3 point component that is judged solely on your code "composition" -- i.e. whether your code is clear, concise, and easy to read.<br />
<br />
=== Simplifying code ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=1116 Source: Spring 2014 Piazza (1116)]<br />
<br />
Hi everyone, here's some tips about certain functions in Python that can greatly simplify your code for the Trends project.<br />
<br />
'''Sorting keys'''<br />
<br />
You should be familiar with the max and min functions in python, which can take in many arguments and return the maximum value.<br />
<br />
<pre>>>> max(1,3,2)<br />
3</pre><br />
<br />
These functions can also take in lists:<br />
<br />
<pre>>>> min([1,5,1,6])<br />
1</pre><br />
<br />
(In fact they can take in any ''iterable'' and return the maximum/minimum value)<br />
<br />
These functions work because Python knows how to compare the elements in the list (they are all integers). But what if the elements in the list are not integers? Fortunately, there is a way for you to tell Python how to turn each element of the list into a number that it can understand.<br />
<br />
Lets start with an example. Lets say you have a list of strings, and want to find the shortest string in the list. Here's what you can do:<br />
<br />
<pre>>>> min(['hihi', 'bye', 'a', 'zebra'], key=len)<br />
'a'</pre><br />
<br />
Notice the new keyword argument key we are passing into the min function. key is a function that min applies to each element of the list. In this case, the key is the len function, which returns the length of each string. Applying the key function to each element will return a cooresponding integer, which Python can easily use to find the minimum element.<br />
<br />
You can also use keys in the <code>sorted</code> function too, which returns a sorted list of its inputs, based on the key function passed in.<br />
<br />
<pre>>>> sorted(['hihi', 'bye', 'a', 'zebra'], key=len)<br />
['a', 'bye', 'hihi', 'zebra']</pre><br />
<br />
We can have more complex key functions. Here we sort a list of people by their age, which is the second element in the tuple. A key function, once defined, works for sorted, min and max:<br />
<br />
<pre>>>> names = [('Alice', 19, 'F'), ('Bob', 5, 'M'), ('Charlie', 12, 'M')]<br />
>>> get_age = lambda name: name[1]<br />
>>> sorted(names, key=get_age)<br />
[('Bob', 5, 'M'), ('Charlie', 12, 'M'), ('Alice', 19, 'F')]<br />
>>> max(names, key=get_age)<br />
('Alice', 19, 'F')</pre><br />
<br />
'''Dictionary default values'''<br />
<br />
Suppose we have a dictionary mapping names to counts:<br />
<br />
<pre>>>> d = {'apples': 1, 'pears': 9000}</pre><br />
<br />
If we want to add a new pear to the dictionary, we can use:<br />
<br />
<pre>>>> d['pears'] = d['pears'] + 1<br />
>>> d<br />
{'apples': 1, 'pears': 9001}</pre><br />
However we cannot use the same code to add a new item that is not already in the dictionary.<br />
<br />
<pre>>>> d['oranges'] = d['oranges'] + 1<br />
Traceback (most recent call last):<br />
...<br />
KeyError: 'oranges'</pre><br />
<br />
To solve this problem, we have to use <code>dict.setdefault(key, default)</code>. If <code>key</code> is in <code>dict</code>, it will return <code>dict[key]</code>. If not, it will insert <code>key</code> with a value of <code>default</code> and return <code>default</code>. Now we can write:<br />
<br />
<pre>>>> d['oranges'] = d.setdefault('oranges', 0) + 1<br />
>>> d<br />
{'oranges': 1, 'apples': 1, 'pears': 9001}<br />
>>> d['oranges'] = d.setdefault('oranges', 0) + 1<br />
>>> d<br />
{'oranges': 2, 'apples': 1, 'pears': 9001}</pre><br />
<br />
There's actually a even better way of doing this. If you are curious to find out, look up <code>collections.defaultdict</code>.<br />
<br />
'''For loops'''<br />
<br />
If you are iterating through a list and want to get both the item and the index the item is at, the built-in function enumerate is helpful here.<br />
<br />
<pre>>>> a = ["apple", "pear", "orange"]<br />
>>> for index, fruit in enumerate(a):<br />
... print(index, fruit)<br />
...<br />
0 apple<br />
1 pear<br />
2 orange</pre><br />
<br />
You can iterate through each key-value pair in a dictionary with dictionary.items. This is useful if you want to access both the key and the value at the same time.<br />
<br />
<pre>>>> prices = {"apple": 3, "pear": 5, "orange": 20}<br />
>>> for fruit, price in prices.items():<br />
... print(fruit, price)<br />
...<br />
apple 3<br />
pear 5<br />
orange 20</pre><br />
<br />
Hope this helps for the project!<br />
<br />
=== Programming style in scheme ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=2068 Source: Spring 2014 Piazza (2068)]<br />
<br />
Since Scheme has no rules on whitespace and indentation, you could technically write all your Scheme in one line like this:<br />
<pre>(define (fib n) (if (< n 1) n (+ (fib (- n 1)) (fib (- n 2)))))</pre><br />
<br />
But that would be terrible and everyone who had to read your code would hate you for it. Here is a more reasonable version:<br />
<pre>(define (fib n)<br />
(if (< n 1) ; Arguments to if are flush<br />
n ; Each argument gets a new line<br />
(+ (fib (- n 1)) ; Sometimes it makes to insert a newline<br />
(fib (- n 2))))) ; so that you can see arguments side by side</pre><br />
<br />
Remember that code is primarily for humans to read and incidentally for computers to run.<br />
<br />
Here are some more examples:<br />
<pre>(define (deep-map f lst)<br />
(cond ((null? lst) lst)<br />
((list? (car lst))<br />
(cons (deep-map f (car lst))<br />
(deep-map f (cdr lst))))<br />
(else<br />
(cons (f (car lst))<br />
(deep-map f (cdr lst)))) ))<br />
<br />
STk> (deep-map (lambda (x) (* x x)) (list 1 2 (list (list 3) 4)))<br />
(1 4 ((9) 16))</pre><br />
<br />
<pre>(define (reverse lst)<br />
(define (helper lst result)<br />
(if (null? lst)<br />
result<br />
(helper (cdr lst)<br />
(cons (car lst) result)) ))<br />
(helper lst ()) )<br />
<br />
STk> (reverse (list 1 2 3))<br />
(3 2 1)</pre><br />
<br />
=== ucb.py's <code>trace</code> method ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=3000 Source: Spring 2014 Piazza (3000)]<br />
<br />
'''Student Question'''<br />
<br />
How does the trace in ucb.py actually work?<br />
<br />
'''Student Answer'''<br />
<br />
It's actually very similar to the <code>printed</code> function that was defined in the Hog spec!<br />
<br />
<pre>def printed(fn):<br />
def print_and_return(*args):<br />
result = fn(*args)<br />
print('Result:', result)<br />
return result<br />
return print_and_return<br />
</pre><br />
<br />
The main idea is still the same in <code>trace</code> -- we want to figure out all the arguments and save the result of calling the function with those arguments (so we can print it before returning it). One (sort of) major improvement is <code>**kwds</code>. Just like how <code>*args</code> collected all the "positional arguments", <code>**kwds</code> captures all the "keyword arguments" (the ones of the form <code>param='some_val'</code>). This can be a bit confusing, but a couple of examples might help!<br />
<br />
<pre>&gt;&gt;&gt; def add_three(a, b, c):<br />
... return a + b + c<br />
&gt;&gt;&gt; add_three(1, 2, 3) # all arguments are positional (normal)<br />
&gt;&gt;&gt; add_three(1, 2, c=3) # a, b are positional arguments, c is a keyword argument<br />
<br />
&gt;&gt;&gt; def fn(*args, **kwargs):<br />
... print(args)<br />
... print(kwargs)<br />
&gt;&gt;&gt; fn(1, 2, 3)<br />
(1, 2, 3)<br />
{}<br />
&gt;&gt;&gt; fn(1, k=2)<br />
(1,)<br />
{'k' : 2}<br />
&gt;&gt;&gt; fn(a=1, b=2, c=3)<br />
()<br />
{'a':1, 'b':2, 'c':3}<br />
</pre><br />
<br />
Since there are only two types of arguments, having both <code>*args</code> and <code>**kwds</code> covers all our bases. If we passed <code>printed</code> a keyword argument, it could cause an error!<br />
<br />
Everything else in <code>trace</code> just makes the output prettier and more helpful. <code>trace</code> uses the <code>_PREFIX</code> global variable to keep track of how far to indent the next print statement. It catches exceptions and prints them out, before re-raising that exception. It also uses some Python black magic to figure out the name of the function so we can print <code>some_fn</code> instead of <code>&lt;function some_fn at 0x...&gt;</code>.<br />
<br />
If there's a particular aspect of <code>trace</code> that you're confused about, feel free to post a followup!<br />
<br />
== Debugging ==<br />
== Miscellaneous ==<br />
=== Andrew Huang's tips ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=779 Source: Spring 2014 Piazza (779)]<br />
<br />
Order of evaluation matters. The rules for evaluating call expressions are<br />
<br />
# Evaluate the operator<br />
# Evaluate the operands<br />
# Call the operator on the operands (and draw a new frame...)<br />
<br />
For example:<br />
<pre>def baz():<br />
print("this was first")<br />
def bar(x):<br />
print(x)<br />
return lambda x: x * x<br />
return bar # baz is a function that when called, returns a function named bar<br />
<br />
>>> baz() # the operator is baz, there are no operands<br />
this was first<br />
<function bar at 0x2797e20><br />
>>> baz()("this was second") # the operator is baz(), the operand is "this was second"<br />
this was first<br />
this was second<br />
<function <lambda> at 0x2120e20><br />
>>> baz()("this was second")(3) # the operator is baz()("this was second"), the operand is 3<br />
this was first<br />
this was second<br />
9<br />
>>> def bar(x):<br />
... print(x)<br />
... return 3<br />
... <br />
>>> baz()("this was second")(bar("this was third")) # the operator is baz()("this was second"), the operand is bar("this was third")<br />
this was first<br />
this was second<br />
this was third<br />
9</pre><br />
<br />
In order to solve any problem, you must first understand what the problem is asking. Often times it helps to try to explain it concisely in English. It also helps to come up with small examples. For example:<br />
<br />
<pre>def mouse(n):<br />
if n >= 10:<br />
squeak = n // 100<br />
n = frog(squeak) + n % 10<br />
return n<br />
<br />
def frog(croak):<br />
if croak == 0:<br />
return 1<br />
else:<br />
return 10 * mouse(croak+1)<br />
<br />
mouse(21023508479)</pre><br />
<br />
So the goal is to figure out what <code>mouse(21023508479)</code> evaluates to.<br />
<br />
One way is to just step-by-step evaluate this, as an interpreter would.<br />
<br />
Another way, is to understand what the functions are doing.<br />
<br />
Looking at <code>mouse</code>, we see that it takes in a number and outputs that same number if it is smaller than 10. otherwise, it'll return something weird. In order to understand that weird thing, we have to understand what <code>frog</code> is doing. <code>frog</code> takes in a number and if that number is <code>0</code>, return <code>1</code>. Otherwise, return ten times <code>mouse(croak+1)</code>. Well, this is still confusing. Let's try a small example.<br />
<pre>>>> mouse(357)<br />
47<br />
>>> mouse(123)<br />
23<br />
>>> mouse(1234)<br />
44<br />
>>> mouse(12345)<br />
245</pre><br />
There is a pattern. We notice that the resulting number is composed of every other digit of the original, plus one (except for the last one.)<br />
So <code>21023508479</code> is <code>[2+1][0+1][3+1][0+1][4+1][9] = 314159</code>. Can you see how the code reflects that?<br />
However in this particular example, the pattern is definitely tricky to find here, so it might make more sense to brute force it.<br />
<br />
<br />
Remember for recursion, you always need to find three things:<br />
* One or more base cases<br />
* One or more was to reduce the problem<br />
* A way to solve the problem given solutions to smaller problems<br />
For example, the discussion notes, we asked you to write count_stairs. This function takes in n, the number of steps, and returns all the ways you can climb up them if at each step, you can take either one or two steps.<br />
<br />
* Base cases: if we consider n to be the number of steps left to climb, then it makes sense that if there is 1 step left, then there is exactly one way. If there are two steps left, then there are exactly 2 ways (1 step, 1 step, or two steps). Why do we need two base cases here?<br />
* We can make the problem smaller by reducing the n. At each step, we can take one step (resulting in count_stairs(n-1)) or two steps (count_stairs(n-2)).<br />
* Assuming we get the solutions to the two recursive calls, we should add them together to get all the ways we can climb the stairs.<br />
<br />
Thus we end up with<br />
<pre>def count_stairs(n):<br />
if n <= 2:<br />
return n<br />
else:<br />
return count_stairs(n-1) + count_stars(n-2)</pre><br />
Notice that at each stair step, we either take one step or two steps. This is a common pattern in tree recursion. Look through Discussion 3 for more info.<br />
<br />
=== Y combinators (in Scheme) ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=2450 Source: Spring 2014 Piazza (2450)]<br />
<br />
'''Student Question'''<br />
<br />
Can someone explain this to me?<br />
<pre>scm> (((lambda (f) (lambda (x) (f f x)))<br />
(lambda (f k) (if (zero? k) 1 (* k (f f (- k 1)))))) 5)</pre><br />
I've edited the code as follows:<br />
<br />
<pre>(<br />
(<br />
(lambda (f) <br />
(lambda (x) (f f x))<br />
)<br />
(lambda (f k) <br />
(if (zero? k) 1 <br />
(* k (f f (- k 1)))<br />
)<br />
)<br />
) 5<br />
)</pre><br />
<br />
My understanding is that the second lambda function is passed as the first <code>f</code> in the first lambda function and the <code>5</code> is passed in as <code>x</code>. But does that mean <code>f f x</code> becomes the second lambda function with itself and <code>x</code> passed as the arguments to <code>(f k)</code>?<br />
<br />
'''Student Answer'''<br />
<br />
You're on the right track. The first lambda function is a higher order function that takes in a function, and then returns a function that takes one argument. It's actually the third lambda that is then passed into the first lambda (currying!) and then 5 is then passed into the resulting function.<br />
<br />
In case you're curious, this is the Python equivalent:<br />
<br />
<pre>>>> (lambda f: (lambda x: f(f, x))) (lambda f, k: 1 if k == 0 else (k * f(f, k - 1)))(5)</pre><br />
Which is then equivalent to:<br />
<br />
<pre>>>> def func1(f):<br />
def func2(x):<br />
return f(f, x)<br />
return func2<br />
>>> def func3(f, k):<br />
if k == 0:<br />
return 1<br />
else:<br />
return k * f(f, k - 1)<br />
>>> func1(func3)(5)<br />
120</pre><br />
By the way, this is just a fancy way of recursively calculating the factorial using only lambda functions. If you're still curious as to how this works, you could try this in Python tutor. Except I would recommend calculating 3! instead of 5, because it's a lot of frames.<br />
<br />
'''Instructor Answer'''<br />
<br />
Maybe it will look a little nicer in Python:<br />
<br />
<pre>(lambda f: lambda x: f(f, x))(lambda g, k: 1 if k == 0 else (k * g(g, k-1)))(5)</pre><br />
Or maybe not.<br />
<br />
So the idea is, you define a lambda function that takes a function <code>f</code>, and that returns a lambda function that takes an argument <code>x</code> and returns <code>f(f, x)</code>. Then, you call this lambda function you just defined on another lambda function (let's call this <code>func</code>) that takes a function g and another argument k, and is basically the factorial function. This first call returns the inner lambda of the first part, and when that's called with <code>5</code> you're essentially calling <code>func(func, 5)</code>. The chain of recursive calls then works as follows:<br />
<br />
<pre>func(func, 5) -> 5 * func(func, 4) -> 5 * 4 * func(func, 3) -> ... -> 120</pre><br />
In functional programming theory, this is known as a Y Combinator, and it is how you achieve recursion with just lambda functions. If you're wondering why we need <code>func</code> to take in a function as the first parameter, see what would happen if you took that part out!</div>Jeffreylu017https://www.ocf.berkeley.edu/~shidi/cs61a/wiki/GuidesGuides2014-06-20T23:47:59Z<p>Jeffreylu017: /* Composition */</p>
<hr />
<div>== Higher-order functions ==<br />
== Environment diagrams ==<br />
=== Environment diagram Rules ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=131 Source: Spring 2014 Piazza (131)]<br />
<br />
Environment Diagrams are very important in our understanding of how the computer interprets our code.<br />
<br />
'''We will test you on this in every exam.'''<br />
<br />
'''It will never go away.'''<br />
<br />
'''Given that, master it as quickly as you can! :)'''<br />
<br />
Below are the rules I follow when drawing environment diagrams. If you understand and faithfully follow these rules when drawing them, you'll never get them wrong.<br />
<br />
One thing you haven't learned yet is nonlocal. You can skip that particular step for now (step 2 of Assignment).<br />
<br />
Post here if you have any questions!<br />
<br />
You can also take a look at this link for some examples of environment diagrams: http://albertwu.org/cs61a/notes/environments<br />
<br />
For a different perspective on the rules, check out: http://markmiyashita.com/cs61a/sp14/environment_diagrams/rules_of_environment_diagrams/<br />
<br />
A handout with detailed instructions on drawing environment diagrams is also available here (linked on the bottom of the course homepage): http://inst.eecs.berkeley.edu/~cs61a/sp14/pdfs/environment-diagrams.pdf<br />
<br />
<pre>Environment Diagram Rules<br />
=========================<br />
<br />
Creating a Function<br />
--------------------<br />
1. Draw the func <name>(<arg1>, <arg2>, ...)<br />
2. The parent of the function is wherever the function was defined<br />
(the frame we're currently in, since we're creating the function).<br />
3. If we used def, make a binding of the name to the value in the current frame.<br />
<br />
Calling User Defined Functions<br />
------------------------------<br />
1. Evaluate the operator and operands.<br />
2. Create a new frame; the parent is whatever the operator s parent is.<br />
Now this is the current frame.<br />
3. Bind the formal parameters to the argument values (the evaluated operands).<br />
4. Evaluate the body of the operator in the context of this new frame.<br />
5. After evaluating the body, go back to the frame that called the function.<br />
<br />
Assignment<br />
----------<br />
1. Evaluate the expression to the right of the assignment operator (=).<br />
2. If nonlocal, find the frame that has the variable you re looking for,<br />
starting in the parent frame and ending just before the global frame (via<br />
Lookup rules). Otherwise, use the current frame. Note: If there are multiple<br />
frames that have the same variable, pick the frame closest to the current<br />
frame.<br />
3. Bind the variable name to the value of the expression in the identified<br />
frame. Be sure you override the variable name if it had a previous binding.<br />
<br />
Lookup<br />
------<br />
1. Start at the current frame. Is the variable in this frame?<br />
If yes, that's the answer.<br />
2. If it isn't, go to the parent frame and repeat 1.<br />
3. If you run out of frames (reach the Global frame and it's not there), complain.<br />
<br />
Tips<br />
----<br />
1. You can only bind names to values.<br />
No expressions (like 3+4) allowed on environment diagrams!<br />
2. Frames and Functions both have parents.</pre><br />
<br />
== Sequences ==<br />
=== Reversing tuples ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=639 Source: Spring 2014 Piazza (639)]<br />
<br />
'''Student Question'''<br />
<br />
Why does [::-1] tuple work while the tuple [0:3:-1] doesn't?<br />
<br />
I thought the -1 after the second semicolon meant that the interpreter is going to read the indexes "backwards". <br />
<br />
'''Student Answer'''<br />
<br />
The syntax of slicing is <tt>tup[start:end:step]</tt>:<br />
<ul><li>start from index <tt>start</tt> and end just before index <tt>end</tt>, incrementing the index by <tt>step</tt> each time<br />
</li><li>if no <tt>step</tt> is provided, <tt>step</tt> = 1<br />
</li><li>if <tt>step</tt> is positive, default values if not provided: <tt>start</tt> = 0, <tt>end</tt> = <tt>len(tup)</tt><br />
</li><li>if <tt>step</tt> is negative, default values if not provided: <tt>start</tt> = -1, <tt>end</tt> = one position before the start of the string<br />
</li></ul><br />
<br />
<pre>&gt;&gt;&gt; (1, 2, 3)[::-1] # start at index -1, end one position before the start of the string<br />
(3, 2, 1)<br />
&gt;&gt;&gt; (1, 2, 3)[0:3:-1] # start at 0 and go to 3, but step is negative, so this doesn't make sense and an empty tuple is returned<br />
()<br />
</pre><br />
<br />
This is a helpful visualization from http://en.wikibooks.org/wiki/Python_Programming/Strings#Indexing_and_Slicing:<br />
<blockquote><br />
To understand slices, it's easiest not to count the elements themselves. It is a bit like counting not on your fingers, but in the spaces between them. The list is indexed like this:<br />
<pre>Element: 1 2 3 4<br />
Index: 0 1 2 3 4<br />
-4 -3 -2 -1<br />
</pre><br />
</blockquote><br />
More info about slicing at http://stackoverflow.com/a/13005464/2460890.<br />
<br />
=== Slicing with negative step ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=702 Source: Spring 2014 Piazza (702)]<br />
<br />
'''Student Question'''<br />
<br />
if the third example returns an empty tuple because you can't take negative steps from 0 to 4, shouldn't the second example also return an empty tuple?<br />
<br />
Can someone explain why each example returns the respective answers?<br />
<br />
Thanks<br />
<br />
<pre>>>> x= (1,2,3,4)<br />
>>> x[0::-1]<br />
(1,)<br />
>>> x[::-1]<br />
(4, 3, 2, 1)<br />
>>> x[0:4:-1]<br />
()<br />
>>> x[1::-1]<br />
(2, 1)</pre><br />
<br />
'''Instructor Answer'''<br />
<br />
(For reference, the notation is <tt>x[start:end:step]</tt>)<br />
<br />
Python does something a very strange when the step is negative: if you omit the arguments to start and end, Python will fill them with what makes sense for a negative step. In the simple case of <tt>x[::-1]</tt>, Python fills in the start with <tt>len(x)-1</tt> and the end with <tt>-(len(x)+1)</tt>. The end term is strange, but remember that the end term isn't included. We therefore can't use 0, but we can't use -1 either, since that clearly refers to the last element of the tuple. We need to fully wrap the negative index around, to refer to the element "before" the 0th index. This way, Python will start at the end of the tuple and proceed to the beginning of the tuple.<br />
<br />
That's why <tt>x[0:4:-1]</tt> doesn't make sense: how can we start at 0 and end at 4, if we're proceeding backwards?<br />
<br />
And that's why <tt>x[0::-1]</tt> makes sense (albeit, in a strange way): Python is proceeding from the 0 index to the beginning of the list. It includes the start index, which is why you see a 1 pop up.<br />
<br />
Let me know if that was confusing!<br />
<br />
== Recursion ==<br />
== Data abstraction ==<br />
== Time complexity ==<br />
=== Andrew Huang's guide to order of growth and function runtime ===<br />
[https://docs.google.com/document/d/1TxfKmM3MlH032hjSUh92I0kQDVcvmitTSzYObGMr8Bk Source: Guide to Order of Growth and Function Runtime (Retrieved June 16th, 2014)]<br />
<br />
'''Introduction'''<br />
<br />
Confused by $O$, $\Omega$, and $\Theta$?<br />
<br />
Want to figure out the runtime of that tricky function?<br />
<br />
Read this.<br />
<br />
'''NOTE THAT THIS GUIDE STARTS WITH BIG O, WHICH IS DIFFERENT FROM THETA. IF YOU UNDERSTAND BIG O, THETA IS EASY (IN FACT, IT DEFINES THETA IN TERMS OF BIG O BELOW).'''<br />
<br />
First some math.<br />
<br />
Formal definition of O(Big O):<br />
<br />
Let $f(n)$ and $g(n)$ be functions from positive integers to positive reals. We say $f \in O(g)$ (“f grows no faster than g”) if there is a constant $0 < c < \inf$ <such that $f(n) \leq c \cdot g(n)$.<br />
<br />
(Paraphrased from Dasgupta, Papadimitriou, & Vazirani)<br />
<br />
(You'll see this again in CS 170)<br />
<br />
What the heck does that mean?<br />
<br />
Let’s look at math functions for a second (just a second).<br />
<br />
Say $f(n)=5n$ and $g(n)=n^{2}$<br />
<br />
What does that look like on a graph?<br />
<br />
http://www.wolframalpha.com/input/?i=plot+5n+and+n%5E2+from+0+to+10<br />
<br />
There’s a section where $n$ dominates $n^{2}$, from 0 to 5, but we don’t really care, because after that point, $n^{2}$ is larger, all the way to infinity! By the definition, we could scale $n^{2}$ by 5 and we would span that initial gap.<br />
<br />
Thus we can say $5n \in O(n^{2})$ or $f \in O(g)$.<br />
<br />
Can we say the converse? That is, is $n^{2} \in O(5n)$?<br />
<br />
Not at all! From the graph we see that $n^{2}$ grows too quickly for $n$ to catch up, no matter what constant we scale $n$ by.<br />
<br />
So what if $f(n)=n+1000$ and $g(n)=n^{2}$?<br />
<br />
It turns out $n+1000 \in O(n^{2})$ still, because according to the definition, as long as we can multiply $n^{2}$ by some $c$, such that the gap of 1000 is spanned, we’re good. In the case, $c=1001$.<br />
<br />
'''What about and $\Omega$ and $\Theta$?'''<br />
<br />
If you digested all of the above, the rest isn’t scary! (Note, $a \equiv b$ means $a$ is equivalent to $b$)<br />
<br />
$f \in \Omega(g) \equiv g \in O(f)$ (You'll see this again briefly in CS 170)<br />
<br />
$f \in \Theta(g) (f \in O(g) and g \in O(f))$<br />
<br />
'''This means that if $f$ is Theta of $g$, then there exist some $c_{1}$ and $c_{2}$ such that'''<br />
<br />
'''$c_{1}g > f$ and'''<br />
<br />
'''$c_{2}g < f$'''<br />
<br />
'''for all positive integers.'''<br />
<br />
'''What does that mean for Python functions?'''<br />
<br />
Given a function $f$, we want to find out how fast that function runs. One way of doing this is to take out a stopwatch, and clock the amount of time it takes for $f$ to run on some input. However, there are tons of problems with that (different computers => different speeds; only one fixed input? Maybe $f$ is really fast for that input but slow for everything else; next year, all the measurements need to be redone on new computers; etc.) Instead, we'll count the steps that a function needs to perform as a function of its input. For example, here are some of the functions that take one step regardless of their input:<br />
<br />
<code>mul</code><br />
<br />
<code>add</code><br />
<br />
<code>sub</code><br />
<br />
<code>print</code><br />
<br />
<code>return</code><br />
<br />
<code>...</code><br />
<br />
So for example, <code>(3 + 3 * 8) % 3</code> would be 3 steps--one for the multiply, one of the add, and one for the mod.<br />
<br />
Let's take a simple example:<br />
<pre>def square(x):<br />
return x * x</pre><br />
<code>square</code> is a function that for any input, always takes two steps, one of the multiplication, and one for returning. Using the notation, we can say square ∈ Θ(1).<br />
<br />
Functions with iteration (for loops, recursion, etc.), usually multiply the steps by some factor. For example, consider factorial:<br />
<pre>def factorial(n):<br />
if n == 0:<br />
return 1<br />
else:<br />
return n * factorial(n-1)</pre><br />
factorial ∈ Θ(n). Why? Well given some input <code>n</code>, we do <code>n</code> recursive calls. At each recursive call, we carry out 4 steps, one for if <code>n == 0</code>, one for subtraction, one for multiply, one for return. Plus, we have the base case, which is another 2 steps, one for if and one for return. So <code>factorial(n)</code> takes $4n+2$ steps => ∈ Θ(n).<br />
<br />
As mentioned, we care about how the running time (how long the function takes to run) of the function changes, '''as we increase the size of the argument'''. So if we imagine a graph, then the x-axis represents the size of our input, and the y-axis represents how long the function took to run for each x. As the size of the input increases, the function’s runtime does something on the graph. So when we say something like “$O(n^{2})$ where $n$ is the length of the list”, we are saying as we double the size of the list, the function is expected to run at most four times as long. '''NOTE ALSO THAT I SAID WHAT $n$ IS! ALWAYS GIVE YOUR UNITS.'''<br />
<br />
This means that when we compare two functions A and B, A may be overall slower than B as we increase the size of their arguments. However, it’s possible at some specific arguments, the A may run faster (like the $f(n)=5n$ and $g(n)=n^{2}$ example above.)<br />
<br />
'''This also means we do not care about the time taken of any particular input! This implies that all those constant-time base cases all those functions don’t really matter, because they don’t scale. That is, only one specific input causes the base case to be reached, and if we increased the size of the argument, $O(1)$ doesn't necessarily hold.'''<br />
<br />
'''Brief “What runs faster than what”'''<br />
<br />
Sorted from fastest to slowest. This is by no means comprehensive.<br />
* $\Theta(1)$<br />
* $\Theta(\log(n))$<br />
* $\Theta(n)$<br />
* $\Theta(n \log(n))$<br />
* $\Theta(n^{2})$<br />
* $\Theta(n^{3})$<br />
* $\Theta(2^{n})$<br />
* (Anything past this point is kind of ridiculous)<br />
* $\Theta(n!)$<br />
* $\Theta(n^{n})$<br />
<br />
'''So we know about the math and the motivation, now how do we actually assign runtimes to real Python functions?'''<br />
<br />
What you must understand, is that there is no one method for finding the runtime. You MUST look at a function holistically or you won’t get the right answer. What does this mean? '''In order to get the correct runtime, you first must understand what the function is doing! You cannot pattern-match your way to becoming good at this.'''<br />
<br />
'''This cannot be stressed enough: UNITS MATTER, if you say O((n)), you must tell us what $n$ is.'''<br />
<br />
General tips<br />
# UNDERSTAND WHAT THE FUNCTION IS DOING!!!<br />
# Try some sample input. That is, pretend you’re the interpreter and execute the code with some small inputs. What is the function doing with the input? Having concrete examples lets you do tip 1 better. You can also graph how the runtime increases as the argument size increases.<br />
# If applicable, draw a picture of the tree of function calls. This shows you the "growth" of the function or how the function is getting "bigger", which will help you do tip 1 better.<br />
# If applicable, draw a picture of how the input is being modified through the function calls. For example, if your input is a list and your function recursively does something to that list, draw out a list, then draw out parts of the list underneath it that are called during the recursion. Helps with tip 1.<br />
# See tip 1.<br />
<br />
Anyways, let's examine some common runtimes (keep scrolling). Remember, '''this is in no way a comprehensive list, NOR IS IT TRYING TO TEACH YOU HOW TO FIND THEM.''' This post is just to give you a starting point into orders of growth by showing you some examples and basic details about each runtime.<br />
<br />
'''Constant $\Theta(1)$'''<br />
<br />
''What it looks like:''<br />
<br />
http://www.wolframalpha.com/input/?i=plot+5<br />
<br />
''Example:''<br />
<br />
<pre>def add(x, y):<br />
return x + y</pre><br />
$add \in \Theta(1)$, where 1 is.. well a constant...<br />
<br />
''Approach:''<br />
<br />
The key behind constant time functions is that regardless of the size of the input, they always run the same number of instructions.<br />
<br />
''Don’t fall for this Trap:''<br />
<br />
<pre>def bar(n):<br />
if n % 7 == 0:<br />
return "Bzzst"<br />
else:<br />
return bar(n -1)</pre><br />
$\mathtt{bar} \in \Theta(1)$. Why?<br />
<br />
'''Logarithmic $\Theta(\log(n))$'''<br />
<br />
''What it looks like:''<br />
<br />
http://www.wolframalpha.com/input/?i=plot+4log3n+from+0+to+10<br />
<br />
''Example:''<br />
<br />
<pre>def binary_search(sorted_L, n):<br />
""" sorted_L is a list of numbers sorted from<br />
smallest to largest<br />
"""<br />
if sorted_L == []:<br />
return False<br />
mid_num = sorted_L[len(sorted_L) // 2]<br />
if n == mid_num:<br />
return True<br />
elif n < mid_num:<br />
return binary_search(sorted_L[:mid_num], n)<br />
else:<br />
return binary_search(sorted_L[mid_num:], n)</pre><br />
$\mathtt{binary\_search} \in \Theta(log(n))$, where $n$ is the number of elements in <code>sorted_L</code>.<br />
<br />
''Approach:''<br />
<br />
Logarithmic functions scale down the size of the problem by some constant every iteration (either with a recursive loop, a for loop, or a while loop). Also, logarithmic functions do not branch out--they generally do not make more than one call to themselves per recursion.<br />
<br />
'''Linear $\Theta(n)$'''<br />
<br />
''What it looks like:''<br />
<br />
http://www.wolframalpha.com/input/?i=plot+8n+from+0+to+10<br />
<br />
''Examples:''<br />
<br />
<pre>def sum_list(L):<br />
sum = 0<br />
for e in L:<br />
sum += e<br />
return sum</pre><br />
$\mathtt{sum\_list} \in \Theta(n)$, where $n$ is the number of elements in $L$.<br />
<br />
</pre>def countdown(n):<br />
if n > 0:<br />
print(n)<br />
countdown(n - 1)<br />
else:<br />
print("Blast off!")</pre><br />
$\mathtt{countdown} \in \Theta(n)$, where $n$ is n.<br />
<br />
''Approach:''<br />
<br />
Linear functions usually act on sequences or other collections of data. In that case, the function will go through the elements once or twice or ''k'' times, where $k<<n$. If the function acts on a number, the number usually gets smaller by a constant each iteration.<br />
<br />
Don't fall for this trap:<br />
<pre>def two_for_loops(n):<br />
for a in range(n):<br />
if n == 4:<br />
for y in range(n):<br />
print("Admiral Ackbar")<br />
else:<br />
print("It's a trap!")</pre><br />
$\mathtt{two\_for\_loops} \in \Theta(n)$, where $n$ is n. Why?<br />
<br />
'''Loglinear/Linearithmic $\Theta(n \log(n))$'''<br />
<br />
''What it looks like:''<br />
<br />
http://www.wolframalpha.com/input/?i=plot+nlog%28n%29+from+0+to+10<br />
<br />
''Example:''<br />
<br />
<pre>def merge(s1, s2):<br />
if len(s1) == 0:<br />
return s2<br />
elif len(s2) == 0:<br />
return s1<br />
elif s1[0] < s2[0]:<br />
return [s1[0]] + merge(s1[1:], s2)<br />
else:<br />
return [s2[0]] + merge(s1, s2[1:])<br />
<br />
def mergesort(lst):<br />
if len(lst) <= 1:<br />
return lst<br />
else:<br />
middle = len(lst) // 2<br />
return merge(mergesort(lst[:middle]), \<br />
mergesort(lst[middle:]))</pre><br />
$\mathtt{mergesort} \in \Theta(n \log(n))$, where $n$ is the number of elements in <code>lst</code>.<br />
<br />
Approach:<br />
These functions tend to make two recursive calls, each making the problem smaller by a half. There's a neat way to see this. For example in mergesort, start with an entire line, which represents mergesort called on the initial list. From there, the list gets split in half by the two recursive calls to mergesort in the code, so draw the another line right below the first, of the same length, but with a small gap in the middle to represent the split. Repeat until you're tired. At the end, you get a rectangle that's nwide and (n)tall!<br />
<pre><nowiki><br />
---------------<br />
------- -------<br />
--- --- --- ---<br />
- - - - - - - -<br />
</nowiki></pre><br />
The total area is the runtime, $\Theta(n \log(n))$<br />
<br />
''Don’t fall for this trap:''<br />
<br />
Don’t confuse functions that have an average running time of n(n)(like quicksort) with functions that are in (n(n))<br />
<br />
'''Polynomial $\Theta(n^{2})$,$\Theta(n^{3})$, etc.'''<br />
<br />
''What it looks like:''<br />
<br />
http://www.wolframalpha.com/input/?i=plot+n%5E2%2B3+from+0+to+10<br />
<br />
''Example:''<br />
<br />
<pre>def print_a_grid(n):<br />
for _ in range(n):<br />
for _ in range(n):<br />
print("+", end="")<br />
print("")</pre><br />
$\mathtt{print\_a\_grid} \in \Theta(n^{2})$, where $n$ is n.<br />
<br />
''Approach:''<br />
<br />
Polynomial functions will examine each element of an input many, many times, as opposed to linear functions, which examine some constant number of times.<br />
<br />
''Don’t fall into this trap:''<br />
<br />
Don’t get polynomial confused with exponential (below).<br />
<br />
'''Exponential'''<br />
<br />
''What it looks like:''<br />
<br />
http://www.wolframalpha.com/input/?i=plot+2%5En+from+0+to+10<br />
<br />
''Example:''<br />
<br />
<pre>(define (strange-add x)<br />
(if (zero? x)<br />
1<br />
(+ (strange-add (- x 1))<br />
(strange-add (- x 1)) )))<br />
<br />
def strange_add(x):<br />
if x == 0:<br />
return 1<br />
else:<br />
return strange_add(x - 1) + strange_add(x - 1)</pre><br />
$\mathtt{strange\_add} \in \Theta(2^{n})$, where $n$ is x.<br />
<br />
''Approach:''<br />
<br />
Exponential functions tend to branch out as you get deeper and deeper into their call tree, and each call only makes the work smaller by a little bit. For example, <code>(strange-add 8)</code> calls <code>(strange-add 7)</code> and <code>(strange-add 7)</code>. Those two calls each make two calls, <code>(strange-add 6)</code>, <code>(strange-add 6)</code>, <code>(strange-add 6)</code>, and <code>(strange-add 6)</code> respectively, and so on.<br />
<br />
== Mutability ==<br />
=== Michelle Chang's guide to immutability and mutability ===<br />
[https://d1b10bmlvqabco.cloudfront.net/attach/hoxc5uu6sud761/gozdkhgdUbT/htdlpko411i0/Python__Immutable_vs_Mutable.pdf Source: What You Should Know about Immutability vs Mutability]<br />
<br />
== Mutable data-structures ==<br />
== Object-oriented programming ==<br />
=== Inheritance and class vs instance attributes ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=1413 Source: Spring 2014 Piazza (1413)]<br />
<br />
'''Student Question'''<br />
<br />
I'm confused on how Classes and Inheritance work.<br />
<br />
If there's a Parent class and a Child class, when coding in the Child class, when do you write <code>Parent.attribute</code>, when do you write <code>Child.attribute</code>, and when do you write <code>self.attribute</code>?<br />
<br />
Also, I'm also confused as to when to put <code>self</code> into the parentheses as well.<br />
<br />
'''Instructor Answer'''<br />
<br />
<code>Parent.attribute</code> and <code>Child.attribute</code> would both be ways of accessing a'''class variable'''. These are variables that can be accessed without creating new '''instances''' of the that class.<br />
<br />
<code>self.attribute</code> would be used in '''methods''' to access an '''instance variable''' (an attribute specific to an instance).<br />
<br />
So for example, <code>Insect.watersafe</code> is <code>False</code>, but <code>Bee.watersafe</code> is <code>True</code>. These are class attributes because you don't have to create an Insect object or a Bee object in order to say <code>Insect.watersafe</code> or <code>Bee.watersafe</code>.<br />
<br />
However it wouldn't make any sense to say <code>Bee.armor</code>, since armor is an '''instance variable'''. You have to first create a new Bee before you could ask it for it's armor. If you created a second Bee after that, the second Bee would also have its own armor.<br />
<br />
There's a lot of vocab (in bold) that might trip you up. Try reading Discussion 6 and posting a followup if you're still unsure!<br />
<br />
== Iterables, iterators and generators ==<br />
== Scheme ==<br />
=== Scheme lists ===<br />
----<br />
==== <code>append</code> vs <code>cons</code> vs <code>list</code> ====<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=2067 Source: Spring 2014 Piazza (2067)]<br />
<br />
This post isn't meant to be comprehensive. '''Ask questions in lab or as a followup here if you're confused.'''<br />
One of its major flaws is that it doesn't cover box and pointers. '''LEARN BOX AND POINTERS. '''<br />
<br />
Here is a beautiful web based Scheme interpreter that will draw box and pointer diagrams for you. Run through the examples below with this thing:<br />
http://xuanji.appspot.com/js-scheme-stk/index.html<br />
<br />
In order to understand these three procedures, you first have to understand a little about Pairs and Lists.<br />
<br />
'''Pairs''' are data structures that have two slots. You can put different stuff in these slots, like numbers or words or sentences or booleans--pretty much anything. You make a pair using cons.<br />
<pre>STk> (cons 'foo 'bar)<br />
(foo . bar)<br />
<br />
STk> (cons 1 'ring)<br />
(1 . ring)<br />
<br />
STk> (cons (+ 1 2 3) (member? 3 '(the 3 stooges)))<br />
(6 . #t)</pre><br />
<br />
In order to get stuff from a pair that you have made, you use <code>car</code> and <code>cdr</code>. <code>car</code> gets the thing in the first slot. <code>cdr</code> gets the thing in the second slot.<br />
<br />
<pre>STk> (define foo (cons 'x 'y))<br />
foo<br />
<br />
STk> foo<br />
(x . y)<br />
<br />
STk> (car foo)<br />
x<br />
<br />
STk> (cdr foo)<br />
y</pre><br />
That was straightforward. Now for the trippy part:<br />
'''You can put pairs inside of pairs:'''<br />
<pre>STk> (define foo (cons (cons 3 4) 5))<br />
foo<br />
<br />
STk> foo<br />
((3 . 4) . 5)<br />
<br />
STk> (car foo)<br />
(3 . 4)<br />
<br />
STk> (car (car foo))<br />
3<br />
<br />
STk> (caar foo) ; functionally equivalent as above.<br />
3<br />
<br />
STk> (cdr foo)<br />
5<br />
<br />
STk> (cdr (car foo))<br />
4<br />
<br />
STk> (cdar foo) ; functionally equivalent as above.<br />
4</pre> <br />
<br />
There's a certain style of pair nesting that is especially useful&mdash;'''Lists.'''<br />
<br />
Each list has these properties:<br />
* Every list is a pair or the empty list (denoted by '() or nil).<br />
* The car of a nonempty list is some item.<br />
* The cdr of a nonempty list must be another list.<br />
<pre>STk> (cons 1 (cons 2 (cons 3 '()))) ; list of numbers<br />
(1 2 3)<br />
<br />
STk> (define stooges (cons 'larry (cons 'curly (cons 'moe nil))))<br />
stooges<br />
<br />
STk> stooges<br />
(larry curly moe)<br />
<br />
STk> (car stooges)<br />
larry<br />
<br />
STk> (cdr stooges) ; Calling cdr on a non-empty list gives you another list!<br />
(curly moe)<br />
<br />
STk> (cadr stooges)<br />
curly<br />
<br />
STk> (cdar stooges) ; Why does this break?<br />
*** Error:<br />
cdar: bad list: (moe larry curly)<br />
Current eval stack:<br />
__________________<br />
0 (cdar stooges)<br />
<br />
STk> (define not-a-list (cons 'foo (cons 'bar 'baz))) ; This is not a list.<br />
not-a-list<br />
<br />
STk> not-a-list ; What property does this break?</pre><br />
Notice how Scheme knew that we were making lists. Before we had <code>parens</code> and periods which organized our items. Scheme now recognizes that we're making a list and does away with the periods and some of the <code>parens</code>.<br />
<br />
If you stare a bit at the list rules above, you can notice we used a recursive definition to define lists. Recursion... '''on data'''!<br />
<br />
Let's talk about <code>list</code>. <code>list</code> takes a bunch of stuff and makes a list out of them. The stuff can be anything. Words, numbers, pairs, other lists. <code>list</code> doesn't care. [picture of a honey badger]<br />
<pre>STk> (list 'foo 'bar' 'baz) ; Lists takes anything and makes a list out of it.<br />
(foo bar baz)<br />
<br />
STk> (list 'foo ((lambda (x) (+ x 4)) 8) #f (cons 1 (cons 3 4)) (cons 1 (cons 2 nil)) (list 1 2 3)) ; ANYTHING <br />
(foo 12 #f (1 3 . 4) (1 2) (1 2 3))<br />
<br />
STk> (list 'x 'y 'z)<br />
(x y z)<br />
<br />
STk> '(x y z) ; Sometimes you can get away with using quote to make literal lists. Yes, sentences are secretly lists.<br />
(x y z)</pre><br />
Now we can talk about <code>append</code>:<br />
<pre>STk> (append '(a b c) '(d e f) '(g h i)) ; Append takes in lists and appends them together.<br />
(a b c d e f g h i)<br />
<br />
STk> (append 'foo '(1 2 3)) ; foo is not a list. Stuff will break.<br />
*** Error:<br />
append: argument is not a list: foo<br />
Current eval stack:<br />
__________________<br />
0 (append (quote foo) (quote (1 2 3)))</pre><br />
You know that <code>cons</code> makes a pair. You also know that you can make a list out of pairs. You can abuse <code>cons</code> for your own maniacal purposes.<br />
<pre>STk> (cons 'joe stooges) ; Put stuff at the beginning of a list!<br />
(joe larry curly moe)</pre><br />
'''The following only applies to the STk interpreter.'''<br />
<pre>STk> (append '(1 2 3) 'foo) ; Wait... what?<br />
(1 2 3 . foo)<br />
<br />
STk> (append '(1 2 3) (cons 4 5)) ; The plot thickens!<br />
(1 2 3 4 . 5)<br />
<br />
STk> (append stooges 'shemp) ; You should really figure out why this works.<br />
(larry curly moe . shemp)</pre><br />
<br />
To summarize:<br />
* append takes in lists and outputs a big list.<br />
* cons takes in things and makes a pair out of them. However, we know that lists are made of pairs, so we can throw together a list if we use cons a certain way<br />
* list takes in things and makes a list out of those things, regardless of what they are.<br />
<br />
=== Tail recursion ===<br />
----<br />
=== Mark Miyashita's guide on tail recursion ===<br />
[http://markmiyashita.com/cs61a/sp14/tail_recursion/tail_recursion_and_tail_optimized_calls/ Source: Tail Recursion and Tail Optimized Calls]<br />
<br />
First off, I think this is an excellent article to read about tail recursion and tail calls in Python: [http://paulbutler.org/archives/tail-recursion-in-python/ here]<br />
<br />
Basically, you can write tail recursive functions in any language. Tail recursion, in one sentence, is where you return the answer in the final frame instead of following the frames back up to the original frame. For example, we have factorial which is normally not tail recursive:<br />
<br />
<pre>def factorial(n):<br />
if n == 1:<br />
return 1<br />
return n * factorial(n - 1)</pre><br />
<br />
because it needs to keep track of the <code>n *</code> at each level of recursion.<br />
<br />
The following implementation of <code>factorial</code>, is tail recursive because at the end of the last frame, it can return the answer, instead of going back up through all the frames to multiply and compute the answer:<br />
<br />
<pre>def factorial(n):<br />
def helper(n, total):<br />
if n == 1:<br />
return total<br />
return helper(n - 1, total * n)<br />
return helper(n, 1)</pre><br />
<br />
You can’t have tail optimized calls in Python – at least, not like the code that we defined above. You can define your own sort of tail optimized way of evaluating the functions by using lambdas, and I believe the article linked at the top of this post goes into detail about how to implement this if you are interested. In Scheme, the language detects when you have something like the helper function in the example above where your return statement consists of only the recursive call. In the first example, we have the <code>n *</code> and the recursive call which means it cannot be tail optimized because it needs to keep track of all of the frames that it creates. In a tail optimized call, Scheme will get rid of the frames that are no longer necessary.<br />
<br />
'''tl;dr''' – Tail recursion can be done in any language where the basic idea is that you return the answer in the final frame of recursion. Tail optimized calls are a Scheme (and some other languages, not including Python) feature where it will get rid of the frames above, if certain conditions are met – such as where the return statement is only the recursive call and nothing else. The cases in which Scheme uses a tail optimized call are located on the lecture slides located [http://www-inst.eecs.berkeley.edu/~cs61a/sp13/slides/35-TailCalls_6pp.pdf here].<br />
<br />
==== Tail recursion in Python ====<br />
[http://kylem.net/programming/tailcall.html Source: http://kylem.net/programming/tailcall.html (Retrieved June 16th, 2014)]<br />
<br />
In this page, we’re going to look at tail call recursion and see how to force Python to let us eliminate tail calls by using a trampoline. We will go through two iterations of the design: first to get it to work, and second to try to make the syntax seem reasonable. I would not consider this a useful technique in itself, but I do think it’s a good example which shows off some of the power of decorators.<br />
<br />
The first thing we should be clear about is the definition of a tail call. The “call” part means that we are considering function calls, and the “tail” part means that, of those, we are considering calls which are the last thing a function does before it returns. In the following example, the recursive call to f is a tail call (the use of the variable <code>ret</code> is immaterial because it just connects the result of the call to <code>f</code> to the return statement), and the call to <code>g</code> is not a tail call because the operation of adding one is done after <code>g</code> returns (so it’s not in “tail position”).<br />
<br />
<pre>def f(n) :<br />
if n > 0 :<br />
n -= 1<br />
ret = f(n)<br />
return ret<br />
else :<br />
ret = g(n)<br />
return ret + 1</pre><br />
<br />
'''1. Why tail calls matter'''<br />
<br />
Recursive tail calls can be replaced by jumps. This is called “tail call eliminination,” and is a transformation that can help limit the maximum stack depth used by a recursive function, with the benefit of reducing memory traffic by not having to allocate stack frames. Sometimes, recursive function which wouldn’t ordinarily be able to run due to stack overflow are transformed into function which can.<br />
<br />
Because of the benefits, some compilers (like <code>gcc</code>) perform tail call elimination[1], replacing recursive tail calls with jumps (and, depending on the language and circumstances, tail calls to other functions can sometimes be replaced with stack massaging and a jump). In the following example, we will eliminate the tail calls in a piece of code which does a binary search. It has two recursive tail calls.<br />
<br />
<pre>def binary_search(x, lst, low=None, high=None) :<br />
if low == None : low = 0<br />
if high == None : high = len(lst)-1<br />
mid = low + (high - low) // 2<br />
if low > high :<br />
return None<br />
elif lst[mid] == x :<br />
return mid<br />
elif lst[mid] > x :<br />
return binary_search(x, lst, low, mid-1)<br />
else :<br />
return binary_search(x, lst, mid+1, high)</pre><br />
<br />
Supposing Python had a <code>goto</code> statement, we could replace the tail calls with a jump to the beginning of the function, modifying the arguments at the call sites appropriately:<br />
<br />
<pre>def binary_search(x, lst, low=None, high=None) :<br />
start:<br />
if low == None : low = 0<br />
if high == None : high = len(lst)-1<br />
mid = low + (high - low) // 2<br />
if low > high :<br />
return None<br />
elif lst[mid] == x :<br />
return mid<br />
elif lst[mid] > x :<br />
(x, lst, low, high) = (x, lst, low, mid-1)<br />
goto start<br />
else :<br />
(x, lst, low, high) = (x, lst, mid+1, high)<br />
goto start</pre><br />
<br />
which, one can observe, can be written in actual Python as<br />
<br />
<pre>def binary_search(x, lst, low=None, high=None) :<br />
if low == None : low = 0<br />
if high == None : high = len(lst)-1<br />
while True :<br />
mid = low + (high - low) // 2<br />
if low > high :<br />
return None<br />
elif lst[mid] == x :<br />
return mid<br />
elif lst[mid] > x :<br />
high = mid - 1<br />
else :<br />
low = mid + 1</pre><br />
<br />
I haven’t tested the speed difference between this iterative version and the original recursive version, but I would expect it to be quite a bit faster because of there being much, much less memory traffic.<br />
<br />
Unfortunately, the transformation makes it harder to prove the binary search is correct in the resulting code. With the original recursive algorithm, it is almost trivial by induction.<br />
<br />
Programming languages like Scheme depend on tail calls being eliminated for control flow, and it’s also necessary for continuation passing style.[2]<br />
<br />
'''2. A first attempt'''<br />
<br />
Our running example is going to be the factorial function (a classic), written with an accumulator argument so that its recursive call is a tail call:<br />
<br />
<pre>def fact(n, r=1) :<br />
if n <= 1 :<br />
return r<br />
else :<br />
return fact(n-1, n*r)</pre><br />
<br />
If <code>n</code> is too large, then this recursive function will overflow the stack, despite the fact that Python can deal with really big integers. On my machine, it can compute <code>fact(999)</code>, but <code>fact(1000)</code> results in a sad <code>RuntimeError: Maximum recursion depth exceeded</code>.<br />
<br />
One solution is to modify fact to return objects which represent tail calls and then to build a trampoline underneath fact which executes these tail calls after fact returns. This way, the stack depth will only contain two stack frames: one for the trampoline and another for each call to fact.<br />
<br />
First, we define a tail call object which reifies the concept of a tail call:<br />
<br />
<pre>class TailCall(object) :<br />
def __init__(self, call, *args, **kwargs) :<br />
self.call = call<br />
self.args = args<br />
self.kwargs = kwargs<br />
def handle(self) :<br />
return self.call(*self.args, **self.kwargs)</pre><br />
<br />
This is basically just the thunk <code>lambda : call(*args, **kwargs)</code>, but we don’t use a thunk because we would like to be able to differentiate between a tail call and returning a function as a value.<br />
<br />
The next ingredient is a function which wraps a trampoline around an arbitrary function:<br />
<br />
<pre>def t(f) :<br />
def _f(*args, **kwargs) :<br />
ret = f(*args, **kwargs)<br />
while type(ret) is TailCall :<br />
ret = ret.handle()<br />
return ret<br />
return _f</pre><br />
<br />
Then, we modify fact to be<br />
<br />
<pre>def fact(n, r=1) :<br />
if n <= 1 :<br />
return r<br />
else :<br />
return TailCall(fact, n-1, n*r)</pre><br />
<br />
Now, instead of calling <code>fact(n)</code>, we must instead invoke <code>t(fact)(n)</code> (otherwise we’d just get a TailCall object).<br />
<br />
This isn’t that bad: we can get tail calls of arbitrary depth, and it’s Pythonic in the sense that the user must explicitly label the tail calls, limiting the amount of unexpected magic. But, can we eliminate the need to wrap t around the initial call? I myself find it unclean to have to write that <code>t</code> because it makes calling fact different from calling a normal function (which is how it was before the transformation).<br />
<br />
'''3. A second attempt'''<br />
<br />
The basic idea is that we will redefine fact to roughly be <code>t(fact)</code>. It’s tempting to just use <code>t</code> as a decorator:<br />
<br />
<pre>@t<br />
def fact(n, r=1) :<br />
if n <= 1 :<br />
return r<br />
else :<br />
return TailCall(fact, n-1, n*r)</pre><br />
<br />
(which, if you aren’t familiar with decorator syntax, is equivalent to writing <code>fact = t(fact)</code> right after the function definition). However, there is a problem with this in that the fact in the returned tail call is bound to <code>t(fact)</code>, so the trampoline will recursively call the trampoline, completely defeating the purpose of our work. In fact, the situation is now worse than before: on my machine, <code>fact(333)</code> causes a <code>RuntimeError</code>!<br />
<br />
For this solution, the first ingredient is the following class, which defines the trampoline as before, but wraps it in a new type so we can distinguish a trampolined function from a plain old function:<br />
<br />
<pre>class TailCaller(object) :<br />
def __init__(self, f) :<br />
self.f = f<br />
def __call__(self, *args, **kwargs) :<br />
ret = self.f(*args, **kwargs)<br />
while type(ret) is TailCall :<br />
ret = ret.handle()<br />
return ret</pre><br />
<br />
and then we modify <code>TailCall</code> to be aware of <code>TailCallers</code>:<br />
<br />
<pre>class TailCall(object) :<br />
def __init__(self, call, *args, **kwargs) :<br />
self.call = call<br />
self.args = args<br />
self.kwargs = kwargs<br />
def handle(self) :<br />
if type(self.call) is TailCaller :<br />
return self.call.f(*self.args, **self.kwargs)<br />
else :<br />
return self.call(*self.args, **self.kwargs)</pre><br />
<br />
Since classes are function-like and return their constructed object, we can just decorate our factorial function with <code>TailCaller</code>:<br />
<br />
<pre>@TailCaller<br />
def fact(n, r=1) :<br />
if n <= 1 :<br />
return r<br />
else :<br />
return TailCall(fact, n-1, n*r)</pre><br />
<br />
And then we can call fact directly with large numbers!<br />
<br />
Also, unlike in the first attempt, we can now have mutually recursive functions which all perform tail calls. The first-called <code>TailCall</code> object will handle all the trampolining.<br />
<br />
If we wanted, we could also define the following function to make the argument lists for tail calls be more consistent with those for normal function calls:[3]<br />
<br />
<pre>def tailcall(f) :<br />
def _f(*args, **kwargs) :<br />
return TailCall(f, *args, **kwargs)<br />
return _f</pre><br />
<br />
and then fact could be rewritten as<br />
<br />
<pre>@TailCaller<br />
def fact(n, r=1) :<br />
if n <= 1 :<br />
return r<br />
else :<br />
return tailcall(fact)(n-1, n*r)</pre><br />
<br />
One would hope that marking the tail calls manually could just be done away with, but I can’t think of any way to detect whether a call is a tail call without inspecting the source code. Perhaps an idea for further work is to convince Guido von Rossum that Python should support tail recursion (which is quite unlikely to happen).<br />
<br />
[1] This is compiler-writer speak. For some reason, “elimination” is what you do when you replace a computation with something equivalent. In this case, it’s true that the call is being eliminated, but in its place there’s a jump. The same is true for “common subexpression elimination” (known as CSE), which takes, for instance,<br />
<br />
<pre>a = b + c<br />
d = (b + c) + e<br />
and replaces it with<br />
a = b + c<br />
d = a + e</pre><br />
<br />
Sure, the <code>b+c</code> is eliminated from the second statement, but it’s not really gone...<br />
The optimization known as “dead code elimination” actually eliminates something, but that’s because dead code has no effect, and so it can be removed (that is, be replaced with nothing).<br />
<br />
[2] In Scheme, all loops are written as recursive functions since tail calls are the pure way of redefining variables (this is the same technique Haskell uses). For instance, to print the numbers from 1 to 100, you’d write<br />
<br />
<pre>(let next ((n 1))<br />
(if (<= n 100)<br />
(begin<br />
(display n)<br />
(newline)<br />
(next (+ n 1)))))</pre><br />
<br />
where next is bound to be a one-argument function which takes one argument, <code>n</code>, and which has the body of the <code>let</code> statement as its body. If that <code>100</code> were some arbitrarily large number, the tail call to next had better be handled as a jump, otherwise the stack would overflow! And there’s no other reasonable way to write such a loop!<br />
<br />
Continuation passing style is commonly used to handle exceptions and backtracking. You write functions of the form<br />
<br />
<pre>(define (f cont)<br />
(let ((cont2 (lambda ... (cont ...) ...)))<br />
(g cont2)))</pre><br />
<br />
along with functions which take multiple such f’s and combines them into another function which also takes a single cont argument. I’ll probably talk about this more in another page, but for now notice how the call to g is in the tail position.<br />
<br />
[3] This is basically a curried[4] version of <code>TailCall</code>.<br />
<br />
[4] That is, ''Schönfinkelized''.<br />
<br />
=== Miscellaneous ===<br />
----<br />
==== Useful Scheme Procedures ====<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=2226 Source: Spring 2014 Piazza (2226)]<br />
<br />
Here is a short list of Scheme procedures that you might use in writing your programs:<br />
<pre>; define - defines a variable or a procedure<br />
(define my-variable 4)<br />
(define (square x)<br />
(* x x))<br />
<br />
; if - conditional branching akin to if ... else<br />
(define (fib n)<br />
(if (< n 2)<br />
n<br />
(+ (fib (- n 1)) (fib (- n 2))) ))<br />
<br />
; conditional branching akin to if ... elif ... elif ... else<br />
(define (deep-map f lst)<br />
(cond ((null? lst) lst)<br />
((list? (car lst))<br />
(cons (deep-map f (car lst))<br />
(deep-map f (cdr lst))))<br />
(else<br />
(cons (f (car lst))<br />
(deep-map f (cdr lst)))) ))<br />
<br />
; and - outputs the the rightmost value if all of the arguments evaluate to #t. Outputs #f otherwise.<br />
STk> (and 0 1 2 3)<br />
3<br />
<br />
; or - outputs the the first value that evaluates to #t. Outputs #f otherwise.<br />
STk> (or 0 1 2 3)<br />
0<br />
<br />
; equal - tests if symbols are the same<br />
STk> (equal? 'foo 'bar)<br />
#f<br />
STk> (equal? 'foo 'foo)<br />
#t<br />
<br />
STk> (list? 'foo)<br />
#f<br />
STk> (list? '(1 2 3))<br />
#t<br />
STk> (list? '())<br />
#t<br />
<br />
STk> (null? '(1 2 3))<br />
#f<br />
STk> (null? ())<br />
#t<br />
<br />
; member? - tests if a symbol is in a list EDIT: NOT BUILT IN (BUT SUPER USEFUL SEE FOLLOWUP)<br />
STk> (member? 'quick '(the quick brown fox jumped over the lazy dog))<br />
#t<br />
<br />
; number? - checks if input is a number<br />
STk> (number? 42)<br />
#t<br />
STk> (number? #t)<br />
#f<br />
<br />
; remainder - computes the remainder of the first number divided by the second<br />
STk> (remainder 100 21)<br />
16</pre><br />
<br />
== Streams ==<br />
== Logic ==<br />
=== Quick Guide to Logic Programming ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=2524 Source: Spring 2014 Piazza (2524)]<br />
''Note: Someone should convert this from scmlog to Logic notation''<br />
<br />
'''Here's something I wrote a long time ago. The logic interpreter scmlog still exists and you should be able to access it using your cs61a-xx account. Post a followup if you have any questions. Hope this helps!'''<br />
<br />
<br />
'''Introduction:''' logic programming is a completely different way to think about telling computers to do stuff. Instead of telling the computer what to compute, you give the computer facts and ask it questions. The computer does its own thinking given the facts and the question, and then returns an answer.<br />
<br />
'''Table of Contents'''<br />
- Introduction<br />
- What is Logic Programming<br />
- - Giving Facts and Asking Questions<br />
- - More Complicated Facts and Questions<br />
- How to write Logic Programs<br />
- - Common Pitfalls<br />
- More Resources<br />
<br />
<br />
'''What is Logic Programming?'''<br />
Logic Programming is a way to ask the computer questions and get answers without telling it explicitly how to reach the conclusion. Kind of like this: http://youtu.be/tpKx7Oi0oeM<br />
<br />
Anyways, we have a logic programming interpreter called Scmlog. It interprets a simpler version of the logic programming language, Prolog. The first few things to understand about it is that '''SCMLOG IS NOT SCHEME'''. It just happens to look like it. Scmlog is its own language and so it has its own rules. You can't program in Scmlog like you would in Scheme or Python, so it might be good to forget what you know about those languages for a second.<br />
<br />
When you fire up scmlog (type scmlog into a terminal on the school computers), you get this prompt:<br />
<pre>star [501] ~ # scmlog<br />
scmlog (Prolog in Scheme), v. 0.2<br />
Type 'help' for help; 'quit' to exit.<br />
?- </pre><br />
You can interact with this prompt in two ways:<br />
# Give facts - "Let's tell the computer some things it should know!"<br />
# Ask questions - "Let's ask the computer questions about the things we told it about!"<br />
<br />
'''Giving Facts and Asking Questions'''<br />
A basic fact takes this form: <code>(fact (assertion))</code>, '''Where each assertion is simply a relation between things.'''<br />
<br />
For example: <code>(fact (likes potstickers brian))</code> relates three ideas, liking something, potstickers, and some guy name Brian. "Brian likes potstickers."<br />
Note that we put the relation first, and then the parties the relation acts upon. In this example, "likes" is the relation, it hooks up "brian" and "potstickers". Here's some more:<br />
<pre>?- (fact (likes potstickers brian)) <br />
?- (fact (likes potstickers andrew)) <br />
?- (fact (likes the_beatles brian)) <br />
?- (fact (likes the_beatles andrew)) <br />
?- (fact (likes led_zeppelin andrew)) <br />
?- (fact (dislikes led_zeppelin brian))</pre><br />
Now that we've given the computer a bunch of facts, how do we ask questions about them? Just replace "fact" with a "?", and replace any part of the relation (besides the relation itself) with a variable prefixed by an underscore. This is called "querying":<br />
<pre>?- (? (likes _what brian))<br />
_what : potstickers<br />
More? <br />
_what : the_beatles<br />
More? <br />
?- (? (likes potstickers _who))<br />
_who : brian<br />
More? <br />
_who : andrew<br />
More? <br />
?- (? (dislikes _what brian))<br />
_what : led_zeppelin<br />
More?<br />
?- (? (likes led_zeppelin andrew))<br />
Yes.<br />
?- (? (dislikes the_beatles _who))<br />
No.</pre><br />
Notice:<br />
* We can query any part of the assertion, besides the relation itself (can't replace "likes" with a variable).<br />
* All possible answers to the question show up.<br />
* If Scmlog couldn't find a fact that matched your query, it'll say "No."<br />
* Asking a question without any variables essentially asks if that fact exists. To which Scmlog will answer "Yes." or "No."<br />
<br />
'''More Complicated Facts and Questions'''<br />
Now this isn't the whole picture. We also have the ability to make more powerful assertions via variables, hypotheses, and conclusions:<br />
<br />
<code>(fact (ancestor _x _y) (parent _x _y))</code> "X is an ancestor of Y if X is a parent of Y"<br />
<br />
Here _x and _y are variables like usual. However, we have two parts to this fact, the conclusion ("X is an ancestor of Y") and the hypothesis ("X is a parent of Y"). We can have more than one hypotheses, and they can be any kind of query. Here's ancestor in action:<br />
<pre>?- (fact (parent george paul))<br />
?- (fact (parent martin george))<br />
?- (fact (parent martin martin_jr))<br />
?- (fact (parent martin donald))<br />
?- (fact (parent george ann))<br />
?- (fact (ancestor _X _Y) (parent _X _Y))<br />
?- (fact (ancestor _X _Y) (parent _X _Z) (ancestor _Z _Y))<br />
?- (? (ancestor paul george))<br />
No.<br />
?- (? (ancestor george paul))<br />
Yes.<br />
?- (? (ancestor george george))<br />
No.<br />
?- (? (ancestor martin paul))<br />
Yes.</pre><br />
Now there's one more thing you need to know about Scmlog. Scmlog knows about pairs and lists:<br />
<pre>?- (fact (lst (1 2 3)))<br />
?- (? (lst _x))<br />
_x : (1 2 3)<br />
More?<br />
?- (? (lst (1 . _x)))<br />
_x : (2 3)<br />
More? <br />
?- (? (lst (1 2 . _x)))<br />
_x : (3)<br />
More? <br />
?- (? (lst (_x . _y)))<br />
_x : 1<br />
_y : (2 3)<br />
?- (fact (my_pair (2 . 3)))<br />
?- (? (my_pair _y))<br />
_y : (2 . 3)<br />
?- (? (my_pair (2 . _x)))<br />
_x : 3</pre><br />
<br />
'''How to write Logic Programs'''<br />
<br />
The trick behind writing logic programs is to forget everything you know about programming. You instead want to focus on the relation you're trying to establish. Take append for example:<br />
<pre>?- (fact (append () _b _b))<br />
?- (fact (append (_x . _rest) _b (_x . _z)) (append _rest _b _z))<br />
?- (? (append (1 2 3) (3 2 1) _answer))<br />
_answer : (1 2 3 3 2 1)<br />
?- (append (1 2) 3 (1 2 3))<br />
Huh?<br />
?- (? (append (1 2) 3 (1 2 3))<br />
)<br />
No.<br />
?- (? (append (1 2) (3) (1 2 3))<br />
)<br />
Yes.<br />
?- (? (append (1 2) (3) (1 2 3)))<br />
Yes.<br />
?- (? (append _x (3 4 9) (1 0 3 2 3 4 9)))<br />
_x : (1 0 3 2)<br />
More? <br />
?- (? (append _x _y (2 1)))<br />
_x : ()<br />
_y : (2 1)<br />
More? <br />
_x : (2)<br />
_y : (1)<br />
More? <br />
_x : (2 1)<br />
_y : ()</pre><br />
<br />
What is append? Well the first fact establishes that if we append an empty list to something, the result is that something.<br />
The next fact is the meat of the code. You can almost think of this as a recursive relation. Any append relation between some three lists a b c must also fulfill another append relation between the rest of a, b, and the rest of c, respectively. Think about why that will always be true for all inputs. If the example above doesn't make sense to you, see the following:<br />
<br />
<br />
'''Common Pitfalls'''<br />
<br />
'''- One of the biggest issues people have with writing logic programs is that they don't realize that the variables don't work like they do in Scheme or Python. All Scmlog ever does is pattern match:'''<br />
<pre>?- (fact (x (0 1 8)))<br />
?- (fact (x (1 8 4)))<br />
?- (fact (x (9 4 4)))<br />
?- (fact (x (3 0 8)))<br />
?- (? (x _y))<br />
_y : (0 1 8)<br />
More? <br />
_y : (1 8 4)<br />
More? <br />
_y : (9 4 4)<br />
More? <br />
_y : (3 0 8)<br />
?- (? (x (0 1 . _z)))<br />
_z : (8)</pre><br />
<br />
- SCMLOG IS NOT SCHEME. SCMLOG IS NOT PYTHON.<br />
<br />
More Resources:<br />
* http://www-inst.eecs.berkeley.edu/~cs61a/sp12/lectures/prolog/ReadMe<br />
* http://www-inst.eecs.berkeley.edu/~cs61a/sp12/lectures/prolog/<br />
* http://www-inst.eecs.berkeley.edu/~cs61a/sp12/discussion/week14/<br />
* http://www-inst.eecs.berkeley.edu/~cs61a/fa13/slides/31-Logic_6pp.pdf<br />
<br />
=== Logic Mathematics ===<br />
[https://piazza.com/class/hoxc5uu6sud761?cid=3050 Source: Spring 2014 Piazza (3050)]<br />
<br />
'''Student Question'''<br />
<br />
Can someone explain the intuition behind the implementation of the increments and the addition facts? The following is from Mark's website<br />
<br />
<pre>(fact (increment 0 1))<br />
(fact (increment 1 2))<br />
(fact (increment 2 3))<br />
(fact (increment 3 4))<br />
(fact (increment 4 5))<br />
(fact (increment 5 6))<br />
(fact (increment 6 7))<br />
(fact (increment 7 8))<br />
(fact (increment 8 9))<br />
(fact (increment 9 10))<br />
(fact (increment 10 11))<br />
(fact (increment 11 12))<br />
(fact (increment 12 13))<br />
<br />
(fact (add 1 ?x ?x+1)<br />
(increment ?x ?x+1))<br />
<br />
(fact (add ?x+1 ?y ?z+1)<br />
(increment ?x ?x+1)<br />
(increment ?z ?z+1)<br />
(add ?x ?y ?z))<br />
<br />
(query (add 2 4 6))<br />
; expect Success!</pre><br />
<br />
'''Student Answer'''<br />
<br />
First, we state a bunch of facts that denote relations between a number and the number that follows it (e.g. 1 and 2, 2 and 3, and so on). This is the increment fact.<br />
<br />
Then, we state a "base" fact, which is:<br />
<pre>(fact (add 1 ?x ?x+1)<br />
(increment ?x ?x+1))</pre><br />
In English, this fact states that some value <code>?x</code> added to 1 will give us some value <code>?x+1</code> (<code>x+1</code> is a valid variable name in Logic) '''if and only if''' the fact (<code>increment ?x ?x+1</code>) is true. As an example, <code>(add 1 2 3)</code> is true '''if and only if''' (<code>increment 2 3</code>) is true. This is true because of the increment facts we stated before.<br />
<br />
Now, to deal with additions that aren't just the sum of two numbers in which one number is a 1, we need:<br />
<pre>(fact (add ?x+1 ?y ?z+1)<br />
(increment ?x ?x+1)<br />
(increment ?z ?z+1)<br />
(add ?x ?y ?z))</pre><br />
I will try to give an intuition as to what is happening here.<br />
<br />
We state our fact: that two numbers, <code>?x+1</code> and <code>?y</code>, will add up to some number <code>?z+1</code> if and only if the following 3 hypotheses are true:<br />
* (<code>increment ?x ?x+1</code>) - "There exists some number <code>?x</code> that is 1 less than <code>?x+1</code>, and"<br />
* (<code>increment ?z ?z+1</code>) - "There exists some number <code>?z</code> th