http://cs106b.stanford.edu/about_assessments
We know that you work hard on completing your programming assignments, and that work forms the primary mechanism for growing your coding practice skills and learning the theory concepts in CS106B. Growing and learning is a journey, and each weekly assigmnment has the possibility for setbacks and redemption. Exams allow you to demonstrate that after all those assignment ups and downs, you eventually reached a place of mastery at the level we expect for this course. This course has two exam assessments, one at mid-quarter and another at end-quarter. Mid-quarter Exam Midway through the quarter, students will complete a timed assessment assessing the core topics from the first half of CS106B. The midterm exam is scheduled for 7-9pm on Tuesday, November 1, 2022. End-quarter Exam The end-quarter final exam will have the same format as the midterm. It will cover content from the entirety of the quarter, with particular focus on material introduced in the second half of CS106B. The registrar has assigned our final exam slot for 8:30-11:30am on Monday, December 12, 2022. We will not offer any alternate exam times, please confirm you have no exam conflicts before enrolling in the course. Watch this space for more details about exam logistics and practice problems to be released as the date of the exam approaches.
http://cs106b.stanford.edu/about_assignments
Programming is a skill best learned by doing, and the programming assignments in CS106B form the central skill development part of your experience in the course. We have a great set of assignments planned that we hope you will find fun, challenging, illuminating, and rewarding. There are 7 assignments, about one each week with breaks around the two exams (see the course schedule for tentative assignment dates). Students self-report spending between 10 and 20 hours on each assignment. If you find yourself heading towards the upper end of that range for an assignment, please reach out to course staff for tips. Our workload is challenging because we want to foster the most growth possible for you in our 10 weeks together, but we do want the total hours to stay within reason for a 5-unit course and are happy to work with you towards that goal. In CS106B, we write programs in the C++ language and use the Qt Creator IDE for editing, compiling, and debugging. Please visit the Qt Installation Guide for install instructions. Common questions about assignments What is the policy on late assignments? Students are granted a penalty-free grace period for submission on each assignment, the length of which depends on the specific assignment. Read our course late policy for the details. What is the assignment collaboration policy? Since this is essentially a beginning writing course, but with code instead of essays, we have a policy that assignments are done individually (no partners/groups). In later CS courses, you will often encounter teamwork-based programming, and we believe strongly in the value of that-for programmers who have already developed some individual skills in the fundamentals. We adhere to the Stanford and CS department Honor Code policies. Please review our Honor Code policy for guidelines specific to this course (i.e., do not assume that what is ok in other classes is necessarily ok in this one). How can we get help on our assignments? Your main starting points for help are the online forum and the nightly virtual drop-in "Lair" help hours with the section leaders (undergraduate TAs). The online forum alows 24-hr access to discussion with your peers, and quick (though not 24-hr, we do sleep!) answers from course staff. The instructors will also hold weekly office hours where you are welcome to discuss assignments or other topics. How are assignments evaluated? Programs will be evaluated on "functionality" (is the program's behavior correct?) and "style" (is the code well written and elegant?). We apply a bucket grading scale for a wholistic qualitative evaluation rather than fixate on individual points. To learn from the grading feedback, focus your attention on the qualitative comments and the discussion with your SL in IGs. + A submission that is "perfect" or exceeds our standard expectation for the assignment. To receive this grade, a program reflects additional work beyond the requirements or gets the job done in a particularly elegant way. (letter grade A+) A submission that satisfies all the requirements for the assignment, showing solid functionality as well as good style. It reflects a job well done. (range A/A-) A submission that mostly meets the requirements, with some small issues or oversights. (letter grade range B+/B) A submission that is a good attempt at meeting the requirements, but falls short in some ways. (letter grade range B-/C+) - A submission that exhibits serious problems, but nonetheless shows some effort and accomplishment. (letter grade range C/C-) -- A submission that shows low effort or does not represent passing work. From past experience, we expect most grades to be and How do we receive feedback from our grader? You can view your submission on Paperless with comments and annotations from your grader along with the bucket scores for functionality and style. For some assignments, you will also meet with your section leader to discuss the grading feedback in a short IG ("interactive grading") conference.
http://cs106b.stanford.edu/about_lectures
Lecture MWF 1:30-2:20pm in Hewlett 200 For lecture slides, see the Schedule page. We have scheduled our lecture to meet in the largest room available on campus - we strongly encourage all of you to join us in-person! This will allow you to participate in class discussion and in-class activities, to ask your questions real-time, and ensures that you are keeping on pace with the course schedule. We think staying on pace with the class is so important (and we know you know it too-binge-watching videos just isn't it!) that we are offering a small grade incentive to help you motivate yourself to stay on top of it. More details below. Common questions about lectures How is lecture participation graded? We think staying on pace with the class is so important (and we know you know it too-binge-watching videos just isn't it!) that we are offering a small incentive to help you motivate yourself to stay on top of it: 5% of the course grade is allocated for lecture participation, on a can-only-help-you basis. If you show up to a lecture in person, congratulations, you earned that day's credit! If you aren't able to make it, simply watch the video on Canvas and answer a few questions on Gradescope so we know you're up to speed. Unlike practice questions we do in class, you do need to answer these correctly to earn that day's credit. But Gradescope will give you the chance to retry questions you miss. These make-up questions need to be completed before the start of the next lecture (i.e., Monday lecture questions submitted before the start of Wednesday's lecture) to earn credit. Your participation grade will be calculated as the number of lectures you earned credit for divided by 25 (we won't count the first two lectures of the quarter, or the last lecture). At the end of the quarter, we will calculate your total course grade including the 5% particpation, and also not including it (and putting that 5% on the final exam instead). Whichever formula ends up more favorable to you is the formula we will apply for you. So if you'd rather not attend lecture or watch videos, because you are confident in other methods of learning the material, you can do what works for you without it damaging your grade. Are lectures recorded? The fall quarter offering of CS106B will be recorded for our SCPD remote participants. Those video recordings are also available to regular on-campus for review or missed class. It usually takes about 2-3 hours after the end of class for the videos to post to Canvas, where they will appear under Panopto Course Videos. There is no live synchronous remote viewing. How can I ask questions during lecture? Raise your hand to ask it live. Since we have two co-instructors, you also can ask questions during lecture via our online forum in a special megathread that will be continuously monitored by one instructor while the other lectures.
http://cs106b.stanford.edu/about_section
Starting in Week 2, you'll meet in a weekly 60-minute small group discussion led by your section leader (an undergraduate TA). Your SL also serves as your mentor and grader in the course. The section materials for each week consist of a set of problems for further practice with recent lecture topics. We expect students to attend section and participate with a high level of engagement. Common questions about sections How do I sign up for section? Section signups are conducted on our CS198 section portal (do not sign-up for sections on Axess). Our section portal will open signups on Thursday, September 29 at 5:00 pm PDT and close on Sunday, October 2 at 5:00 pm PDT. Section sign-ups are not first-come first-serve, rather all students rank their preferences and after submissions close, the staff arrange a workable schedule for all. Your assigned section will then be emailed to you. Where can I find my assigned section? You can find your section time and meeting location on Paperless. How do I prepare for section each week? The most important preperation for section is that you are up to date on lecture viewing. You don't need to have understood everything in lecture perfectly, but your section leader won't be able to effectively guide students through the problems unless everyone is at least caught up on viewing all of the lectures preceding the section. Where can I find the section problems? The section problems will be posted on the course website under the "Sections" tab at the beginning of each week. After all sections for the week have met, the section page is updated with the solutions for later review. Is section participation mandatory? How is it graded? Active participation in section is required for all students. Choose a participation style that is comfortable for you, including asking questions, contributing answers, and participating in pair discussions with fellow students. Section participation is credited on this scale: 2 : Showed up to section on time, followed section norms, actively participated in an engaged manner 1 : Showed up to section late and/or had limited participation 0 : Did not show up to section, or did not follow established section norms, was unengaged Section participation contributes 5% to your final course grade. What should I do if I must miss a section meeting? You are welcome to attend an alternate section as a guest for a makeup. Visit the cs198 section portal for the full list of all section times. Tell the section leader whose section you attend that you're attending as a guest so they can pass the participation grade back to your section leader. You can also work the section problems on your own as self-review, but participation credit is specifically earned by attending and participating in a section. How do I become a section leader myself someday? You can apply during/after completing 106B. Come join us! Information about applying can be found on the CS198 Website.
http://cs106b.stanford.edu/about_staff
Cynthia Email Cynthia Office Hours MWF 2:20-2:50pm in Hewlett 200 F 3-4pm in Durand 321 Julie Email Julie Office Hours MWF 2:20-2:50pm in Hewlett 200 Th 10-11:30am in Durand 313 Neel Email Neel Office Hours MWF 2:20-2:50pm in Hewlett 200 T 10:30am-12pm in Durand 303 W 3-4pm in Durand 303 Instructor and TA office hours are open to all students, no sign up necessary, just drop in. Come ask your questions about concepts from lecture, request assignment help, and chat with us about Computer Science, advice, and life. Section leaders CS106B would not be possible without our wonderful team of undergraduate section leaders! In addition to leading small group sections, the SLs collectively run LaIR helper hours. LaIR is open for student questions and assignment help SMTuWTh evenings 7-11PM. More info about LaIR. Lauren Brandon Daniel Ecy Esteban Evan Jack Jayendra Jennifer Keely Lucia Naomi Patricia Robbie Ryan Tobey Victoria
http://cs106b.stanford.edu/citation
At the end of the quarter, we run analytic software over all CS106B submissions to find sections of student-submitted code that have been copied from elsewhere rather than independently written by the student. A staffer then reviews the situation to determine appropriate followup. With a correct understanding of the authorship, we can ensure that credit is being awarded properly. Accurate attribution of code authorship is an important tenet of the programming community and of academic integrity in general. The purpose of this notice is to provide students with the opportunity to retroactively add any citations missing from the original submission, to set the record straight before our analytic software is run. Permissible re-use The course policy on collaboration and the Honor Code allows students to exchange ideas with others, but not to share or copy code. If you are in office hours or discussing the assignment with other students and a few lines of code are jointly sketched, it is fine to learn from those lines and incorporate them into your assignment. Your assignment might also take inspiration from a code example in lecture, section, or the textbook. Both of these situations are within the spirit of exchanging ideas, learning from others, but writing the code yourself. This help is entirely permissible, but please be sure to cite any direct influence. The code you submit is still distinctly your own and you retain full authorship; the citation simply acknowledges the work of others that influenced your code. Unpermissible re-use In contrast, an honor code violation looks more like having access to solution code (such as found on the Internet or written by another current or past student) and basing your submission off of it, whether reusing an exact copy or borrowing code sections from it and remixing. Submitting this code without citation is a misrepresentation of authorship and is a breach of academic integrity -code written by another is being passed off your own. This is unacceptable, and the CS department has become adept at identifying instances of misrepresented authorship using sophisticated tools. Unpermitted collaboration/partnership There is yet a third category where two current students work together and both submit the same/similar code based on the joint work. Submitting such work as your own without acknowledging the contributions of another is also a misrepresentation of authorship. Past quarters have allowed partnership, but this quarter does not. If you have mistakenly engaged in a disallowed partnership, we offer you the option to provide a retroactive citation of that collaboration. This citation opens the path to a resolution where we work with you to establish a fair attribution of authorship, and adjust allocated credit accordingly. Making a retroactive citation If you are concerned that some of your work is missing a necessary citation, please consider reaching out to set things right: Email one of the instructors as soon as possible to make a retroactive citation. Please identify the affected code, the collaboration that occurred, and your interpretation of how this collaboration fell onto the spectrum of permissible -> not permissible. There are a number of possible outcomes in response: If the retroactive citation cites permissible help, your citation acknowledges that contribution and corrects the previous oversight. There is no further action to take. If the retroactive citation cites use of work of another as your own, we would not be able to award you credit for that work. We would discount/zero the score to reflect your diminished contribution. If the retroactive citation cites work done in collaboration with another student, we will follow up to understand the extent of the collaboration and adjust grading credit to reflect an appropriate division of work and authorship. In all of the above, if the retroactive citation corrects the previous misrepresentation of authorship, there is no longer an Honor Code violation and there is no followup with the judicial office. If you are not sure of how to proceed, please reach out to us. We very much appreciate those of you who want to do the right thing, and we will meet for a constructive, non-judgmental review of what has happened and what is the best way forward.
http://cs106b.stanford.edu/course_placement
Not sure of CS106B is right for you? Wondering if you should start with CS106A or CS107 instead? This is a collection of our usual advice to students who ask about selecting the course that's right for them. Of course, we are happy to provide further guidance, just reach out on the forum or in office hours. CS106A: Start here! CS106A is our first-quarter programming course. It teaches the widely-used Python programming language, with an emphasis on conceptual understanding of the fundamental building blocks of coding (in any language) and principles of good coding style. If you're interested in learning how to program a computer, this is the place to start. CS106A has no prerequisites - it's open to everyone! The course is designed to appeal to everyone from humanists and social scientists to aspiring hard-core techies. If you've had some experience with coding, it can be hard to decide if CS106A or CS106B is the right starting point for you. If you've taken AP CS Principles (but not AP CS A / Java), we recommend that you start with CS106A. If you completed AP CS A, then CS106B is most likely the best match for you, although some students who feel unsatisfied with their high school AP CS A experience do start with CS106A. We recommend that you take CS106A if You are interested in learning the first fundamentals of how to program computers. We recommend that you not take CS106A if You have prior programming experience at a level comparable to an introductory college course (for example, if you scored a 4 or 5 on the AP CS A / Java exam). You have significant prior programming experience and just want to learn Python as a second+ programming language. We sometimes hear that students are concerned that starting in CS106A means being too far "behind" their peers to successfully complete a CS major, but this is not true at all. Nearly half of the CS department's bachelor's degree graduates each June got their start in CS106A, so you'll be in good company! In the 2022-23 academic year, CS106A is offered every quarter. Visit the CS106A website. CS106B: Next step CS106B is our second course in computer programming. It focuses on techniques for solving more complex problems than those covered in CS106A and for analyzing program efficiency. Specifically, it explores fundamental data types and data structures, recursive problem solving, and basic algorithmic analysis. It's taught in C++, but the focus is on conceptual understanding of algorithms. If you'd like a focused study of C++ the language itself, consider taking CS106L. CS106B assumes you have programming experience at the level of CS106A, though you don't necessarily have to have taken our CS106A course. If you are experienced with basic control structures (conditions, loops), variables, arrays/lists, maps, and program decomposition, then you should be ready to take CS106B. If you've had some experience with coding, it can be hard to decide if CS106A or CS106B is the right starting point for you. If you've taken AP CS Principles (but not AP CS A / Java), we recommend that you start with CS106A. If you completed AP CS A, then CS106B is most likely the best match for you, although some students who feel unsatisfied with their high school AP CS A experience do start with CS106A. We recommend that you take CS106B if You have prior programming experience at the level of CS106A. You are interested in learning more about problem-solving with computers. You've programmed before but have not seen recursion, data structures, or algorithmic analysis. We recommend that you not take CS106B if You already have completed equivalent coursework elsewhere. You have taken AP CS Principles, but not AP CS A / Java. In the 2022-2023 academic year, CS106B is offered every quarter. Visit the CS106B website. Optional add-ons to CS106B We offer several courses that are designed to complement CS106B with additional material. None of these courses are required, and they do not count toward the CS major or CS minor requirements. However, if you're interested in going deeper for your own enrichment, you may find them worth checking out! Additional foundation support: CS100B CS100B is an optional 1-unit companion course to CS106B that provides extra support to students from under-resourced backgrounds. It meets for an additional weekly section where students receive access to additional mentoring, in depth content review, and other study resources. It is part of the Pathfinders/ACE program jointly sponsored by the CS department and the School of Engineering. Enrollment is by application, read more at https://engineering.stanford.edu/students-academics/equity-and-inclusion-initiatives/undergraduate-programs/additional-calculus We accept applications from all students who believe they may benefit from participating in small active-learning sessions led by a highly trained graduate student. C++ Language: CS106L CS106L is an optional 1-unit companion course to CS106B that focuses purely on the C++ programming language. Unlike CS106A and CS106B, which focus more on general programming skills and fundamental programming concepts, CS106L is specifically designed to focus on language features particular to C++ and how to use the C++ programming language to solve problems. Although CS106L is designed as a companion course to CS106B, it's open to anyone with a comparable background. We recommend that you take CS106L if You have prior programming experience at the level of CS106B (or are currently enrolled.) You are interested in learning more about the C++ programming language and the standard libraries. You are willing to put in more work than is necessary for CS106B. We recommend that you not take CS106L if You want a deeper understanding of topics like recursion, data structures, or big-O notation. You want to learn programming at the level of CS106B, but don't have the time to take those courses. In the 2022-2023 academic year, CS106L is offered in Autumn, Winter, and Spring quarters. Visit the CS106L website. More adventures: CS106M CS106M is an optional 1 unit add-on course to CS106B that explores supplemental material in a small discussion setting. For example, this year's offering will likely cover topics like data compression, error-correcting codes, and digital signatures. The topics covered in CS106M will not be required by later CS courses, even if you are planning to major in CS. We recommend that you take CS106M in addition to CS106B if: You are currently enrolled in CS106B. You are interested in exploring additional topics and deepening your study of the course material in a small discussion setting. You are willing to put in more work than is necessary for CS106B. We recommend that you not take CS106M if You are concerned that you "need" to take CS106M to avoid falling behind everyone else. In the 2022-2023 academic year, CS106M is offered only in Fall quarter. Social Good: CS106S CS106S is an optional 1 unit add-on course to CS106B that gives you a chance to work on programs for social good. The class brings in student groups, nonprofits, and local tech companies and is a mix of a speaker series and small project course. The course also teaches basic web development, but is not meant to be a stand-alone web development course. We recommend that you take CS106S in addition to CS106B if You are interested in exploring social good applications of computer science. You are willing to put in more work than is necessary for CS106B. In the 2022-2023 academic year, CS106S is offered in Autumn, Winter, and Spring quarters. Visit the CS106S website. Can I skip the intro courses altogether? Many students entering Stanford today have had considerable programming experience in high school or from their own independent work with computers. If you are in that position, the idea of starting with a beginning programming course-even an intensive one like CS106B-seems like a waste of time. Your perception may in fact be correct. In our experience, there are somewhere between 10 and 15 students in each entering class who should start at a more advanced point in the sequence. Below we talk about some of these more advanced classes (CS107 and CS107E). For most of you, however, the right place to start is with the CS106 series. Most high-school computing courses are somewhat weak and provide little background in modern software engineering techniques. By taking CS106, you will learn how the CS department at Stanford approaches programming and get a solid foundation for more advanced work. If you're unsure where you should start the programming sequence, please talk with us. CS107: How it all works After completing the intro programming sequence, CS107 takes you under the hood to learn the ins and outs of computer systems. It explores how high-level programming constructs are represented internally inside the computer and how those internal representations affect program behavior and performance. Along the way, it provides programming maturity and exposure to developing software in a Unix environment. CS107 has CS106B as a prerequisite and assumes an understanding of fundamental programming techniques and good programming style. As a result, it's rare for incoming students to jump directly into CS107 and to skip the CS106 series entirely. Typically, we'd only recommend this to students with a background comparable to CS106A/B and who already have good programming style. Most students, even those who go on to be CS majors, usually begin in the CS106 sequence. We recommend that you take CS107 if You have completed CS106B or have the equivalent programming background, including familiarity with recursion and fundamental data structures (binary trees, dynamic arrays, linked lists, graphs, etc.) You have experience writing readable code - writing comments, decomposing problems into smaller pieces, etc. We recommend that you not take CS107 if You have never before taken a class in computer programming. You have prior programming experience but have not met the postconditions of CS106B. Visit the CS107 website. CS107E: How it works, embedded CS107E is version of CS107 that covers similar topics but which focuses on programming a small computer that can easily fit into the palm of your hand. The class is smaller and more project-oriented than CS107 and lets you play around with small embedded devices to see how low-level systems concepts directly let you control physical devices. The CS107E FAQ offers perspective on advice on choosing between 107 and 107E. We recommend that you take CS107E if You meet all the requirements for CS107. You enjoy working on open-ended projects. We recommend that you not take CS107E if You're nervous about taking CS107 and want to satisfy that requirement in a different way. Visit the CS107E website.
http://cs106b.stanford.edu/assignments/2-adt/ethics.html
In Week 2 of lecture, you learned about using the techniques of benchmarking (i.e., measuring run time in our test framework) and counting the number of "writes" to memory, to determine performance characteristics of different algorithms using a Vector. Next week, we will expand and formalize our accounting of the number of steps in an algorithm using a technique called Big-O analysis, which will inform our study of algorithms throughout the rest of the course. However, we want to approach the topic of Big-O with a certain amount of cautious skepticism in terms of its ethical implications. This assignment is designed to prepare you to learn about Big-O by pausing to consider these implications. (Note: although usually we try to have assignments only cover lecture topics up to the time they go out, in this case you may want to wait to watch the end of Week 3 Monday's lecture to hear more about this topic before completing this section.) Q9. In a short paragraph, describe a real or plausible scenario not previously presented in lecture in which using techniques like benchmarking or counting "write" steps to improve the performance of an algorithm might benefit Earth's environment. Include your thoughts on how a software engineer working on this piece of code might identify such potential benefits and take them into consideration when designing the code. As ethical and socially-conscious computer programmers, we also know that many considerations other than the speed and runtime are important in choosing the appropriate algorithm for a particular use. Dr. Gillian Smith, an associate professor at the Worcester Polytechnic Institute, identifies an interesting fallacy that computer scientists often fall into when applying algorithmic analysis techniques like benchmarking and Big-O analysis: If it's more efficient, it's better. The quality of a program, independent of how people interact with it, should be evaluated only with respect to how well it minimizes cost. The following case study illustrates the importance of supplementing efficiency and performance analyses with human-centric evaluation. In 2006 the state of Indiana awarded IBM a contract for more than $1 billion to modernize Indiana's welfare case management system and manage and process the State's applications for food stamps, Medicaid and other welfare benefits for its residents. The program sought to increase efficiency and reduce fraud by moving to an automated case management process. After only 19 months into the relationship, while still in the transition period, it became clear to Indiana that the relationship was not going as planned. In particular here are some "lowlights" of the system's failures to provide important and necessary services for those in need: "Applicants waited 20 or 30 minutes on hold, only to be denied benefits for 'failure to cooperate in establishing eligibility' if they were unable to receive a callback after having burned through their limited cellphone minutes." "Applicants faxed millions of pages of photocopied driver's licenses, Social Security cards, and other supporting documents to a processing center in Marion, Indiana; so many of the documents disappeared that advocates started calling it "the black hole in Marion" [...] Any application missing just one of tens to hundreds of pieces of necessary information or paperwork were automatically denied." "By February 2008, the number of households receiving food stamps in Delaware County, which includes Muncie, Indiana, dropped more than 7 percent, though requests for food assistance had climbed 4 percent in Indiana overall." (Quotations from this article written by Virginia Eubanks.) In light of these failures, the State of Indiana cancelled its contract with IBM and sued the company for breach of contract, stating that the company had failed to deliver a system that was supposed to help people get the services they needed. In court, IBM argued that they were not responsible for issues related to wait times, appeals, wrongful denials, lost documents, etc. as the contract only stated that a successful system would succeed by reducing costs and fraud. IBM's system did reduce costs, but did so by denying people the benefits they needed. In light of this, we would like you to consider the following questions: Q10. According to the contract that IBM struck with the state of Indiana, the criteria for optimization were improving efficiency of the overall welfare system and reducing fraud. Criteria for reducing wait times and wrongful denials were not included. However, wrongfully denying benefits has a huge negative impact on the citizens who rely on the system. If criteria like minimizing wrongful denials were not included in the contract, should engineers have included them in their optimization algorithm? Why or why not? Q11. Imagine that after completing CS106B you are hired at IBM as an engineer working on this system. How might you have approached designing and setting the goals of this system? How might you apply algorithmic analysis tools to build a system that achieved the desired goals? Could you do so in a way that avoids the severe negative impacts on users of the system that are outlined in the case study? If you're interested in reading more about this case study, we highly recommend reading the linked article above. The author, Virginia Eubanks, also wrote a great book titled Automating Inequality that can make for interesting further exploration!
http://cs106b.stanford.edu/faq
Are you a student looking for information about how the course will be structured in Fall Quarter 2022? You're in the right place! Are lectures recorded? Is attendance required? Lectures are MWF 1:30-2:20pm in Hewlett 200. We encourage you to join us in person, but recordings will also be posted to Canvas for later asynchronous viewing. Read more about lectures. How will sections work? Is attendance required? Each of you will be assigned to a small group section that meets weekly. Section participation is expected and contributes to your course grade. Note the section times listed in Axess are not accurate. Section times will be arranged in the first week of the quarter. Read more about section. Will there be exams? Yes, at mid-quarter and end-quarter. Read more about assessments. I am currently unable to enroll on Axess. How can I shop the course in the meantime? Canvas has recently enabled course shopping , and we configured our Canvas site to be "open for shopping"; this allows you to access videos without enrolling. Note that you must be formally enrolled on Axess to submit work for grading. Do you allow auditors? Auditors are welcome to attend lecture, access website materials, and work on section and assignments on their own without submitting. We will not be able to grade work from auditors, nor can auditors be accommodated in sections, helper hours, or the online forum. How do I choose which of the intro courses is right for me? Most incoming students find the right place by starting in either CS106A or CS106B, or, more rarely, in CS107. You may also want to consider adding on one of the CS106B companion courses, CS100B, CS106L, and CS106M. Read more about the options in our guide to course placement. Still have unanswered questions? Hop on over to the Ed discussion forum and ask! We're happy to help.
http://cs106b.stanford.edu/index.html
CS106B Programming Abstractions Fall Quarter 2022 Lecture MWF 1:30-2:20pm in Hewlett 200 Announcements What's happening this week Last updated 1 week ago by Neel Lectures Date Lecture Monday, October 3rd Vectors and Grids Wednesday, October 5th Ordered Data Structures (Stacks and Queues) Friday, October 7th Section This week in section you'll be meeting your section-mates and section leader, and practicing with the C++ fundamentals we're learning! Assignment Assignment 1 is out now and due back on Friday, October 7th at 11:59PM! You'll sharpen your C++ skills: strings, variables, and testing galore. Enjoy! Quick Links ed Discussion Forum Office Hours LaIR Signup Canvas videos Paperless Honor Code Blank Qt Project
http://cs106b.stanford.edu/honor_code
Since 1921, academic conduct for students at Stanford has been governed by the Honor Code, which reads as follows: THE STANFORD UNIVERSITY HONOR CODE The Honor Code is an undertaking of the students, individually and collectively: that they will not give or receive aid in examinations; that they will not give or receive unpermitted aid in class work, in the preparation of reports, or in any other work that is to be used by the instructor as the basis of grading; that they will do their share and take an active part in seeing to it that others as well as themselves uphold the spirit and letter of the Honor Code. The faculty on its part manifests its confidence in the honor of its students by refraining from proctoring examinations and from taking unusual and unreasonable precautions to prevent the forms of dishonesty mentioned above. The faculty will also avoid, as far as practicable, academic procedures that create temptations to violate the Honor Code. While the faculty alone has the right and obligation to set academic requirements, the students and faculty will work together to establish optimal conditions for honorable academic work. The purpose of this handout is to make our expectations as clear as possible regarding the Honor Code. The basic principle under which we operate is that each of you is expected to submit your own work in this course. In particular, attempting to take credit for someone else's work by turning it in as your own constitutes plagiarism, which is a serious violation of basic academic standards. Under the Honor Code you are obligated to follow all of the following rules in this course: Rule 1: You must not look at assignment solutions that are not your own. It is an act of plagiarism to take work that is copied or derived from the work of others and submit it as your own. For example, using a solution from the Internet, a solution from another student (past or present), a solution taken from an answer set released in past quarters, or some other source, in part or in whole, that is not your own work is a violation of the Honor Code. Many Honor Code infractions we see make use of past solution sets. The best way to steer clear of this possibility is simply to not search for solutions to the assignments. Moreover, looking at someone else's solution in order to determine how to solve the problem yourself is also an infraction of the Honor Code. In essence, you should not be looking at someone else's answers in order to solve the problems in this class. This is not an appropriate way to "check your work," "get a hint," or "see alternative approaches." Additionally, you must not solicit solutions from anyone, animate or inanimate. For example, it is a violation of the Stanford Honor Code to ask another student to share their answers with you, to ask a tutor to share other students' solutions with you, or to ask for solutions on sites like Stack Overflow or Chegg. It is also a violation to use a tool to generate solution code for you. Rule 2: You must not share your solutions with other students. In particular, you should not ask anyone to give you a copy of their answers or, conversely, give your answers to another student who asks you for it. Similarly, you should not discuss your solution strategies to such an extent that you and your collaborators end up turning in the same answers. Moreover, you are expected to take reasonable measures to maintain the privacy of your solutions. For example, you should not leave copies of your work on public computers nor post your solutions on a public website. Rule 3: You must properly cite any assistance you received. If you received aid while producing your solution, you must mention who you got help from (if that person is not a TA or the instructor) and what specifically he/she helped you with. A proper citation should specifically identify the source (e.g., person's name, book title, website URL, etc.) and a clear indication of how this assistance influenced your work. For example, you might write "Student X mentioned the idea of having the base case be Y and the recursive step work in way Z." If you make use of such assistance without giving proper credit - or, if you provide a misleading or inaccurate statement describing the help you received - you may be guilty of plagiarism. It is also important to make sure that the assistance you receive consists of general advice that does not cross the boundary into having someone else write the actual solutions or show you their solutions. It is fine to discuss ideas and strategies, but you should be careful to write your solutions on your own, as indicated in Rules 1 and 2. If you are working with a tutor, be sure they/you are following the rules for the Honor Code as it applies to tutoring. Rule 4: You may only reuse past work in certain, limited situations. We tend to reuse assignments from quarter to quarter. Following the general principle that the names affixed to a submission should accurately represent its authorship, you may only resubmit work from prior quarters provided that the exact same set of people who initially turned in the assignment resubmit. This means, in particular, that if you completed an assignment individually in a previous quarter, you may only resubmit that assignment if you do so individually; and if you completed an assignment with a partner in a previous quarter, you may only resubmit that assignment if you submit with that exact same partner. To elaborate on that last point, if you worked with a partner in a previous quarter, you are retaking the course or resolving an incomplete, and your partner is not also retaking the class or resolving an incomplete, you may not resubmit the past work you did on that assignment in any circumstance. The policies above apply equally to reading, copying, or adapting solutions you submitted in previous quarters. For example, if you submitted an assignment individually in a previous quarter, you should not refer to your submission on that assignment if you are planning on redoing the assignment in a pair. Similarly, if in a previous quarter you worked with a partner who is not retaking the class, you must not reread or copy anything from that previous submission in the course of redoing the assignment. Note: all submissions are subject to automated plagiarism detection. The Stanford CS department employs powerful automated plagiarism detection tools that compare assignment submissions with other submissions from current and previous quarters, including intermediate versions. The tools also compare submissions against a wide variety of online solutions. These tools are effective at detecting unusual resemblances in programs, which are then further examined by the course staff. The staff then make the determination as to whether submissions are deemed to be potential infractions of the Honor Code and referred to Stanford's Community Standards office.
http://cs106b.stanford.edu/lectures/01-welcome/
Introduction to CS106B Readings Readings: Syllabus Links Video on canvas Code and Slides 01-Syllabus.pdf HelloCode.zip
http://cs106b.stanford.edu/section/section1/
This week's section exercises explore the very fundamentals of programming in C++. We'll be exploring the material from Week 1 and the beginning of Week 2 (functions, parameters, return, decomposition, strings and basic data structures). Have fun! Each week, we will also be releasing a Qt Creator project containing starter code and testing infrastructure for that week's section problems. When a problem name is followed by the name of a .cpp file, that means you can practice writing the code for that problem in the named file of the Qt Creator project. Here is the zip of the section starter code: Starter code Note: Maps will be covered in lecture on Friday, Oct. 7. For those that have section Wednesday or Thursday, these problems can be useful practice to cement your understanding after lecture! 1) Returning and Printing Topics: Function call and return, return types Below is a series of four printLyrics_v# functions, each of which has a blank where the return type should be. For each function, determine what the return type of the function should be, what value, if any, is returned, and what output, if any, will be produced if that function is called. Is it appropriate for each of these functions to be named printLyrics? Why or why not? _____ printLyrics_v1() { cout << "Havana ooh na na" << endl; } _____ printLyrics_v2() { return "Havana ooh na na"; } _____ printLyrics_v3() { return "H"; } _____ printLyrics_v4() { return 'H'; } Solution void printLyrics_v1() { cout << "Havana ooh na na" << endl; } string printLyrics_v2() { return "Havana ooh na na"; } string printLyrics_v3() { return "H"; } char printLyrics_v4() { return 'H'; } Of these four functions, only printLyrics_v1 will print anything. Specifically, it prints out the string "Havana ooh na na.". The name "printLyrics" is inappropriate for the other functions, as those functions don't actually print anything. The function printLyrics_v1 doesn't return anything - it just sends information to the console. As a result, its return type should be void. The functions printLyrics_v2 and printLyrics_v3 each return strings, since C++ treats anything in double-quotes as a string. Finally, printLyrics_v4 returns a char, since C++ treats anything in single-quotes as a character. 2) References Available Upon Request Topic: Reference parameters, range-based for loops Reference parameters are an important part of C++ programming, but can take some getting used to if you're not familiar with them. Trace through the following code. What does it print? void printVector(const Vector<int>& values) { for (int elem: values) { cout << elem << " "; } cout << endl; } void maui(Vector<int> values) { for (int i = 0; i < values.size(); i++) { values[i] = 1258 * values[i] * (values[2] - values[0]); } } void moana(Vector<int>& values) { for (int elem: values) { elem *= 137; } } void heihei(Vector<int>& values) { for (int& elem: values) { elem++; } } Vector<int> teFiti(const Vector<int>& values) { Vector<int> result; for (int elem: values) { result += (elem * 137); } return result; } int main() { Vector<int> values = { 1, 3, 7 }; maui(values); printVector(values); moana(values); printVector(values); heihei(values); printVector(values); teFiti(values); printVector(values); return 0; } Solution Here's the output from the program: 1 3 7 1 3 7 2 4 8 2 4 8 Here's a breakdown of where this comes from: The maui function takes its argument by value, so it's making changes to a copy of the original vector, not the vector itself. That means that the values are unchanged back in main. The moana function uses a range-based for loop to access the elements of the vector. This makes a copy of each element of the vector, so the changes made in the loop only change the temporary copy and not the elements of the vector. That makes that the values are unchanged back in main. heihei, on the other hand, uses int& as its type for the range-based for loop, so in a sense it's really iterating over the elements of the underlying vector. Therefore, its changes stick. The teFiti function creates and returns a new vector with a bunch of updated values, but the return value isn't captured back in main. 3) SumNumbers (sum.cpp) Topics: Vectors, strings, file reading The function sumNumbers reads a text file and sums the numbers found within the text. Here are some library functions that will be useful for this task: readEntireFile, to read all lines from a file stream into a Vector stringSplit, to divide a string into tokens isdigit, to determine whether char is a digit stringToInteger, to convert a string of digits to integer value In particular you will be asked to write the following function int sumNumbers(string filename) When given the following file, named numbers.txt, as input, your function should return 42. 42 is the Answer to the Ultimate Question of Life, the Universe, and Everything This is a negative number: -9 Welcome to CS106B! I want to own 9 cats. Solution bool isNumber(string s) { // strip negative sign off negative numbers if (s.length() > 0 && s[0] == '-') { s = s.substr(1); } for (char ch : s) { if (!isdigit(ch)) return false; } return s.length() > 0; } int sumNumbers(string filepath) { ifstream in; Vector<string> lines; int sum = 0; if (!openFile(in, filepath)) { return 0; } readEntireFile(in, lines); for (string line : lines) { Vector<string> tokens = stringSplit(line, " "); for (string t : tokens) { if (isNumber(t)) { sum += stringToInteger(t); } } } return sum; } 4) Debugging Deduplicating (deduplicate.cpp) Topics: Vector, strings, debugging Consider the following incorrect C++ function, which accepts as input a Vector<string> and tries to modify it by removing adjacent duplicate elements: void deduplicate(Vector<string> vec) { for (int i = 0; i < vec.size(); i++) { if (vec[i] == vec[i + 1]) { vec.remove(i); } } } The intent behind this function is that we could do something like this: Vector<string> hiddenFigures = { "Katherine Johnson", "Katherine Johnson", "Katherine Johnson", "Mary Jackson", "Dorothy Vaughan", "Dorothy Vaughan" }; deduplicate(hiddenFigures); // hiddenFigures = ["Katherine Johnson", "Mary Jackson", "Dorothy Vaughan"] The problem is that the above implementation of deduplicate does not work correctly. In particular, it contains three bugs. First, find these bugs by writing test cases that pinpoint potentially erroneous situations in which the provided code might fail, then explain what the problems are, and finally fix those errors in code. Solution There are three errors here: Calling .remove() on the Vector while iterating over it doesn't work particularly nicely. Specifically, if you remove the element at index i and then increment i in the for loop, you'll skip over the element that shifted into the position you were previously in. There's an off-by-one error here: when i = vec.size() - 1, the indexing vec[i + 1] reads off the end of the Vector. The Vector is passed in by value, not by reference, so none of the changes made to it will persist to the caller. Here's a corrected version of the code: void deduplicate(Vector<string>& vec) { for (int i = 0; i < vec.size() - 1; ) { if (vec[i] == vec[i + 1]) { vec.remove(i); } else { i++; } } } [Credit to Andrew Tierno for the alternate solution] Alternatively, you can also re-write the function to use a loop that traverses the vector from right-to-left, which is a common pattern when working with deleting items from linear collections. A solution that does so could look like this: void deduplicate(Vector<string>& vec) { for (int i = vec.size() - 1; i > 0; i--) { if (vec[i] == vec[i - 1]) { vec.remove(i); } } } 5) Pig-Latin (piglatin.cpp) Topics: Strings, reference parameters, return types Write two functions, pigLatinReturn and pigLatinReference, that accept a string and convert said string into its pig-Latin form. To convert a string into pig-Latin, you must follow these steps: Split the input string into 2 strings: a string of characters BEFORE the first vowel, and a string of characters AFTER (and including) the first vowel. Append the first string (letters before the first vowel) to the second string. Append the string "ay" to the resulting string. Here are a few examples... nick -> icknay chase -> asechay chris -> ischray You will need to write this routine in two ways: once as a function that returns the pig-Latin string to the caller, and once as a function that modifies the supplied parameter string and uses it to store the resulting pig-Latin string. These will be done in pigLatinReturn and pigLatinReference, respectively. You may assume that your input is always a one-word, all lowercase string with at least one vowel. Here's a code example of how these functions differ... string name = "julie"; string str1 = pigLatinReturn(name); cout << str1 << endl; // prints "uliejay" pigLatinReference(name); cout << name << endl; // prints "uliejay" Once you've written these functions, discuss with your section the benefits and drawbacks of these two approaches. Which do you feel is easier to write? Which do you think is more convenient for the caller? Do you think one is better style than the other? Solution // Use const because VOWELS won't change -- no need to declare repeatedly // in isVowel. const string VOWELS = "aeiouy"; // Helper function, which I'd highly recommend writing! bool isVowel(char ch) { // A little kludgy, but the handout guarantees that // ch will ALWAYS be lower case :) // NOTE: For an assignment, you probably want a more robust isVowel. return VOWELS.find(ch) != string::npos; } string pigLatinReturn(string input) { int strOneIndex = 0; for (int i = 0; i < input.length(); i++) { if (isVowel(input[i])) { strOneIndex = i; break; } } string strOne = input.substr(0, strOneIndex); string strTwo = input.substr(strOneIndex); return strTwo + strOne + "ay"; } void pigLatinReference(string &input) { int strOneIndex = 0; for (int i = 0; i < input.length(); i++) { if (isVowel(input[i])) { strOneIndex = i; break; } } string strOne = input.substr(0, strOneIndex); string strTwo = input.substr(strOneIndex); input = strTwo + strOne + "ay"; } Notice how similar these two approaches are - the only difference is how the result is handled at the very end. To address the discussion questions, although the pigLatinReference function is marginally more efficient because it doesn't need to make a copy of the input string, pigLatinReturn is probably more intuitive for both the caller and the writer: if the function's job is to somehow output some product, returning is the most explicit way to do so. In that way, a function that returns is also better style - it's makes the purpose of the function clearer to the reader. If you wanted to combine the efficiency of pigLatinReference with the clarity of pigLatinReturn, I would recommend writing a function that takes in the input string by const reference, basically string pigLatin(const string &input); Although the const isn't explicitly necessary, it's nice to have because you never need to modify input. Moreover, you still get the efficiency gains from pass-by-reference while also writing very-understandable code. 6) Mirror (mirror.cpp) Topic: Grids Write a function that accepts a reference to a of integers as a parameter and flips the grid along its diagonal. You may assume the grid is square; in other words, that it has the same number of rows as columns. For example, the grid below that comes first would be altered to give it the new grid state shown afterwards: Original state: { { 6, 1, 9, 4}, {-2, 5, 8, 12}, {14, 39, -6, 18}, {21, 55, 73, -3} } Mirrored state: { {6, -2, 14, 21}, {1, 5, 39, 55}, {9, 8, -6, 73}, {4, 12, 18, -3} } Bonus: How would you solve this problem if the grid were not square? Solution // solution void mirror(Grid<int>& grid) { for (int r = 0;r < grid.numRows(); r++) { // start at r+1 rather than 0 to avoid double-swapping for (int c = r + 1; c < grid.numCols(); c++) { int temp = grid[r][c]; grid[r][c] = grid[c][r]; grid[c][r] = temp; } } } // bonus void mirror(Grid<int>& grid) { Grid<int> result(grid.numCols(), grid.numRows()); for (int r = 0; r < grid.numRows(); r++) { for (int c = 0; c < grid.numCols(); c++) { result[r][c] = grid[c][r]; } } grid = result; } 7) Check Balance (balance.cpp) Topic: Stacks Write a function named checkBalance that accepts a string of source code and uses a Stack to check whether the braces/parentheses are balanced. Every ( or { must be closed by a } or ) in the opposite order. Return the index at which an imbalance occurs, or -1 if the string is balanced. If any ( or { are never closed, return the string's length. Here are some example calls: // index 0123456789012345678901234567 checkBalance("if (a(4) > 9) { foo(a(2)); }") // returns -1 (balanced) // index 01234567890123456789012345678901 checkBalance("for (i=0;i<a;(3};i++) { foo{); )") // returns 15 because } is out of order // index 0123456789012345678901234 checkBalance("while (true) foo(); }{ ()") // returns 20 because } doesn't match any { // index 01234567 checkBalance("if (x) {") // returns 8 because { is never closed Solution int checkBalance(string code) { Stack<char> parens; for (int i = 0; i < (int) code.length(); i++) { char c = code[i]; if (c == '(' || c == '{') { parens.push(c); } else if (c == ')' || c == '}') { if (parens.isEmpty()) { return i; } char top = parens.pop(); if ((top == '(' && c != ')') || (top == '{' && c != '}')) { return i; } } } if (parens.isEmpty()) { return -1; // balanced } else { return code.length(); } } 8) Collection Mystery Topics: Stacks and Queues void collectionMystery(Stack<int>& s) { Queue<int> q; Stack<int> s2; while (!s.isEmpty()) { if (s.peek() % 2 == 0) { q.enqueue(s.pop()); } else { s2.push(s.pop()); } } while (!q.isEmpty()) { s.push(q.dequeue()); } while(!s2.isEmpty()) { s.push(s2.pop()); } cout<< s << endl; } Write the output produced by the above function when passed each of the following stacks. Note that stacks and queues are written in to back order, with the oldest element on the left side of the queue/stack. Stacks: {1, 2, 3, 4, 5, 6} ________________________________________ {42, 3, 12, 15, 9, 71, 88} ________________________________________ {65, 30, 10, 20, 45, 55, 6, 1} ________________________________________ Solution {6, 4, 2, 1, 3, 5} {88, 12, 42, 3, 15, 9, 7} {6, 20, 10, 30, 65, 45, 55, 1} 9) Friend List (friendlist.cpp) Topic: Maps Write a function named friendList that takes in a file name and reads friend relationships from a file and writes them to a Map. friendList should return the populated Map. Friendships are bi-directional, so if Abby is friends with Barney, Barney is friends with Abby. The file contains one friend relationship per line, with names separated by a single space. You do not have to worry about malformed entries. If an input file named buddies.txt looked like this: Barney Abby Abby Clyde Then the call of friendList("buddies.txt") should return a resulting map that looks like this: {"Abby":{"Barney", "Clyde"}, "Barney":{"Abby"}, "Clyde":{"Abby"}} Here is the function prototype you should implement: Map<string, Vector<string> > friendList(String filename) Solution Map<string, Vector<string> > friendList(string filename) { ifstream in; Vector<string> lines; if (openFile(in, filepath)) { readEntireFile(in, lines); } Map<string, Vector<string> > friends; for (string line: lines) { Vector<string> people = stringSplit(line, " "); string s1 = people[0]; string s2 = people[1]; friends[s1] += s2; friends[s2] += s1; } return friends; } 10) Twice (twice.cpp) Topic: Sets Write a function named twice that takes a vector of integers and returns a set containing all the numbers in the vector that appear exactly twice. Example: passing {1, 3, 1, 4, 3, 7, -2, 0, 7, -2, -2, 1} returns {3, 7}. Bonus: do the same thing, but you are not allowed to declare any kind of data structure other than sets. Solution // solution Set<int> twice(Vector<int>& v) { Map<int, int> counts; for (int i : v) { counts[i]++; } Set<int> twice; for (int i : counts) { if (counts[i] == 2) { twice += i; } } return twice; } // bonus Set<int> twice(Vector<int>& v) { Set<int> once; Set<int> twice; Set<int> more; for (int i : v) { if (once.contains(i)) { once.remove(i); twice.add(i); } else if (twice.contains(i)) { twice.remove(i); more.add(i); } else if (!more.contains(i)) { once.add(i); } } return twice; }
http://cs106b.stanford.edu/assignments/0-namehash/
Due Friday, September 30 at 11:59 pm Pacific Assignment 0 is a short exercise that introduces you to the tools and policies for this course. The due date is Friday to get you ready to jump on the first programming assignment when it is released. The grace period on Assignment 0 allows late work up to 48 hours after the deadline. Read more about our course policy for accepting late work. Welcome to CS106B! In this assignment, you will first install the Qt tools and CS106-specific package and then work through compiling, running, and debugging a sample program. This confirms you and your development environment are ready for the awesome adventures to come this quarter! Step 1) Install Qt Creator You must first install Qt Creator, the development environment that we use in CS106B. Follow the step-by-step instructions in our Qt Installation Guide. If you run into a snag when installing Qt, reach out to us for help! We will hold a Qt Creator install help session 7-9pm Thursday, September 29 in Durand 353. You can also post your install questions on the Ed forum or bring them to office hours. Step 2) Download starter project For each assignment, we put together a folder of the starter project and post it as a ZIP archive. Here is the archive for this assignment: Starter code Download the archive and extract all. The starter project consists of two files: NameHash.pro (the Qt project file) and one C++ source file NameHash.cpp. Open the project file in Qt Creator. The first time you open a new project, Qt Creator will ask you to configure it; select the default kit (Qt 6.x) from your Qt install. Step 3) Hash your name In the Qt Creator window, click the green triangle in the lower-left to build and run the program. When the program runs, the console window appears and will prompt you to enter your preferred first and last names. The program then computes a hash code from your name. The hash code is like a "fingerprint", unique to your name and unlikely to be the same as anyone else's. Write your hash code down; you'll need it to complete the assignment! While you are here, skim the code for a preview of C++ syntax and control flow. Don't worry if you don't fully understand the math behind the algorithm just yet, consider it a teaser for ideas we will explore together later in the quarter. Step 4) Practice with debugger Knowing your debugger is a key component in your programming tool belt. Our colleague Keith Schwarz wrote a wonderful tutorial to introduce students to the Qt debugger. The tutorial guides you through using the debugger to inspect the NameHash program. Open the debugger tutorial and follow along step-by-step. At some point, you'll be asked to remember a special value. Write this special value down; you'll need it when you submit. Step 5) Read course policies (Syllabus and Honor Code) Please read the pages on the course website that detail our policies for the syllabus and Honor Code. We want to ensure that you know what to expect from us and what we will expect from you. If you have any questions or concerns about the course policies, make a post on Ed or via private email to clarify or resolve issues before choosing to enroll. Step 6) Submit Once you've finished everything, fill out this form while logged into your Stanford Google account: Submit Google form: https://forms.gle/tPrqNie4EgxS8htM7 Enter the numbers from Steps 3 and 4, confirm that you understand the course policies, and submit the form. Congrats and welcome to CS106B!
http://cs106b.stanford.edu/assignments/2-adt/easter_egg/
Here is a hidden file on the course website, buried deep among the many other pages containing important course information. For your entertainment, it also contains an easter egg, which can only be found by using the "Search" feature in the top left corner of the website... Your quote for today: Have a GREAT day! Presenting... Bad Trombone Guy Your browser does not support the audio element. Latest release from indie band College Avenue Lockdown (mixed by sons Rein & Kalev, husband Matt on trombone, Rein on drum kit, vocals by Julie) A classic XKCD comic that we can all relate to:
http://cs106b.stanford.edu/assignments/2-adt/
Due Friday, October 14 at 11:59 pm Submissions received by the due date receive a small on-time bonus. Late work after the due date is accepted if within the grace period (48 hours for this assignment). The grace period gives you the ability to self-grant an extension; we trust you will make reasonable and sparing use of this power. The grace period expires Sun, Oct 16 at 11:59 pm, after which we do not accept further late work. All due dates and submission times are expressed in Pacific time. The recent lectures have introduced you to some of the classic Abstract Data Types (ADTs), and now it's time to put that knowledge to use. In this assignment, you will write code that leverages those ADTs to implement some nifty algorithms. The tasks may sound a little daunting at first, but given the powerful tools in your arsenal, each requires a very manageable amount of code. Let's hear it for abstraction! This assignment is to be completed individually. Working in pairs/groups is not permitted. Learning goals To more fully experience the joy of using pre-written classes. Most of the heavy-lifting is handled by the collection ADTs. To stress the notion of abstraction as a mechanism for managing data and providing functionality without revealing the representational details. To learn how to model and solve problems using classic data structures such as vectors, grids, stacks, queues, sets, and maps. Assignment parts This assignment consists of a short warmup exercise using the debugger and two coding tasks that use of variety of ADTs. Finally, you'll finish off by answering a couple of embedded ethics questions. Warmup Practice with testing and debugging on different abstract data types. Do the warmup first! Maze A Grid of walls and corridors is used to represent a maze, and the Vector, Stack, Queue, and Set ADTs are used in a clever algorithm to find a solution path that escapes the maze. Search Engine A Map is used to associate words with a Set of documents containing that word. Using the map, you can find matching entries that contain terms from simple or compound queries, and construct a mini search engine. Beyond Algorithmic Analysis In this section, you will explore beyond traditional Big-O analysis to study some of the potential human and societal impacts of designing and optimizing efficient software systems. The two coding tasks are roughly comparable to each other in size and scope, so pace yourself to complete each in about three days. Note: The code you will write for Assignment 2 is considerably more complex than Assignment 1, so be sure to get an early start! Getting started We provide a ZIP of the starter project. Download the zip, extract the files, and double-click the .pro file to open the project in Qt Creator. Starter code The two source files you will edit are maze.cpp and search.cpp. Additionally, you will answer the questions in short_answer.txt. Resources The CS106B Guide to Testing The CS106B Style Guide Resolving Common Build/Run Errors, compiled by section leader Jillian Tang. Stanford library documentation for Vector, Grid, Stack, Map, Set Getting Help Keep an eye on the Ed forum for an announcement of the YEAH (YEAH = Your Early Assignment Help) group session where our veteran section leaders will answer your questions and share pro tips. We know it can be daunting to sit down and break the barrier of starting on a substantial programming assignment - come to YEAH for advice and confidence to get you on your way! The Ed forum is open 24/7 for questions about the assignment, lecture topics, the C++ language, using Qt, and more. Please first search Ed to see if your question has already been asked and answered before making a new post. To troubleshoot a problem with your specific code, your best bet is to bring it to the LaIR helper hours or office hours. Submit Before you call it done, run through our submit checklist to be sure all your ts are crossed and is dotted. Then upload your completed files to Paperless for grading. Please submit only the files you edited; for this assignment, these files will be: maze.cpp search.cpp short_answer.txt You don't need to submit any of the other files in the project folder. Submit to Paperless Note: On Paperless, all due dates and submission times are expressed in Pacific time.
http://cs106b.stanford.edu/assignments/1-cpp/
Due Friday, October 7 at 11:59 pm Submissions received by the due date receive a small on-time bonus. Late work after the due date is accepted if within the grace period (48 hours for this assignment). The grace period gives you the ability to self-grant an extension; we trust you will make reasonable and sparing use of this power. The grace period expires Sun, Oct 9 at 11:59 pm, after which we do not accept further late work. All due dates and submission times are expressed in Pacific time. Here it is - the first programming assignment of the quarter! Your first week's work will be a mix of coding, testing, and debugging tasks that get you practicing with the C++ language and development tools. The code you are to write uses expressions, control structures, functions, and string processing. You all have prior experience with these concepts; the novelty comes in figuring how to take what you already know and translate into the strange new world of C++. In addition to the language transition, you'll practice with the development tools and be introduced to strategies for testing and debugging your code. By the time you've completed the assignment, you'll be more comfortable working in C++ and ready to start building larger projects, or as we like to say, you'll have gotten your C++ legs under you! (apologies for the bad pun...) This assignment is to be completed individually. Working in pairs/groups is not permitted. Learning goals To become comfortable using Qt Creator to edit, build, run, and debug simple C++ programs. To practice writing C++ functions that manipulate numbers and strings. To learn basic use of the SimpleTest framework for unit tests and time trials. Assignment parts This assignment consists of two parts. Click on the links below for the full instructions. Perfect Numbers is a warmup exercise involving number theory, algorithms, and optimization. It gives you a guided transition into C++ and the testing and debugging tools we use. You can start on this task right away - and we recommend doing so! Completing the warmup in the first few days reserves the better part of the week for the bigger second part. Soundex Search is a complete program that demonstrates a nifty algorithm for matching and grouping names based on their pronunciation. This program uses C++ strings, console I/O, and the Vector class. There is a substantial chunk of code for you to write, so get an early start to give yourself sufficient time to work through issues and reach out for help if you hit any snags. Getting started We provide a ZIP of the starter project. Download the zip, extract the files, and double-click the .pro file to open the project in Qt Creator. Starter code The two source files you will edit are: perfect.cpp soundex.cpp Additionally, you will write short answers to some questions in short_answer.txt. Resources The CS106B guide to SimpleTest on testing your code using the SimpleTest framework. A guide to C++ strings written by our awesome colleague Keith Schwarz. This Python to C++ transition guide points out syntactical and functional differences between the two languages. Thank you to section leaders Jillian Tang and Ethan Chi for this wonderful resource. Resolving Common Build/Run Errors, compiled by section leader Jillian Tang. The CS106B Style Guide explains the rubric and standards we use when evaluating the style of your code. Getting Help Keep an eye on the Ed forum for an announcement of the Assignment 1 YEAH (YEAH = Your Early Assignment Help) group session where our veteran section leaders will answer your questions and share pro tips. We know it can be daunting to sit down and break the barrier of starting on a substantial programming assignment - come to YEAH for advice and confidence to get you on your way! We also here to help if you get run into issues along the way! The Ed forum is open 24/7 for general discussion about the assignment, lecture topics, the C++ language, using Qt, and more. Always start by searching first to see if your question has already been asked and answered before making a new post. To troubleshoot a problem with your specific code, your best bet is to bring it to the LaIR helper hours or office hours. Submit Before you call it done, run through our submit checklist to be sure all your ts are crossed and is are dotted. Then upload your completed files to Paperless for grading. Please submit only the files you edited; for this assignment, these files will be: perfect.cpp soundex.cpp short_answer.txt You don't need to submit any of the other files in the project folder. Submit to Paperless That's it; you're done! Congratulations on finishing your first CS106B assignment!
http://cs106b.stanford.edu/lectures/03-strings/
In today's lecture, we explore operations on the C++ string datatype and start to talk about what testing fundamentals look like in CS106B. Readings Text 3.1-3.7; CS106B Testing Guide Links Video on canvas Code and Slides 03-ParametersTesting.pdf 03-StringsEthicsSnippet.mp4 SoundexUnicodeSlides.pdf
http://cs106b.stanford.edu/lectures/05-grid-stack-queue/
Today we will discuss use of Stack and Queue. These containers store data in an ordered format and are used to solve many problems. Readings Text 5.1 - 5.3, Class documentation for Stack and Queue Links Video on canvas Code and Slides 05-StackQueue.pdf RPN.zip RPNCalculator.zip
http://cs106b.stanford.edu/lectures/02-cpp/
Introduction to Fundamentals of C++ Programming Readings Text Ch. 1, 2.1-2.4 Links Video on canvas Code and Slides 02-CPPBasics.pdf HamiltonCode.zip
http://cs106b.stanford.edu/lectures/04-console-vector/
In this lecture, we will introduce use of Vector and Grid. Readings Text Reading: 2.5-2.9, 5.1, Class documentation for Vector and Grid Links Video on canvas Code and Slides 04-ADTsVectorGrid.pdf VectorPerformance.zip
http://cs106b.stanford.edu/lair
What are LaIR helper hours? LaIR helper hours are the in-person office hours for the CS106 courses. The LaIR is staffed by our awesome team of CS198 section leaders. Students come to the LaIR to get individual help with debugging assignments and answers to their conceptual questions. There is also additional LaIR-style support offered by some student-run organizations: Black LaIR, run by Stanford Black in CS WICS LaIR, run by Stanford Women in Computer Science Schedule LaIR is open Sunday-Thursday, 7-11PM Pacific Time. On the first week of the quarter, LaIR starts on Wednesday. In-person logistics LaIR is held in Durand 353 (glass conference room across from 3rd floor elevator). CS106 students are granted access for after-hours entry to Durand (use your Stanford ID card to enter the buildling). To get help, come to Durand and add your request to the queue. The helper queue is managed online. Add your request on the LaIR signup page. Be sure to properly identify your location when signing up so the helper can find you when your turn comes up. Requests are handled in first-come-first-served order. All students are expected to follow current university guidelines regarding COVID safety. Failure to do so will result in revoked LaIR privileges. LaIR has a limited remote option to support SCPD students and those with accommodations. Remote LaIR option Remote LaIR is only available for SCPD students, students with OAE accommodations that require remote access, and students who are in COVID isolation. To use Remote LaIR, you must first make arrangements by emailing cs198lair@gmail.com. This is the only email that will respond to requests for Remote LaIR. Do not email the lecturers, head TA, section leaders, or cs198@cs about Remote LaIR; such emails will not be answered. SCPD students automatically receive access to Remote LaIR for the entire quarter; no need to send email. For those with OAE accommodations, please email cs198lair@gmail.com your OAE letter specifying need for remote access. Indicate clearly in your email the start and end dates you will require Remote LaIR (you may write "entire quarter" if that is the case). If you test positive or receive an exposure notification, please email cs198lair@gmail.com with a screenshot of your positive test (with name and date visible) or exposure notification (with name and date visible). Indicate clearly in your email the start and end dates of isolation. Access to Remote LaIR is removed after your end date has passed. Remote signups for LaIR follow a similar process to in-person signups. Visit lair_signup to add your request to the queue, put your Zoom link in the location field of their request. It is a violation of the Fundamental Standard to falsify documentation for access to Remote LaIR, to attempt to bypass checks to access Remote LaIR, or to share links or logins with other students to access Remote LaIR. If we discover any of the above, the course staff will refer the matter to the Office of Community Standards. Common questions about LaIR I tried to sign up for LaIR, but the queue was closed, even though the schedule says LaIR is open now. What gives? In times of high demand, we may need to close the queue to new requests toward the end of the shift. We do this to ensure that we have sufficient resources to assist all students already in the queue.
http://cs106b.stanford.edu/late
Hofstadter's Law: "It always takes longer than you think, even when you take Hofstadter's Law into account." This course moves quickly and the content builds on itself; we know that the best outcomes come from students staying on pace. That said, we know the the demands of student life are varied, so we have designed our late policy to give you some flexibility where we can. Each assignment has a deadline on which it is due. Submissions received by the deadline earn a small on-time bonus. We will accept late work during a short "grace period" after the deadline. The grace period is typically 48 hours, but may be shorter for certain assignments. There is no penalty for using the grace period, nor do you need to ask our permission. The grace period gives you the ability to self-grant an extension; we trust you will make reasonable use of that power. After the grace period expires, no further late submissions are accepted, unless there have been arrangements made for an exceptional situation. The grace period is effectively an extension that is pre-approved. This allows you to self-accommodate for unexpected mishaps (laptop on the fritz, get a cold, etc) as needed. We strongly recommend that you always plan to finish by the deadline. This sets you on the path to earn the on-time bonus and preserves the grace period as a buffer should you hit a snag. Further extensions beyond the grace period will not be granted, except in cases of truly exceptional circumstances. Such needs are accommodated on a case-by-case basis and arranged by the Head TA (do not email your section leader). Common questions about late policy Is the ontime bonus reflected in my assignment grade? The on-time bonus is separate from the bucket grades shown on Paperless. We aggregate the bonuses you earned and use as small boost to your course grade at the end of the quarter. If I make a first submission by the deadline, can I use the grace period to make a later submission or add a extension? If you make multiple submissions, we grade the last one. If the last submission is during the grace period, you forfeit any on-time bonus. The grace period is intended to provide flexibility when unexpected circumstances prevent you from finishing the assignment by the deadline. Late submissions put extra pressure on your section leader to return feedback to you in a timely manner. We ask that you use the grace period only when your situation requires it.
http://cs106b.stanford.edu/assignments/2-adt/maze.html
A maze is a twisty and convoluted arrangement of corridors that challenges the solver to find a path from the entry to the exit. This part of the assignment is about using ADTs to represent, process, and solve mazes. An introduction to mazes Labyrinths and mazes have fascinated humans since ancient times (remember Theseus and the Minotaur?), but mazes can be more than just recreation. The mathematician Leonhard Euler was one of the first to analyze mazes mathematically, and in doing so, he founded the branch of mathematics known as topology. Many algorithms that operate on mazes are closely related to graph theory and have applications to diverse tasks such as designing circuit boards, routing network traffic, motion planning, and social networking. Your goal for the maze portion of the assignment to implement neat algorithms to solve a maze, while gaining practice with ADTs. Before jumping into the code, please carefully read this background information on how we will represent mazes, which ADTs to use, and the format of the provided maze data files. A Grid represents a maze A maze can be modeled as a two-dimensional array where each element is either a wall or a corridor. The Grid from the Stanford library is an ideal data structure for this. A maze is represented as a Grid<bool>. Each grid element is one "cell" of the maze. The boolean value for each element indicates whether that cell is a corridor. The element grid[row][col] is true when there is a open corridor at location (row, col) and false if it is a wall. A maze is read from a text file. Each line of the file is one row of the maze. Within a row, the character @ is a wall, and - is a corridor. Here is a sample 5x7 maze file (5 rows by 7 columns): ------- -@@@@@- -----@- -@@@-@- -@---@- Our starter project provides the function readMazeFile to read a text maze into a Grid<bool>. The folder "Other files/res" contains our provided maze files. A GridLocation is a row/col pair The GridLocation struct is a companion type to represent a row/col location in a Grid. A GridLocation has two fields, row and col, that are packaged together into one aggregate type. The sample code below demonstrates using a GridLocation variable and assigning and accessing its row and col fields. // Declare a new GridLocation GridLocation chosen; // default initialized to 0,0 chosen.row = 3; // assign row of chosen chosen.col = 4; // assign col of chosen // Initialize row,col of exit as part of its declaration GridLocation exit = { maze.numRows()-1, maze.numCols()-1 }; // last row, last col // You can use a GridLocation to index into a Grid (maze is a Grid) // This checks if maze[chosen] is an open corridor if (maze[chosen]) // chosen was set to {3, 4} so this accesses maze[3][4] ... // You can directly compare two GridLocations if (chosen == exit) ... // You can also individual access the GridLocation's fields for row & col if (chosen.row == 0 && chosen.col == 0) ... A Vector of GridLocations is a path A path through the maze is an ordered sequence of connecting GridLocations. A Vector<GridLocation> is just the tool for the job! A valid maze solution path starts at the maze entrance, steps through connecting GridLocations, and ends at the maze exit. In the "Other files/res" folder, there are a few .soln text files that contain solutions for some of our maze files. Here is the solution file for the 5x7 maze shown earlier. The solution path is a sequence of 11 GridLocations, starting at r0c0 (row 0, col 0), and ending at r4c6. {r0c0, r0c1, r0c2, r0c3, r0c4, r0c5, r0c6, r1c6, r2c6, r3c6, r4c6} Our starter code provides the function readSolutionFile that reads a solution path into a Vector<GridLocation>. Not every maze will have a corresponding .soln file, and part of your job will be to write code to generate solutions! Resources for the Stanford ADTs Documentation for Stanford collections: Grid, GridLocation, Stack, Queue, Vector Section 6.1 of the textbook introduces struct types In the lower-left corner of your Qt Creator window, there is a magnifying glass by a search field. If you type the name of a header file such as grid.h or gridlocation.h , Qt will display the corresponding header file. A few reminders for using our ADT/templates: The assignment operator for our ADTs makes a deep copy. Assigning from one Vector/Set/Stack to another creates a copy of the ADT and a copy of all its elements. When using C++ template, you have to indicate the particular element type you will store. Using Vector without specifying the element type just won't fly, and a Vector<int> is not the same thing as a Vector<string>. The error messages you receive when you have mismatches in template types can be cryptic and hard to interpret. Bring your template woes to LaIR or the Ed forum, and we can help untangle them with you. On to the code! In maze.cpp, you will write two helper functions that process grids and paths and two maze-solving functions. You will also be writing comprehensive test cases. 1) Write helper function generateValidMoves() Your first task is to implement the helper function to generate the neighbors for a given location: Set<GridLocation> generateValidMoves(Grid<bool>& maze, GridLocation cur) Given a maze represented as a Grid of bool and a current GridLocation cur, this function returns a Set of all valid moves from cur. Valid moves are those GridLocations that are: Exactly one "step" away from cur in one of the four cardinal directions (N, S, E, W) Within bounds for the Grid An open corridor, not a wall There are a few provided tests for generateValidMoves, but these tests are not fully comprehensive. Write at least 3 additional tests to make sure your helper function works correctly. Remember to label your tests as STUDENT_TEST. 2) Write helper function validatePath() Next you'll write a function to confirm that a path is a valid maze solution: void validatePath(Grid<bool>& maze, Vector<GridLocation>& path) The image above displays a valid solution; each colored dot marks a GridLocation along the path. A path is a valid solution to a maze if it meets the following criteria: The path is not empty The path starts at the entry (upper left corner) of the maze The path ends at the exit (lower right corner) of the maze Each location in the path is a valid move from the previous path location Hint: rather than re-implement the same logic you already did for generateValidMoves, simply call that function and check whether a move is contained in the set of valid moves. The path must not contain a loop, i.e. the same location cannot appear more than once in the path Hint: a Set<GridLocation> is a good data structure for tracking seen locations and avoiding a repeat. The function validatePath completes successfully if all of the criteria for a valid solution are met. If it instead detects that the path violates one of the above constraints, validatePath should call the error function from error.h to report what is wrong. The error function stops execution and prints your explanatory message: error("Here is my message about what has gone wrong"); The function validatePath will require careful testing to confirm that it correctly detects all forms of valid and invalid paths. We've provided a few tests to get you started, but you will need additional tests of your own to complete the job. Write at least 3 student test cases for validatePath. Note the use of the new test types EXPECT_ERROR and EXPECT_NO_ERROR in our provided tests. An EXPECT_ERROR test case evaluates an expression, expecting that the operation will raise an error. While executing the test, the SimpleTest framework will "catch" the error, record that it happened, and resume. Because the error was expected, the test case is marked Correct. If an error was expected but didn't materialize, the test case is marked Incorrect. EXPECT_NO_ERROR operates in the reverse: it expects the code to run without errors and if so, it is Correct. If an error is raised, it is marked Incorrect. More information on the different test macros can be found in the CS106B Testing Guide. After writing validatePath, not only will you be familiar with using the ADTs to represent mazes, now you have a function to help when testing the functions you will write next. Having thoroughly tested your validatePath on a variety of invalid paths means you can be confident that it is the oracle of truth when it comes to confirming a solution. Your future self thanks you! Q5. After you have written your test cases, describe your testing strategy to confirm that your validatePath operates as intended. How confident are you that it works correctly in all cases? 3) Write solveMazeBFS() Now you're ready to tackle a function that searches the maze for a solution path: Vector<GridLocation> solveMazeBFS(Grid<bool>& maze) Solving a maze can be seen as a specific instance of a path-finding problem, where the challenge is to find a route from the entrance to the exit. Path-finding comes up in a variety of situations such as packet routing on the internet, robot motion planning, analyzing gene mutations, spell correction, and more. Breadth-first search (BFS) is a classic and elegant algorithm for finding a path. A breadth-first search considers paths outward from the entry location in a radial fashion until it finds the exit. The first paths examined take one hop from the entry. If any of these reach the exit location, success! If not, the search expands to those paths that are two hops long. At each subsequent step, the search expands radially, examining all paths of length three, then of length four, and so on, stopping at the first path that reaches the exit. Breadth-first search is typically implemented using a queue. The queue stores partial paths that represent possibilities to explore. The first paths enqueued are all length one, followed by enqueuing the length two paths, and so on. Because a queue processing elements in FIFO order, all shorter paths are dequeued and processed before the longer paths make their way to the front of queue. This means paths are tried in order of increasing length and thus a solution path found via breadth-first search will be the shortest possible such solution. At each step, the algorithm considers the current path at the front of the queue. If the current path ends at the exit, it must be a completed solution path. If not, the algorithm considers the current path, extends it to reach locations that are one hop further away in the maze, and enqueues those extended paths to be examined in later rounds. Here are the steps followed by a breadth-first search: Create an empty queue of paths. Each path is a Vector<GridLocation> and the queue of paths is of type Queue<Vector<GridLocation>>. A nested ADT type like this looks a little scary at first, but it is just the right tool for this job! Create a length-one path containing just the entry location. Enqueue that path. In our mazes, the entry is always the location in the upper-left corner, and the exit is the lower-right. While there are still more paths to explore: Dequeue the current path from queue. If the current path path ends at exit: You're done. The current path is the solution! Otherwise: Determine the viable neighbors from the end location of the current path. A viable neighbor is a valid move that has not yet been visited during the search For each viable neighbor, make copy of current path, extend by adding neighbor and enqueue it. A couple of notes to keep in mind as you're implementing BFS: You may make the following assumptions: The maze is well-formed, rectangular, contains at least two rows and two columns, and the grid locations at upper-left (enter) and lower-right (exit) are both open corridors. The maze has a solution. The search should not revisit previously visited locations or create a path with a cycle. For example, if the current path leads from location r0c0 to r1c0, you should not extend the path by moving back to location r0c0. You should not call validatePath within your solveMazeBFS function, but you can call it in your test cases to confirm the validity of paths found by solveMazeBFS. For a helpful visualization and debugging aid, try adding a graphical animation using our provided mazegraphics. We have provided some tests to get your started. Add 2 or more student tests that further verify the correct functionality of the solveMazeBFS function. There are additional maze files in the res folder and you can also create your own files. 4) Write solveMazeDFS() Your final task for maze is to implement depth-first search: Vector<GridLocation> solveMazeDFS(Grid<bool>& maze) Depth-first search is an alternate path-finding algorithm that works similarly to breadth-first search, but tries the possible paths in a different order. Once a depth-first search starts on a path, it goes deep, continuing until it reaches its ultimate end and only if unsuccessful, moves on to try other paths. Both algorithms consider all possible paths in search of a solution, the difference being in which order they consider the paths. The power of ADTs makes it magically easy to convert between the two algorithms. Depth-first search uses the same steps for a breadth-first search but everywhere you see queue with enqueue/dequeue operations, substitute a stack with push/pop operations. Whereas BFS uses a queue of paths, enqueuing paths to the back and dequeuing the next path to explore from the front, a DFS instead uses a stack and pushes paths to the top and popping the next path to explore from the top. Using a queue considers paths in FIFO (first-in-first-out) order , a stack will process the paths in LIFO (last-in-first-out) order. This means that implementing solveMazeDFS is going to be a snap - just a switcheroo in which ADTs are being used. For this function: copy/paste your working implementation from solveMazeBFS change the data type of allPaths from Queue<Vector<GridLocation>> to Stack<Vector<GridLocation>> change Queue operations (enqueue/dequeue) into Stack operations (push/pop) Voila, you've done it! Add tests to confirm the solutions found by solveMazeDFS are valid according to validatePath. You now have two powerful algorithms to solve a maze and have gotten lots of practice with ADTs - way to go! Q6. While BFS and DFS are equivalently capable in terms of finding a solution, the two can differ in which solution is found (when there are multiple) and how quickly a solution is discovered. An advantage of BFS is that it always finds the shortest possible solution, explain why this is guaranteed for BFS and not for DFS. An advantage of DFS is that for some mazes it will find a solution more quickly than BFS, explain how this could be so. Use graphics functions to animate solving the maze The Stanford C++ library includes components for drawing and user interaction, but we won't ask you to dig into those features. Instead, we will supply any needed graphics routines pre-written in a simple, easy-to-use form. For this project, the provided mazegraphics module has function to display a maze and highlight a path. Read the mazegraphics.h header file for more details about: drawMaze(Grid<bool>& grid) highlightPath(Vector<GridLocation>& path, string color, int mSecsToPause = 0) During solve, call drawMaze once to draw the maze and then repeatedly call highlightPath to mark the locations on the path currently being explored. If desired, supply the optional third argument of highlightPath to pause the animation after each step to slow things down. This gives you time to watch the animation and see how the path is updated and follow along with the algorithm and debug its operation. Now sit back and watch the show as your clever algorithms find the path to escape any maze you throw at it! Congratulations! Q7. Most students are sold on the benefit of the fancy features provided by Vector, Grid, and Set but the ADT skeptics may question the value of Stack and Queue ADTs, seeing them as nothing more than a restricted Vector. Make your best argument to convince them of the benefits these ADTS have over Vector. References There are many interesting facets to mazes and much fascinating mathematics underlying them. Mazes will come up again several times this quarter. Chapter 9 of the textbook uses a recursive depth-first search as path-finding algorithm. At the end of the quarter when we talk about graphs, we'll explore the equivalence between mazes and graphs and note how many interesting results in mazes came from breakthroughs in graph algorithms. Walter Pullen, Maze Classification. http://www.astrolog.org/labyrnth/algrithm.htm Website with lots of great info on mazes and maze algorithms Jamis Buck. Maze Algorithms. https://www.jamisbuck.org/mazes/ Fun animations of maze algorithms. He also wrote the excellent book about Mazes for Programmers: Code Your Own Twisty Little Passages. Extensions If you have completed the assignment and want to explore further, here are some ideas for extensions. Instead of reading pre-written mazes from a file, you could instead generate a new random maze on demand. There are an amazing (I could not resist...) variety of algorithms for maze construction, ranging from the simple to the sublime. Here are a few names to get you started: backtracking, depth-first, growing tree, sidewinder, along with algorithms named for their inventor: Aldous-Broder, Eller, Prim, Kruskal, Wilson, and many others. Try out other maze solving algorithms. How does BFS and DFS measure up against the approaches in terms of solving power or runtime efficiency? Take a look at other solvers such as random mouse, wall following, recursive depth-first, Bellman-Ford, or others. There are many other neat maze-based games out there that make fun extensions. You might gather ideas from Robert Abbott's http://www.logicmazes.com or design a maze game board after inspiration from these musings on mazes from Chris Smith.
http://cs106b.stanford.edu/assignments/1-cpp/perfect.html
This warmup task gives you practice with C++ expressions, control structures, and functions, as well as testing and debugging your code. Throughout the writeup, we pose thought questions (in the highlighted yellow boxes) for you to answer. The starter project includes the file short_answer.txt (located under "Other Files" in the Qt Project pane). Edit this file to fill in your responses and submit it with your code. Your section leader will review your responses as part of grading. We'll be evaluating the sincerity and thoughtfulness of your reflection and reasoning; not a rigid "right/wrong" thing. Perfect numbers This exercise explores a type of numbers called perfect numbers. Before we jump into the coding, let's begin with a little math and history. A perfect number is an integer that is equal to the sum of its proper divisors. A number's proper divisors are those positive numbers that evenly divide it, excluding itself. The first perfect number is 6 because its proper divisors are 1, 2, and 3, and 1 + 2 + 3 = 6. The next perfect number is 28, which equals the sum of its proper divisors: 1 + 2 + 4 + 7 + 14. Perfect numbers are an interesting case study at the intersection of mathematics, number theory, and history. The rich history of perfect numbers is a testament to how much these numbers have fascinated humankind through the ages. Using our coding powers, we can explore different algorithmic approaches to finding these special numbers. An exhaustive algorithm One approach to finding perfect numbers is using an exhaustive search. This search operates by brute force, looping through the numbers one by one, and testing each to determine if it is perfect. Testing whether a particular number is perfect involves another loop to find those numbers which divide the value and add those divisors to a running sum. If that sum and the original number are equal, then you've found a perfect number! Here is some Python code that performs an exhaustive search for perfect numbers: def divisor_sum(n): total = 0 for divisor in range(1, n): if n % divisor == 0: total += divisor return total def is_perfect(n): return n != 0 and n == divisor_sum(n) def find_perfects(stop): for num in range(1, stop): if is_perfect(num): print("Found perfect number: ", num) if num % 10000 == 0: print('.', end='',flush=True) # progress bar print("Done searching up to ", stop) The Python code from above is re-expressed in C++ below. If your CS106A was taught in Python, comparing and contrasting these two may be a helpful way to start adapting to the language differences. If instead your prior experience was in Java or Javascript, just sit back and enjoy how C++ already seems familiar to what you know! /* The divisorSum function takes one argument `n` and calculates the * sum of proper divisors of `n` excluding itself. To find divisors * a loop iterates over all numbers from 1 to n-1, testing for a * zero remainder from the division using the modulus operator % * * Note: the C++ long type is a variant of int that allows for a * larger range of values. For all intents and purposes, you can * treat it like you would an int. */ long divisorSum(long n) { long total = 0; for (long divisor = 1; divisor < n; divisor++) { if (n % divisor == 0) { total += divisor; } } return total; } /* The isPerfect function takes one argument `n` and returns a boolean * (true/false) value indicating whether or not `n` is perfect. * A perfect number is a non-zero positive number whose sum * of its proper divisors is equal to itself. */ bool isPerfect(long n) { return (n != 0) && (n == divisorSum(n)); } /* The findPerfects function takes one argument `stop` and performs * an exhaustive search for perfect numbers over the range 1 to `stop`. * Each perfect number found is printed to the console. */ void findPerfects(long stop) { for (long num = 1; num < stop; num++) { if (isPerfect(num)) { cout << "Found perfect number: " << num << endl; } if (num % 10000 == 0) cout << "." << flush; // progress bar } cout << endl << "Done searching up to " << stop << endl; } Observing the runtime The starter project contains the C++ code above. It is given to you pre-written and works correctly. Look over the code and confirm your understanding of how it works. If you have questions or points of confusion, make a post on Ed to start a conversation or come by Lair or office hours. We want you to first observe the performance of the given code. One simple approach for measuring runtime is to simply run the program while keeping an eye on your watch or a clock. Open the starter project in Qt Creator and build and run the program. When you are prompted in the console to "Select the test groups you wish to run," enter 0 for "None" and the program will instead proceed with the ordinary main function. This main function does a search for perfect numbers across the range 1 to 40000. Watch the clock while the program runs and note the total elapsed time. Q1. Roughly how long did it take your computer to do the search? How many perfect numbers were found and what were they? You now have one timing result (how long it takes for a range of size 40000), but would need additional data to suss out the overall pattern. What would be ideal is to run the search several times with different search sizes (20000, 40000, 80000, etc.) and measure the runtime for each. To do this manually, you would edit main.cpp, change the argument to findPerfects and re-run the program while again watching the clock. Repeating for many different sizes would be tedious. It would be more convenient to run several time trials in sequence and have the program itself measure the elapsed time. The SimpleTest framework can help with this task, so let's take a detour there now. What is SimpleTest? In CS106B, you will use a unit-test framework called SimpleTest to test your code. This type of testing support will be familiar to you if you've been exposed to Java's JUnit or python doctest. (and no worries if you haven't yet, there will be much opportunity to practice with unit testing in CS106B!) Stop here and read our guide to testing to introduce yourself to SimpleTest. For now, focus on use of STUDENT_TEST, EXPECT, EXPECT_EQUAL, and TIME_OPERATION, as these are the features you will use in Assignment 1. Practice with TIME_OPERATION Now that you know the basics of SimpleTest, you are ready to practice with it using the provided tests in perfect.cpp. Open the perfect.cpp file and scroll to the bottom. Review the provided test cases to see how they are constructed. The last provided test shows a sample use of TIME_OPERATION to measure the time spent during a call to findPerfects. Run the program and when prompted to "Enter your selection," enter the number that corresponds to the option for the perfect.cpp tests. This will run the test cases from the file perfect.cpp. The SimpleTest window will open to show the results of each test. The given code should pass all the provided tests. This shows you what a successful sweep will look like. Note that the SimpleTest results window also reports the elapsed time for each TIME_OPERATION. When constructing a time trial test, you will want to run several TIME_OPERATION across a set of different input sizes that allow the trend line to emerge. Selecting appropriate input sizes is a bit of an art form. If you choose a size that is too small, it can finish so quickly that the time is not measurable or gets lost in the noise of other activity. A size that is too large will have you impatiently waiting for it to complete. You are generally aiming for test cases that complete in 10-60 seconds. Note that a size that is just right for your friend's computer might be too small for your Mac, so you have to hunt a bit to find a good range for your system. Set up an experiment by adding a STUDENT_TEST that does a single TIME_OPERATION of findPerfects. Try different sizes to determine the largest size which your computer can complete in around 60 seconds or so. Edit your STUDENT_TEST to do four time trials on increasing sizes, doubling the size each time and ending with the final trial of that large size. The starter code offers a suggestion for how to use a loop to configure a series of time trials. This is a great technique to add to your repertoire! Run your time trials and write down the reported times. Q2. Make a table of the timing results for findPerfects that you observed. (old-school table of text rows and columns is just fine) Use the data from your table to work out the relationship between the search size and the amount of time required. To visualize the trend, it may help to sketch a plot of the values (either by hand or using a tool like https://www.desmos.com/calculator). You will find that doubling the input size doesn't take simply twice the time; the time goes up by a factor of 4. There appears a quadratic relationship between size and program execution time! Let's investigate why this might be. Q3. Does it take the same amount of work to compute isPerfect on the number 10 as it does on the number 1000? Why or why not? Does it take the same amount of work for findPerfects to search the range of numbers from 1-1000 as it does to search the numbers from 1000-2000? Why or why not? That quadratic relationship means the algorithm is going to seriously bog down for larger ranges. Sure, the first four perfect numbers pop out in a jif (6, 28, 496, 8128) because they are encountered early in the search, but that fifth one is quite a ways off - in the neighborhood of 33 million. Working up to that number by exhaustive search is going to take l-o-o-ng time. Q4. Extrapolate from the data you gathered and make a prediction: how long will it take findPerfects to reach the fifth perfect number? This is your first exposure to algorithmic analysis, a topic we will explore in much greater detail throughout the course. As a fun aside, if you have access to a Python environment, you can attempt similarly-sized searches using the Python version of the program to see just how much slower an interpreted language (Python) is compared to a compiled language (C++). Check out the Python vs C++ Showdown we've posted to the Ed discussion forum. Digging deeper into testing Having completed these observations about performance, we move on to testing the code, which is one of the most important skills with which we hope you will come out of this class. Designing good tests and writing solid, well-tested code are skills that will serve you well for the rest of your computer science journey! Here are some further explorations to do with SimpleTest: All perfect numbers are positive, there is no such thing as a negative perfect number; thus isPerfect should return false for any negative numbers. Will you need to make a special case for this or does the code already handle it? Let's investigate... Look at the code and make a prediction about what happens if isPerfect is given a negative input. Add a new STUDENT_TEST case that calls isPerfect on a few different negative inputs. The expected result of isPerfect for such inputs should be false. A quick reminder that the tests that we give you in the starter code will always be labeled PROVIDED_TEST and you should never modify the provided tests. When you add tests of your own, be sure to label them STUDENT_TEST. This allows your grader to easily identify which tests are yours. Run these test cases to confirm that the code behaves as expected. Introduce a bug into divisorSum by erroneously initializing total to 1 instead of zero. Rebuild and run the tests again. This lets you see how test failures are reported. Q5. Do any of the tests still pass even with this broken function? Why or why not? Be sure to undo the bug and restore divisorSum to a correct state before moving on! Streamlining and more testing The exhaustive search does a lot of work to find the perfect numbers. However, there is a neat little optimization that can significantly streamline the process. The function divisorSum runs a loop from 1 to N to find divisors, but this is a tad wasteful, as we actually only need to examine divisors up to the square root of N. Each time we find a divisor, we can directly compute what the corresponding pairwise factor for that divisor (no need to search all the way up to N to find it). In other words, we can take advantage of the fact that each divisor that is less than the square root is paired up with a divisor that is greater than the square root. Take a moment to carefully think about why this is true and how you would rearrange the code to capitalize on this observation. You are to implement a new function smarterSum: long smarterSum(long n) that produces the same result as the original divisorSum but uses this optimization to tighten up the loop. You may find it helpful to use the provided divisorSum implementation as a starting point (that is, you may choose to copy-paste the existing code and make tweaks to it). Be careful: there are some subtle edge cases that you may have to handle in the adapted version that were not an issue in the original. The C++ library function sqrt can be used to compute a square root. After adding new code to your program, your next step is to thoroughly test that code and confirm it is bug-free. Only then should you move on to the next task. Having just written smarterSum, now is the time for you to test it. Because you already have the vetted function divisorSum at your disposal, a clever testing strategy uses EXPECT_EQUAL to confirm that the result from divisorSum(n) is equal to the result from smarterSum(n). Rather than pick any old values for n, brainstorm about which values would be particularly good candidates. As an example, choosing n=25 (which has an square root that is an integer value) could confirm there is no off-by-one issue on the stopping condition of your new loop. Add at least 3 new student test cases for smarterSum. Q6. Explain your testing strategy for smarterSum and how you chose your specific test cases that lead you to be confident the function is working correctly. Now with confidence in smarterSum, implement the following two functions that build on it: bool isPerfectSmarter(long n) void findPerfectsSmarter(long stop) The code for these two functions is very similar to the provided isPerfect and findPerfects functions, basically just substituting smarterSum for divisorSum. Again, you may copy-paste the existing implementations, and make small tweaks as necessary. Now let's run time trials to see just how much improvement we've gained. Add a STUDENT_TEST that uses a sequence of TIME_OPERATION to measure findPerfectsSmarter. Set up 4 runs on increasing sizes, doubling the size each time, and ending with a run on the maximum size your program can complete in around 60 seconds using this improved algorithm. Given the smarter algorithm, these sizes will be larger than you used for the original version. Q7. Record your timing results for findPerfectsSmarter into a table. Our previous algorithm grew at the rate N^2, while this new version is since we only have to inspect divisors for every number along the way. If you plot runtimes on the same graph as before, you will see that they grow much less steeply than the runtimes of the original algorithm. Q8. Make a prediction: how long will findPerfectsSmarter take to reach the fifth perfect number? Mersenne primes and Euclid Back to story time: in 2018, there was a rare finding of a new Mersenne prime. A Mersenne number is a number that is one less than a power of two, i.e., of the form 2k-1 for some integer k. A prime number is one whose only divisors are 1 and the number itself. A Mersenne number that is prime is called a Mersenne prime. The Mersenne number 25-1 is 31 and 31 is prime, making it a Mersenne prime. Mersenne primes are quite elusive; the most recent one found is only the 51st known and has almost 25 million digits! Verifying that the found number was indeed prime required almost two weeks of non-stop computing. The quest to find further Mersenne primes is driven by the Great Internet Mersenne Prime Search (GIMPS), a cooperative, distributed effort that taps into the spare computing cycles of a vast community of volunteer machines. Mersenne primes are of great interest because they are some of the largest known prime numbers and because they show up in interesting ways in games like the Tower of Hanoi and the wheat and chessboard problem. Back in 400 BCE, Euclid discovered an intriguing one-to-one relationship between perfect numbers and the Mersenne primes. Specifically, if the Mersenne number 2k-1 is prime, then 2(k-1) * (2k-1) is a perfect number. The number 31 is a Mersenne prime where k = 5, so Euclid's relation applies and leads us to the number 24 * (25-1) = 496 which is a perfect number. Neat! Those of you enrolled in CS103 will appreciate this lovely proof of the Euclid-Euler theorem. Turbo-charging with Euclid The final task of the warmup is to leverage the cleverness of Euclid to implement a blazingly-fast alternative algorithm to find perfect numbers that will beat the pants off of exhaustive search. Buckle up! Your job is to implement the function: long findNthPerfectEuclid(long n) The exhaustive search algorithm painstakingly examines every number from 1 upwards, testing each to identify those that are perfect. Taking Euclid's approach, we instead hop through the numbers by powers of two, checking each Mersenne number to see if it is prime, and if so, calculate its corresponding perfect number. The general strategy is outlined below: Start by setting k = 1. Calculate m = 2k-1 (Note: C++ has no exponentiation operator, instead use library function pow) Determine whether m is prime or composite. (Hint: a prime number has a divisorSum and smarterSum equal to one. Code reuse is your friend!) If m is prime, then the value 2(k-1) * (2k-1) is a perfect number. If this is the nth perfect number you have found, stop here. Increment k and repeat from step 2. The call findNthPerfectEuclid(n) should return the nth perfect number. What will be your testing strategy to verify that this function returns the correct result? You may find this table of perfect numbers to be helpful. One possibility is a test case that confirms each number is perfect according to your earlier function, e.g. EXPECT(isPerfect(findNthPerfectEuclid(n))). Note: The findNthPerfectEuclid function can assume that all inputs (values of n) will be positive (that is, greater than 0). In particular, this means that you don't have to worry about negative values of n or the case where n is zero. Add at least 4 new student test cases of your own to verify that your findNthPerfectEuclid works correctly. Q9. Explain how you chose your specific test cases and why they lead you to be confident findNthPerfectEuclid is working correctly. A call to findNthPerfectEuclid(5) should near instantaneously return the fifth perfect number. Woah! Quite an improvement over the exhaustive algorithm, eh? But if you try for the sixth, seventh, eighth, and beyond, you can run into a different problem of scale. The super-fast algorithm works in blink of an eye, but the numbers begin to have so many digits that they quickly exceed the capability of the long data type (long can hold a maximum of 10 to 20 digits depending on your system. When a number gets too large, the value will unexpectedly go negative - how wacky is that? Take CS107 to learn more) So much for the invincibility of modern computers... As a point of comparison, back in 400 BC, Euclid worked out the first eight perfect numbers himself - not too shabby for a guy with no electronics! Warmup conclusions Hooray for algorithms! One of the themes for CS106B is the tradeoffs between algorithm choices and program efficiency. The differences between the exhaustive search and Euclid's approach is striking. Although there are tweaks (such as the square root trick) that will improve each algorithm relative to itself, the biggest bang for the buck comes from starting with a better overall approach. This result foreshadows many of the interesting things to come this quarter. Before moving on to the second part of the assignment, confirm that you have completed all tasks from the warmup. You should have answers to questions 1 to 9 in short_answer.txt. You should have implemented the following functions in perfect.cpp smarterSum isPerfectSmarter findPerfectsSmarter findNthPerfectEuclid as well as added the requisite number of tests for each of these functions. Your code should also have thoughtful comments, including an overview header comment at the top of the file. In the header comment for perfect.cpp, share with your section leader a little something about yourself and to offer an interesting tidbit you learned in doing this warmup (be it something about C++, algorithms, number theory, how spunky your computer is, or some other neat insight).
http://cs106b.stanford.edu/resources/python_to_cpp.html
Denoting Structure: Semicolons, Parentheses, and Braces In Python, indentation and whitespace indicates where statements end and how structures nest inside one another. In C++, you need to explicitly indicate this using semicolons, parentheses, and braces. Curly braces - the { and } characters - are almost exactly equivalent to Python indentation. You'll need to use them in if statements, for loops, while loops, and functions. For example: Python C++ def my_function(a, b): if a == 1: print(b) void myFunction(int a, string b) { if (a == 1) { cout << b << endl; } } Although indentation alone does not tell the C++ compiler how structures nest inside one another, it's important to indent things nonetheless to better convey the meaning of your code. When using curly braces, it's customary to put each close brace on its own line and aligned with the part of the code it's closing out. This makes it easier to see how things are nested. Parentheses - the ( and ) characters - also have the same meaning as in Python. The difference is that, in C++, several structures require the use of parentheses in places where they were optional in Python. For example, the conditions in an if statement and while loop require parentheses: Python C++ while x < 137: x = 3*x + 1 if x % 2 == 0: x /= 2 while (x < 137) { x = 3*x + 1; if (x % 2 == 0) { x /= 2; } } One of the more obvious differences between Python and C++ is the use of semicolons - the ;character. In Python, the end of a statement is denoted by a newline. In C++, every statement (except for control statements like for, if, and while) must end with a semicolon. For example: /***** C++ *****/ int number = 137; callAFunction(arg1, arg2); However, make sure that you do not put a semicolon after a control statement like for, if, or while. Similarly, do not put a semicolon at the end of a statement beginning with #. /***** Bad C++: Do Not Do This! *****/ #include "strlib.h"; // <-- Oops, no semicolon here! if (myNumber == 137); { // <-- Oops, no semicolon here! while (myNumber % 2 == 1); { // <-- Oops, no semicolon here! myNumber /= 2; } } Types C++ is a typed language, which means that you sometimes need to explicitly say what type something is. A type is a fundamental kind of value. Examples include int, string, char (single character, not in Python), double (equivalent of Python float). You must explicitly state the type when declaring a variable, but not while using it after that. For example: /***** C++ *****/ int number = 137; // Declare number; type needed number += 106; // number already declared; do not include type Function parameters must also have types; also, every function must include a return type. If the function doesn't return anything, it has return type void. However, you don't have to include the types when calling the function. Python C++ def pizkwat(a, b): return a + b def squigglebah(a, b): print(a + 2 * b) ooboo = pizkwat(1, 2) squigglebah(3, 4) int pizkwat(int a, int b) { return a + b; } void squigglebah(int a, int b) { cout << a + 2 * b << endl; } int ooboo = pizkwat(1, 2); squigglebah(3, 4); For Loops In Python, iterating over a range of numbers can be done using the for ... in loop. In C++, the syntax is a bit more involved: Python C++ for i in range(10): print(i) for (int i = 0; i < 10; i++) { cout << i << endl; } Conditionals The if and else keywords work basically the same way in C++ as they do in Python: Python C++ if myNumber == 137: print("Huzzah!") else: print("Alas!") if (myNumber == 137) { cout << "Huzzah!" << endl; } else { cout << "Alas!" << endl; } In C++, there is no elif keyword. Instead, write out else if as two words, like this: Python C++ if myNumber == 137: print("Yeehaw!") elif myNumber == 106: print("Golly gee!") else: print("Oh fiddlesticks.") if (myNumber == 137) { cout << "Yeehaw!" << endl; } else if (myNumber == 106) { cout << "Golly gee!" << endl; } else { cout << "Oh fiddlesticks." << endl; } In Python, you use and, not, and or to combine or modify predicates. While these keywords will technically work in C++, it's not considered good style to use them. Instead, use the (slightly more cryptic, but more standard) symbols && in place of and || in place of or ! in place of not For example: Python C++ if a == b and b == c: print("Norwuz") elif a == b or b == c: print("Rosh Hashanah") elif not predicate(a): print("Losar") else: print("Just a day.") if (a == b && b == c ) { cout << "Norwuz" << endl; } else if (a == b || b == c) { cout << "Rosh Hashanah" << endl; } else if (!predicate(a)) { cout << "Losar" << endl; } else { cout << "Just a day." << endl; } In Python, you can chain inequalities together. In C++, you cannot do this, and instead need to build multiple inequalities and use && to combine them together: Python C++ if 0 <= a < 10: print("One digit.") if (0 <= a && a < 10) { cout << "One digit." << endl; } Comments Python has single-line comments that start with #. In C++, we use // instead. Python C++ sporgle(quizbah) # Transform input sporgle(quizbah); // Transform input C++ also has multiline comments that can be used to describe a dense block of code. They begin with the sequence /* and end with */. For aesthetic reasons it's common to see each line of the comment starting with a star, but this isn't strictly necessary. Python C++ # Did you know that the ocean sunfish # is so large that, when a single # sunfish is accidentally caught by # a fishing boat, it can account for # about half the total catch? /* Did you know that the ocean sunfish * is so large that, when a single * sunfish is accidentally caught by * a fishing boat, it can account for * about half the total catch? */ Python uses docstrings to document what a function does inside the body of the function. In C++, the convention is to use a multiline comment before the body of the function: Python C++ def phchthshkh(o): """ This function name and argument name are terrible. They're just examples. """ return o * o /* This function name and argument * name are terrible. They're just * examples. */ int phchthshkh(double o) { return o * o; } Function Prototypes C++ (for the most part) uses a model called one-pass compilation. This means that the C++ compiler starts at the top of the program, reading downward, and only knows about functions that it's seen so far. As a result, if you want to call a function that you will eventually define but haven't yet gotten to writing, you need to include a prototype for that function at the top of the program. Python C++ def eclair(n): return croissant(n) def croissant(n): return n + 1 print(eclair(137)) int crossaint(int n); // Prototype int eclair(int n) { return croissant(n); } int croissant(int n) { return n + 1; } int main() { cout << eclair(137) << endl; return 0; } Strings and Characters C++ makes a distinction between strings and characters. A character (denoted by the type char) is a single glyph you can display on the screen. A string (denoted by the type string) is a sequence of zero or more characters. Anything in single quotes (e.g. 'a') is considered a char, while anything in doublequotes is considered a string (e.g. "a"). For example: Python C++ papyrus = 'a' # String of length 1 quill = "a" # String of length 1 quill = papyrus # Sure, no problem. papyrus = quill # Sure, no problem. char papyrus = 'a'; // Character string quill = "a"; // String quill = papyrus; // Error! Wrong types papyrus = quill; // Error! Wrong types Many string functions built into Python are not present in C++, but you can get the same functionality by using functions from the "strlib.h" header file. Python C++ text = "Pirate" if text.startswith("Pi"): print("A circle") if text.endswith("irate"): print("It's angry") if "ra" in text: print("Sun god!") if text.find("at") != -1: print("Preposition!") print(text.lower()) print(text.upper()) text = "a walk in the park" parts = text.split(' ') print(parts[0]) for part in parts: print(part) text = "137" value = int(text) text = str(value) #include "strlib.h" #include "vector.h" string text = "Pirate"; if (startsWith(text, "Pi")) { cout << "A circle!" << endl; } if (endsWith(text, "irate")) { cout << "It's angry!" << endl; } if (text.find("ra") != string::npos) { cout << "Sun god!" << endl; } if (text.find("at") != string::npos) { cout << "Preposition!" << endl; } cout << toLowerCase(text) << endl; cout << toUpperCase(text) << endl; text = "a walk in the park"; Vector<string> parts = stringSplit(text, " "); cout << parts[0] << endl; for (string part : parts) { cout << part << endl; } text = "137"; int value = stringToInt(text); text = to_string(value); Substrings Substrings in C++ work differently than in Python. In Python, the notation str[start:end] gives you a substring starting at position start and ending at position end. In C++, the function call str.substr(start, len) gives you a substring of length len starting just before position start. You can also use str.substr(start) to get a substring starting at position start and continuing to the end of the string. Negative indices are not allowed. Python C++ text = "appraising" # praising print(text[2:]) # raisin print(text[3:9]) print(text[3:-1]) string text = "appraising"; // praising cout << text.substr(2) << endl; // raisin cout << text.substr(3, 6) << endl; cout << text.substr(3, text.size() - 4) << endl;
http://cs106b.stanford.edu/assessments/refsheet.html
We often distribute a reference sheet during exams that gives the syntax of common operations so that students do not need to memorize these details. Below is a sample such reference sheet. You may want to have this on hand when practicing or taking an exam. This list isn't comprehensive - for that, read the header files (available in Qt Creator) or view the online docs for standard C++ functions or the Stanford C++ libraries. Standard string s.at(index) or s[index] // access single char s + text OR s += text s.substr(start) // return new substring s.substr(start, length) s.find(key) // returns index or string::npos s.erase(index, length) // erase, insert, replace destructively modify s s.insert(index, text) s.replace(index, length, text) for (char ch: s) { ... } // chars visited in order Stanford strlib.h if (equalsIgnoreCase(s1, s2)) { ... } if (endsWith(str, suffix)) { ... } if (startsWith(str, prefix)) { ... } if (stringContains(str, key) { ... } integerToString(int) stringToInteger(str) realToString(double) stringToReal(str) stringSplit(str, delimiter) // returns Vector<string> of tokens toLowerCase(str) toUpperCase(str) trim(str) // remove leading/trailing whitespace Vector Vector<T> v = {elem, elem, ... , elem}; v.add(elem) OR v += elem v.clear() v.get(index) OR v[index] v.insert(index, elem) if (v.isEmpty()) { ... } v.remove(index) v.set(index, elem) OR v[index] = elem v.size() for (T elem: v) { ... } // visited in order of index Grid Grid<T> g(nRows, nCols); int numCells = grid.numRows() * grid.numCols() if (grid.inBounds(row, col)) { ...} grid[row][col] = value for (T elem: grid) { ... } // visited left-to-right, top-to-bottom GridLocation GridLocation loc = {row, col}; grid[loc] = value if (grid.inBounds(loc)) { ... } GridLocation neighbor = {loc.row + 1, loc.col - 1}; if (loc == neighbor) { ... } Stack Stack<T> s = {bottom, ... , top}; s.push(elem) s.peek() // Look at top s.pop() // Removes top s.size() if (s.isEmpty()) { ... } s.clear() // no iterator, no direct access to elements other than top Queue Queue<T> q = {front, ... , back}; q.enqueue(elem) q.peek() // Look at front q.dequeue() // Removes front q.size() if (q.isEmpty()) { ... } q.clear() // no iterator, no direct access to elements other than front Set Set<T> s = {elem1, elem2, ... , elemN}; if (s.contains(elem)) { ... } s.add(elem) s + elem; s + set2; // union s.remove(elem) s - elem; s - set2; // difference intersectSet = s1 * s2; s.size() if (s.isEmpty()) { ... } if (s.isSubsetOf(s2)) { ... } s.clear() // Visits elements in sorted order for (T elem: set) { ... } Map Map<K, V> m = { { key1, val1}, ... {keyN, valN } }; m.put(key, value) OR m[key] = value m.get(key) OR m[key] // bracket form auto-inserts default value if not present if (m.containsKey(key)) { ... } m.size() if (m.isEmpty()) { ... } m.remove(key) m.clear() Vector<K> keys = m.keys(); // Visits keys in sorted order for (K key: m) { ... } Lexicon Lexicon lex(filename); if (lex.contains(word)) { ... } if (lex.containsPrefix(prefix)) { ... } PriorityQueue (Stanford library version) PriorityQueue<T> pq; pq.enqueue(value, priority) pq.peek() // Look at frontmost pq.peekPriority() // Look at priority of frontmost pq.dequeue() // Removes frontmost pq.changePriority(value, priority) pq.size() if (pq.isEmpty()) { ... } pq.clear() // no iterator, no direct access to elements other than front ListNode struct ListNode { int data; ListNode* next; // ListNode* prev; // if problem states that list is doubly-linked }; TreeNode struct TreeNode { int data; TreeNode* left; TreeNode* right; }; Huffman EncodingTreeNode struct EncodingTreeNode { char ch; EncodingTreeNode* zero; EncodingTreeNode* one; EncodingTreeNode(char c); // construct leaf node EncodingTreeNode(EncodingTreeNode* z, EncodingTreeNode* o); // construct interior node bool isLeaf(); char getChar(); };
http://cs106b.stanford.edu/schedule.html
This is a preview of our planned schedule. We will update this schedule as we go. This page should faithfully describe the past, but it won't always accurately predict the future. Lecture MWF 1:30-2:20pm in Hewlett 200, recordings posted to Canvas for later asynchronous viewing. 1 Sep 26 Welcome! Readings: Syllabus Sep 28 C++ Fundamentals Text Ch. 1, 2.1-2.4 Sep 30 C++ Review, Strings, and Testing Text 3.1-3.7; CS106B Testing Guide Assignment 0. due Sep 30 2 Oct 3 Vectors and Grids Text Reading: 2.5-2.9, 5.1, Class documentation for Vector and Grid Oct 5 Ordered Data Structures (Stacks and Queues) Text 5.1 - 5.3, Class documentation for Stack and Queue Oct 7 Unordered Data Structures (Sets and Maps) Text 5.4, 5.5, Class documentation for Set and Map Section 1. Assignment 1. due Oct 7 3 Oct 10 Introduction to Recursion Text 7.1-7.3 Oct 12 Big-O and Algorithmic Analysis Text 10.2 Oct 14 More Recursion and Fractals Text 7.4, 8.4 Section 2. Assignment 2. due Oct 14 4 Oct 17 Recursive Problem Solving Text 8.1, 8.2, 8.3 Oct 19 Recursive Backtracking and Enumeration Text 9.1-9.3 Oct 21 More Recursive Backtracking Text 9.1-9.3 Section 3. Assignment 3. due Oct 21 5 Oct 24 Recursive Backtracking Wrap Oct 26 Sorting Algorithms Text 10.1-10.5 Oct 28 Object-Oriented Programming Text 6.1-6.5 Section 4. Assignment 4. due Oct 28 6 Oct 31 Dynamic Memory Allocation and Arrays Text 11.1, 11.3, 12.1 Nov 2 Pointers and Structs Text 14.3,16.5 Nov 4 Priority Queues and Binary Heaps Text 14.3,16.5 Section 5. Mid-quarter exam 7 Nov 7 Katie Creel: Applied Ethics Nov 9 Memory and Pointers Text 11.1, 11.2, 11.4 Nov 11 Introduction to Linked Lists Text 12.2, 13.5 Section 6. Assignment 5. due Nov 11 8 Nov 14 More Linked Lists Text 14.2-14.4 Nov 16 Trees Text 16.1 Nov 18 Binary Search Trees Text 16.2 - 16.4 Section 7. Assignment 6. due Nov 18 9 Nov 21 Thanksgiving recess Nov 23 Thanksgiving recess Nov 25 Thanksgiving recess 10 Nov 28 Huffman Coding Nov 30 Graph Algorithms Dec 2 Hashing Text 15.3 Section 8. Assignment 7. due Dec 7 11 Dec 5 Wrap Dec 7 Ask-Me-Anything Dec 9 No class Section 9. 12 Dec 12 Final exam Dec 14 . Dec 16 . End-quarter exam
http://cs106b.stanford.edu/assignments/2-adt/searchengine.html
An introduction to search engines Search engines are one of the most influential developments of the modern internet age and have completely revolutionized how users interact with the web. What was once an intractable jumble of data with no semblance of organization has become a treasure trove of useful information due to the magic of search. Armed with the power of Map and Set, you can recreate this phenomenal achievement yourself. Once completed, you will understand the core technology underpinning Spotlight, Google, and Bing, and have solid practice in the use of the Map and Set ADTs. Want to see the power of search right now? Click Search in the top-right of the page navigation bar and search for easter egg to see what lurks deep in the recesses of the course website... In your search engine, each web page has a URL ("Uniform Resource Locator") that serves as its unique id and a string containing the body text of the page. You will first write functions that pre-process the body text and populate the data structure. Next you'll implement the function to search for pages matching a search query. Finally, you will write a console program that allows the user to enter many search queries and retrieve the matching web pages. Put all together, you will have built your own mini search engine! Understanding the web page data We have harvested the body text from the pages of our course website into a database file. The format of the database file is as follows: The lines of the file are grouped into pairs with the following structure: The first line of a pair is a page URL. The second line of a pair is the body text of that page, with all newlines removed (the entire text of the page in a single string). The first two lines in the file form the first pair. The third and fourth lines form another pair, the fifth and sixth another, and so on, with alternating lines of page URL and page content. To view an example database, open the file tiny.txt or website.txt in the folder Other files/res of the starter project. Using an inverted index for searching To make our search efficient, we need to be thoughtful about how we structure and store the data. A poor choice in data structures would make search painfully slow, but clever use of our wonderful ADTs can avoid that fate. A search engine typically uses a nifty arrangement known as an inverted index. An inverted index is akin to the typical index in the back of a book. If you look up the keyword "internet" in the index of the CS106B textbook, it lists two page numbers, 18 and 821. The word internet occurs on page number 18 and again on page number 821. Thus, an inverted index is a mapping from word to locations. You look up a keyword in an inverted index to find all the locations where that keyword occurs. In contrast a forward index operates in the other direction. For our book example, the forward index would be a mapping from a location (page number) to all the words that occur on that page. To build an inverted index, you typically start with the data in the form of a forward index and then process to convert to the inverted form. Inverting the index takes time up front, but once complete, the inverted index supports extremely fast operations to find query matches. On to the code! Decomposition is one of your best tools for tackling a complex problem. We'll guide you along by breaking the problem down into a sequence of steps. Follow our steps to success! All of the functions and tests for the search engine are to be implemented in the file search.cpp. 1) Write helper function cleanToken() Start by writing a helper function for this small but key string-processing task: string cleanToken(string token) cleanToken takes in a string token and returns a "cleaned" version of the token, ready to be stored in the index. To clean a token, extract only the letter and number characters and convert all letters to lowercase, i.e. "Wow!" becomes "wow", mid-quarter" becomes "midquarter", and "CS106B" becomes "cs106b". Standardizing on this simple canonical form alows our search queries to operate case-insensitively and ignore punctuation. You might want to start by reworking the code from the lettersOnly function you wrote for Soundex and change/extend as necessary. The return value from cleanToken is the cleaned lowercase version, or will be empty string if the token is to be discarded (i.e. contains no letter or number characters). Write test cases to confirm that cleanToken works correctly in all situations before moving on! You will make heavy use of cleanToken when building the inverted index and if this helper mishandles tokens, it will throw off your results and lead to sad times. It is definitely worth your time to confirm with a solid set of test cases before moving on. Remember to label your tests as STUDENT_TEST. 2) Write helper function gatherTokens() The helper function gatherTokens extracts the set of unique tokens from the body text. Set<string> gatherTokens(string bodytext) The argument to gatherTokens is a string containing the body text from a single web page. The function returns a Set of the unique cleaned tokens that appear in the body text. The function first tokenizes the body text - this means to separate the string into words using the space character as delimiter. Call the stringSplit function from the Stanford library for this task. Then call your cleanToken helper function on each token and store the cleaned tokens into a Set. No matter how many repeat occurrences of a given word are in the body text, repeatedly adding it to a set stores just the one copy, which makes this ADT ideal for gathering the unique tokens. Time to test! Add test cases that confirm the output from gatherTokens, so you will later be able to call on this function with confidence that it does its job correctly. 3) Create inverted index in buildIndex() The function buildIndex reads the content from the database file and processes it into the form of an inverted index. int buildIndex(string dbfile, Map<string, Set<string>>& index) The first argument to buildIndex is the name of the database file of the web page data, the second argument is the Map to be populated with data for the inverted index. The return value of buildIndex is the number of documents processed from the database file. Before starting to code, work through a small example on paper to ensure you understand the data structure you are trying to build. Open the res/tiny.txt database file and manually process the content to build the inverted index. Q8. Sketch the contents of the inverted index built from the res/tiny.txt database file. When you are ready to start writing code, read the previous section Understanding the web page data to review the format of the database file. Look at the code we provided for readMazeFile in maze.cpp for an example of C++ code to open a file and read the contents into a Vector of lines. Feel free to reuse that code for buildIndex. For each page, you will call gatherTokens to extract the set of unique tokens. For each token in the page, you record the match to the page's URL by adding to its entry in the inverted index. The index is of type Map<string, Set<string>>, this map associates each keyword with the set of the URLs where that word occurs. The function returns the count of web pages that were processed. Our starter code contains some provided tests to get you started; add student tests of your own to ensure your coverage is comprehensive. Use a TIME_OPERATION test to confirm your function operates in reasonable time. Building the inverted index should generally not take more than 5 seconds. 4) Search using findQueryMatches() Next up is to implement the function: Set<string> findQueryMatches(Map<string, Set<string>>& index, string query) The query string can either be a single search term or a compound sequence of multiple terms. A search term is a single word, and a sequence of search terms is multiple consecutive terms, each of which (besides the first one) may or may not be preceded by a modifier like + or - (see below for details). The same stringSplit function used to divide the body text into tokens will be used to divide the query sentence into search terms. When finding the matches for a given query, follow these rules: For a single search term, result is all web pages that contain that term. A sequence of terms is handled as a compound query, where the matches from the individual terms are synthesized into one combined result. A search term has a slightly altered meaning when the term is prefaced by certain modifiers: By default when not prefaced with a + or -, the matches are unioned across search terms. (any result matching either term is included) Hint: use Set operation unionWith If the user prefaces a search term with +, then matches for this term are intersected with the existing results. (results must match both terms) Hint: use the Set operation intersect If the user prefaces a search term with -, then matches for this term are removed from the existing result. (results must match one term without matching the other) Hint: use the Set operation difference The same token cleaning applied to the body text is also applied to query terms. Call your helper cleanToken to process each search term to strip punctuation and convert to lowercase before performing the search for matches. Note that searching is case-insensitive, that is, a search for "binky" returns the same results as "Binky" or "BINKY". Be sure to consider what implications this has for how you create and search the index. Here are some example queries and how they are interpreted quokka matches all pages containing the term "quokka" simple cheap means simple OR cheap matches pages that contain either "simple" or "cheap" or both tasty +healthy means tasty AND healthy matches pages that contain both "tasty" and "healthy" tasty -mushrooms means tasty WITHOUT mushrooms matches pages that contain "tasty" but do not contain "mushrooms" tasty -mushrooms simple +cheap means tasty WITHOUT mushrooms OR simple AND cheap matches pages that match ((("tasty" without "mushrooms") or "simple") and "cheap") There is no precedence for the operators, the query is simply processed from left to right. The matches for the first term are combined with matches for second, then combined with matches for third term and so on. In the last query shown above, the matches for tasty are first filtered to remove all pages containing mushrooms, then unioned with all matches for simple and lastly intersected with all matches for cheap. You may assume that the query sentence is well-formed, which means: The query sentence is non-empty and contains at least one search term If a search term has a modifier, it will be the first character A modifier will not appear on its own as a search term A + or - character within a search term that is not the first character is not considered a modifier The first search term in the query sentence will never have a modifier You can assume that no search term will clean to the empty string (i.e. has at least one letter) There is a lot of functionality to test in query processing, be sure you add an appropriate range of student tests to be sure you're catching all the cases. 5) Put it all together with searchEngine() Thus far, your amazing code has re-arranged a mass of unstructured text data into a highly-organized inverted index with instantaneous retrieval and fancy query-matching capability. Now take it over the finish line to build your own search engine! The final function to implement is: void searchEngine(string dbfile) This function implements a console program that works as follows: It first constructs an inverted index from the contents of the database file. It prints how many web pages were processed to build the index and how many distinct words were found across all pages. It enters a loop that prompts the user to enter a query For each query entered by the user, it find the matching pages and prints the URLs. When the user enters the empty string (""), this indicates they are done and the program finishes. After you have completed this function, your program should behave as shown in the transcript shown below. Example program run (executed by running searchEngine("res/website.txt") in main.cpp): Stand by while building index... Indexed 35 pages containing 4587 unique terms Enter query sentence (RETURN/ENTER to quit): llama Found 1 matching pages {"http://cs106b.stanford.edu/assignments/2-adt/searchengine.html"} Enter query sentence (RETURN/ENTER to quit): expect_error +testing Found 4 matching pages {"http://cs106b.stanford.edu/assignments/2-adt/maze.html", "http://cs106b.stanford.edu/assignments/2-adt/searchengine.html", "http://cs106b.stanford.edu/assignments/2-adt/warmup.html", "http://cs106b.stanford.edu/resources/testing_guide.html"} Enter query sentence (RETURN/ENTER to quit): cs107 stanford -qt Found 5 matching pages {"http://cs106b.stanford.edu/course_placement", "http://cs106b.stanford.edu/faq", "http://cs106b.stanford.edu/honor_code", "http://cs106b.stanford.edu/lair", "http://cs106b.stanford.edu/resources/style_guide.html"} Enter query sentence (RETURN/ENTER to quit): All done! Way to go, you're well on your way to becoming the next internet search pioneer! Notes The res folder of the starter project includes two database files: tiny.txt is the small example used in the writeup and website.txt is the body text extracted from all of the pages in our course website (as of Oct 7). You can open these files in Qt Creator to view their contents. The project resource files are listed under Other files -> res. Your program can open a resource file by specifying the path as "res/myfilename". References Inverted Index on GeeksForGeeks Wikipedia article on Inverted Indexes Stanford Natural Processing Group on Tokenization Extensions If you have completed the basic assignment requirements and and want to go further, we encourage you to try adding an extension! A non-exhaustive list of potential extensions is listed below: Weights When you get the results from a Google search, they are ranked so that the more relevant results are first on the list. The current Google algorithm is a well-kept trade secret (though it was originally the Page Rank algorithm, named for its creator, then-Stanford graduate student Larry Page), but a simple approach is to give higher rank to pages with more occurrences of the search term. For this extension, you would need to re-think how you create your index to include the number of matches. Phrase search The assignment does not allow a search for multi-word terms, such as section leader. Searching for phrases is not trivial, as you cannot simply keep a mapping of all possible phrases in the document. You could, however, keep track of where in each document a word is, and then use that information to determine if words in a phrase are next to each other in any particular document. Stop Words The English language has many, many words that show up in text but are not particularly important for content, such as the, and, if, a, etc. These words are called Stop Words, and it would make your index smaller if you removed such stop words from the index. Here is more info about stop words. Stemming In the current design, if a user searches for section they won't find matches for sections, even though pages that mention either might be a relevant match. Stemming is the process of reducing words to their base form, so that (for example) both section and sections would become, simply, section in the index. If you have other creative ideas for extensions, run them by the course staff, and we'd be happy to give you guidance!
http://cs106b.stanford.edu/assignments/1-cpp/soundex.html
For the Soundex search portion of the assignment you will: study a real-world algorithm used by the U.S. Census to encode the phonetic pronunciation of surnames. implement the algorithm, developing a function that can take surnames as input and produce phonetic encodings as output. implement a console program that allows users to input a surname and then find all matches in a database of Stanford surnames that have the same encoding. respond to a few reflective questions on the efficacy and limitations of this algorithm. The coding is mostly C++ string processing, along with a little bit of file reading and use of Vector. You'll also be practicing with use of decomposition and a test-as-you-go strategy. The two files you will be editing are soundex.cpp (for the code) and short_answer.txt (for responses to thought questions). Why phonetic name matching is useful One of the more pesky features of the English language is the lack of consistency between phonetics and spelling. Matching surnames can be vexing because many common surnames come in a variety of spellings and continue to change over time and distance as a result of incorrectly inputted data, cultural differences in spelling, and transliteration errors. Traditional string matching algorithms that use exact match or partial/overlap match perform poorly in this messy milieu of real world data. In contrast, the Soundex system groups names by phonetic structure to enable matching by pronunciation rather than literal character match. This makes tasks like tracking genealogy or searching for a spoken surname easier. Soundex was patented by Margaret O'Dell and Robert C. Russell in 1918, and the U.S. Census is a big consumer of Soundex along with genealogical researchers, directory assistance, and background investigators. The Soundex algorithm is a coded index based on the way a name sounds rather than the way it is spelled. Surnames that sound the same but are spelled differently, like "Vaska," "Vasque," and "Vussky," have the same code and are classified together. How Soundex codes are generated The Soundex algorithm operates on an input surname and converts the name into its Soundex code. A Soundex code is a four-character string in the form of an initial letter followed by three digits, such as Z452. The initial letter is the first letter of the surname, and the three digits are drawn from the sounds within the surname using the following algorithm: Extract only the letters from the surname, discarding all non-letters (no dashes, spaces, apostrophes, ...). Encode each letter as a digit using the table below. Digit represents the letters 0 A E I O U H W Y 1 B F P V 2 C G J K Q S X Z 3 D T 4 L 5 M N 6 R Coalesce adjacent duplicate digits from the code (e.g. 222025 becomes 2025). Replace the first digit of the code with the first letter of the original name, converting to uppercase. Discard any zeros from the code. Make the code exactly length 4 by padding with zeros or truncating the excess. Note that Soundex algorithm does not distinguish case in the input; letters can be lower, upper, or mixed case. The first character in the result code is always in upper case. To ensure you understand the construction, get a piece of scratch paper and manually compute a few names, such as "Curie" (C600) and "O'Conner" (O256). Q10. What is the Soundex code for "Angelou"? What is the code for your own surname? Decomposing the problem Your best strategy for approaching a complex algorithm like this is to decompose the problem into smaller, more manageable tasks and proceed step by step, testing as you go. Q11. Before writing any code, brainstorm your plan of attack and sketch how you might decompose the work into smaller tasks. Briefly describe your decomposition strategy. To get you started, we're going to walk you through what it might look like to decompose and implement the first step of the Soundex algorithm. Decomposition is important here because if you tried to implement a single function that accomplished the whole Soundex algorithm all in one go, you could end up with one big, unwieldy piece of code. However, if you break down the problem into a number of different steps, each corresponding to its own helper function, you can develop these helper functions one at a time and test each one as you go. For example, Step 1 of the Soundex algorithm could be implemented as a helper function that extracts only the letters from a string. The C++ library function isalpha will report whether a character is alphabetic (i.e. is a letter). Here is a starting point (provided for you in soundex.cpp): // WARNING: Code is buggy! Add test cases to identify which inputs are mishandled string lettersOnly(string s) { string result = charToString(s[0]); for (int i = 1; i < s.length(); i++) { if (isalpha(s[i])) { result += s[i]; } } return result; } Testing, testing, testing With this draft implementation of lettersOnly, the next step is to test it. Our starter code includes some provided tests to get you started. Run the program and when prompted, select the option from the menu for tests from soundex.cpp. When you run these provided tests, you will see that the tests for the lettersOnly function all pass. From this, you might conclude that the function is good to go. However, there is a problem lurking within the code that has yet to be uncovered! Review our provided test cases to see what sorts of inputs we tested, and, more importantly, what sorts of inputs we didn't test. Brainstorm what those missing cases are and then add them. Think about edge cases that could lurk in the extremes or cases that are uniquely distinguished from the provided tests, such as a completely empty string or a string composed of only non-letter characters. Add at least 1 new student test to expose the bug in the given implementation of lettersOnly. Your goal in writing tests is to enumerate a comprehensive set of tests that brings any bugs in the code to light so you can debug and fix them. Good tests are the key to writing robust software. A great developer is not only a great coder, but also a great tester. Debugging a failing test case Once you have added a test case that fails, use the debugger to get more information about how the function has gone wrong. Set a breakpoint within the code block that contains your test case. A good place to stop is on the line with the operation that you want to trace through, like so: Now run the tests while using the debugger. When you hit the breakpoint, single step through the call to lettersOnly while watching the debugger's variables pane to see how the values are changing. This "step-and-watch" approach is the same as you used in the Assignment 0 debugging tutorial. Using the debugger should help you find the bug -once you understand it, go ahead and fix it! Implementing the Soundex algorithm With your fix, you have a working helper function that implements the first of the Soundex algorithm steps. Now for each subsequent step of the algorithm (encode, coalesce duplicates, and so on), follow the same process: Identify the next small task of the algorithm to implement. Define a new helper function to accomplish that task. Write student tests that confirm the expected behavior of the new function. Fill in the code for your helper function, debugging as you go. Continue writing code and debugging until you have passed all the tests from the previous step. Rinse and repeat. Your eventual goal is to implement the function: string soundex(string s) The top-level function will use the helper functions that you have written and tested. In addition to the test cases for the individual helper functions, you will also need test cases for the top-level function. The starter code has a number of provided test cases to get you started, but you should also add tests of your own to ensure you have covered the full possible range of inputs. Developing a census search program The capstone of this part of the assignment is to build a program that emulates the way in which the U.S. Census uses the Soundex algorithm. This console program allows a user to perform a Soundex search on a database of surnames. Implement the following function prototype: void soundexSearch(string filepath) The one argument to soundexSearch is the name of a text file containing a database of names. The program then repeatedly allows the user to enter a surname to look up in the database. For each surname that is entered, the program calculates the Soundex code of the entered name and then finds all names in the database that have a matching Soundex code. Here are the steps for the program: Read a database of surnames from the specified text file. This step is provided for you in the starter code. The "database" is simply a Vector<string>. Prompt the user to enter a surname. The function getLine from "simpio.h" will be helpful here. Compute the Soundex code of the surname. Iterate over the Vector of names, compute Soundex code of each name, and gather a result Vector containing those surnames with a matching code. Print the matches in sorted order. The Vector has a handy sort() operation (you can use vec.sort() where vec is the name of your vector), and you can print a vector using the << operator, e.g. cout << vec << endl;. Please note that the sort() function sorts the vector in place and does not return a value. Repeat steps 2-5 until the user indicates that they are done. To run the Soundex search program, you will need to change the main() function from its previous use in the perfect number warmup. Open the file main.cpp and edit the main() to comment out the call to findPerfects and uncomment the call to soundexSearch. Now when you run the program and select no tests, the main() function will execute and enter the Soundex search console program. Below is the output from a sample run of the program. If you are able to match this sample output exactly, then you have successfully completed this part of the assignment! Read file res/surnames.txt, 28458 names found. Enter a surname (RETURN to quit): Zelenski Soundex code is Z452 Matches from database: {"Zelenski", "Zelnick", "Zelnik", "Zelnis", "Zielonka"} Enter a surname (RETURN to quit): hanrahan Soundex code is H565 Matches from database: {"Hammerman", "Haner-McAllister", "Hanrahan", "Honarmand"} Enter a surname (RETURN to quit): All done! Considering limitations of Soundex Note: Be sure you have watched this short video from Katie Creel, our CS Ethicist-in-Residence, giving some background context for the Soundex algorithm. You will need the content presented in this video to complete the ethical reasoning short answer questions below. A consistent theme of this class is that we want you to consider the ethical implications of the choice of algorithm. Like many of the algorithms we will encounter in CS106B, Soundex is used in real systems. For example, the U.S. Census has relied on a variation of the Soundex algorithm for almost a century. But just because it has been used in practice does not make it a perfect algorithm! Now that you understand how Soundex works, we want you to consider some of its limitations. Q12. Think about one or more examples of a class of names that the Soundex system might not work well for. Explain what this class of names is and why the system might incorrectly group them or mis-categorize one of the names. (Hint: You may find it useful to look over the other possible phonetic systems in Extending Your Phonetic Algorithm.) As you know, not all names can be represented using ASCII; many are more accurately represented using Unicode. However, ASCII is the default standard in C++ and many other programming languages and computer systems. The strings you've been working with in this assignment all use ASCII. Q13. Suppose you are a software engineer working for the U.S. government. You have been tasked with implementing a system that collects names as part of the Census survey and analyzes these names using a phonetic algorithm. Your boss suggests using Soundex and notes that the algorithm is only expected to work for ASCII-encoded strings, since supporting Unicode would require extra work and time. What would your response be and why? What representational harms might result from building a system that exclusively uses Soundex and/or ASCII? Additional advice for testing Testing is key for implementing this problem. Make sure that you take advantage of the SimpleTest testing framework! If a test is failing, put a breakpoint inside the failing test, and step through the code line by line using the debugger. Keep an eye on the variables pane -that will be your most helpful resource to figure out what is going wrong! The starter project includes a text file res/surnames.txt containing the surnames of Stanford students, faculty, and staff for the use of Soundex search. There are some 28,000 unique surnames within the Stanford community, wow! For early testing, you may want to change the program to read names from the file res/small.txt instead, which contains a tiny set of names that is easier to debug on. All of the input strings we will use to test your soundex function are guaranteed to contain at least one letter character. This means you do not have to make a special case in soundex to handle a string that doesn't any letters at all (e.g. no empty string or string containing only digits or punctuation). Useful resources On the Qt Creator window, there is search field in the lower left labeled with a magnifying glass icon. If you enter the name of a header file (e.g. strlib.h or cctype), Qt will display its contents. This is a quick way to review the features of a given library. You can also browse/search all library documentation online: Stanford C++ Library Documentation C++ Standard Library Documentation For this assignment, you will be using library functions that operate on strings and characters, in particular: The standard library string functions length, substr, concat, replace, etc. will be useful, along with the Stanford-specific string functions in strlib.h. For case conversions, you can use the functions in cctype to convert a single char or string functions in strlib.h to convert an entire string. Remember that C++ treats individual characters differently than strings of characters. Individual characters have type char and are enclosed in single quotes (i.e. 'a' not "a"). Strings have type string and enclose a sequence of characters in double-quotes (i.e. "hello" not 'hello'). There are helpful functions in strlib.h to aid you with converting between the two, including the charToString and stringToChar functions. In a similar vein, the integer 5 is a distinct beast from the digit character '5' or the string "5". Take care to express the type carefully to match your intentions. Our guide to C++ strings CS106B has a library of useful functions and collection classes including Vector, Set, Map and others, that are specifically designed for our ecosystem. All of these functions and classes are available for you to use. Bookmark the library documentation and keep it handy. You may have heard of similar collection classes provided in the C++ Standard Template Library (STL). Do not use the STL versions in CS106B. As a helpful mnemonic to distinguish between the two, our class names are always capitalized (Queue) whereas STL class names are all lowercase (queue). Stick to the capitalized ones! Our guide to Transitioning from Python to C++ Textbook Soundex references https://www.archives.gov/research/census/soundex Online Soundex code calculator: https://www.functions-online.com/soundex.html Extending your phonetic algorithm If you have completed the assignment and want to go further, we encourage you to try working on an extension! There are many other phonetic systems out there besides Soundex. Here is a non-extensive list: Daitch-Mokotoff Beider-Morse Metaphone New York State Identification and Intelligence System Try implementing one of these other systems and see if you can get better or more intuitive surname matches! When implementing an extension, add a new .cpp file to your project that contains the extension code, keeping it separate from the regular Soundex implementation. If you have other creative ideas for extensions, run them by the course staff, and we'd be happy to give you guidance!
http://cs106b.stanford.edu/resources/style_guide.html
You may think the motivation for good style is earning that from your section leader, but the most important beneficiary of your efforts is you yourself. Committing yourself to writing tidy, well-structured code from the start sets you up for good times to come. Your code will be easier to test, will have fewer bugs, and what bugs there are will be more isolated and easier to track down. You finish faster, your results are better, and your life is more pleasant. What's not to like? The guidelines below identify some of style qualities we will be looking for when grading your programs. As with any complex activity, there is no one "best" style, nor a definitive checklist that covers every situation. That said, there are better and worse choices and we want to guide you toward the better choices. In grading, we will expect that you make a concerted effort to follow these practices. While it is possible to write code that violates these guidelines and yet still exhibits good style, we recommend that you adopt our habits for practical purposes of grading. If you have theoretical points of disagreement, come hash that out with us in person. In most professional work environments you are expected to follow that company's style standards. Learning to carefully obey a style guide, and writing code with a group of other developers where the style is consistent among them, are valuable job skills. This guide gives our general philosophy and priorities, but even more valuable will be the guidance on your own particular style choices. Interactive grading with your section leader is your chance to receive one-on-one feedback, ask questions, and learn about areas for improvement. Don't miss out on this opportunity! Layout, Indentation, and Whitespace Indentation: Consistent use of whitespace/indentation always! Proper whitespace/indentation illuminates the structure of your program and makes it easier to follow the code and find errors. Increment indent level on each opening brace {, and decrement on each closing brace }. Chose an increment of 2-4 spaces per indent level. Be consistent. Do not place more than one statement on the same line. // confusing, hard to follow while (x < y) { if (x != 0) { binky(x); } else { winky(y); y--; }} return x; // indentation follows structure while (x < y) { if (x != 0) { binky(x); } else { winky(y); y--; } } return x; Long lines: When any line is longer than 100 characters, break it into two lines. Indent the overflow text to align with text above. // too long, can't see all result = longFunctionName(argument, 106 * expression * variable) + variable - longerFunctionName() + otherFunction(variable); // nicely reformatted result = longFunctionName(argument, 106 * expression * variable) + variable - longerFunctionName() + otherFunction(variable); Blank lines: Use blank lines to separate functions and logical groups of statements within a function. Whitespace: Add space between operators and their operands. Add parentheses to show grouping where precedence might be unclear to reader. // hard for reader to parse int root = (-b+sqrt(b*b-4*a*c))/2*a; // ah... breathing room! int root = (-b + sqrt((b * b) - (4 * a * c))) / (2 * a); Names Choose meaningful identifiers. This reduce the cognitive load for reader and self-documents the purpose of each variable and function. Nouns for variable names: For variables, the question is "What is it?" Use a noun (name,scores) add modifier to clarify (courseName, maxScore). Do not repeat the variable type in its name (not titleString, just title). Avoid one-letter names like a or p (exceptions for loop counters i, j or, coordinates x and y). Never name a variable l, much too easily confused with the number one. Verbs for function names: For functions, the question is "What does it do?" Functions which perform actions are best identified by verbs ( findSmallest, stripPunctuation, drawTriangle). Functions used primarily for their return value are named according to property being returned (isPrime, getAge). Use named constants: Avoid sprinkling magic numbers throughout your code. Instead declare a named const value and use where that value is needed. This aids readability and gives one place to edit value when needed. const int VOTING_AGE = 18; Capitalization: Use camel-case for names of functions and variables (countPixels), capitalize names of classes/types (GridLocation), and uppercase names of constants (MAX_WIDTH). Conventions allows reader to quickly determine which category a given identifier belongs to. Variable scope Scope: Declare variables in the narrowest possible scope. For example, if a variable is used only inside a loop, declare it inside the scope for the loop body rather than at the top of the function or at the top of the file. Don't reuse same name in inner scope: Declaring a variable in inner scope with same name as a variable in outer scope will cause inner use of name to "shadow" the outer definition. Not only is this confusing, it often leads to difficult bugs. No global variables: Do not declare variables at global scope. When there is information to be shared across function calls, it should flow into and out via parameters and return values, not reach out and access global state. Use of C++ language features Stanford library structures over STL structures: You should be using the data structures and other libraries included in the Stanford library instead of the ones included in the C++ standard template library (i.e. use Vector instead of vector) Prefer C++ idioms over C idioms: Since C++ is based on C, there is often a "C++ way" to do a given task and also a "C way". For example, the "C++ way" to print output is via the output stream cout, while the "C way" is using printf. C++ strings use the string class, older code uses the C-style char*. Prefer the modern C++ way. // old school char* str = "My message"; printf("%s\n", str); // modern and hip string str = "My message"; cout << str << endl; for vs while: Use a for loop when the number of repetitions is known (definite); use a while loop when the number of repetitions is unknown (indefinite). // loop exactly n times for (int i = 0; i < n; i++) { ... } // loop until there are no more lines string str; while (input >> str) { ... } break and continue in loops: Wherever possible, a loop should be structured in the ordinary way with clear loop start, stop, advance and no disruptive loop control. That said, there are limited uses of break that are okay, such as loop-and-a-half (while(true) with break) or need to exit loop mid-iteration. Use of continue is quite rare and often confusing to reader, better to avoid. Use of fallthrough in switch cases: A switch case should almost always end with a break or return that prevents continuing into the subsequent case. In the very rare case that you intend to fallthrough, add a comment to make that clear. Accidental fallthrough is the source of many a difficult bug. switch (val) { case 1: handleOne(); break; case 2: handleTwo(); // HEADS UP: fallthrough from case 2 into case 3 case 3: handleTwoOrThree(); return statements Although it is allowed for a function to have multiple return statements, in most situations it is preferable to funnel through a single return statement at the end of the function. An early return can be a clean option for a recursive base case or error handled at the beginning of a function. return can also serve as a loop exit. However, scattering other return throughout the function is not a good idea- experience shows they are responsible for a disproportionate number of bugs. It is easy to overlook the early-return case and mistakenly assume the function runs all the way to its end. Always include {} on control statements: The body an if/else, for, while, etc., should always be wrapped in {} and have proper line breaks, even if the body is only a single line. Using braces prevents accidents like the one shown below. // ugh if (count == 0) error("not found"); for (int i = 0; i < n; i++) draw(i); if (condition) doFirst(); doSecond(); // inside? Indent looks so, but no braces! // better if (count == 0) { error("not found"); } for (int i = 0; i < n; i++) { draw(i); } if (condition) { doFirst(); doSecond(); } Booleans: Boolean expressions are prone to redundant/awkward constructions. Prefer concise and direct alternatives. A boolean value is true or false, you do not need to further compare to true/false or convert a boolean expression to true/false. if (isWall == true) { ... } if (matches > 0) { return true; } else { return false; } // better if (isWall) { ... } return (matches > 0); Favor &&, ||, and ! over and, or, and not: For various reasons mostly related to international compatibility, C++ has two ways of representing the logical connectives AND, OR, and NOT. Traditionally, the operators &&, ||, and ! are used for AND, OR, and NOT, respectively, and the operators are the preferred ways of expressing compound booleans. The words and, or, and not can be used instead, but it would be highly unusual to do so and a bit jarring for C++ programmers used to the traditional operators. // non-standard if ((even and positive) or not zero) { ... } // preferred if ((even && positive) || !zero) { ... } Use error to report fatal conditions: The error function from the Stanford library can be used to report a fatal error with your custom message. The use of error is preferred over throwing a raw C++ exception because it plays nicely with the debugger and our SimpleTest framework. // raw exception if (arg < 0) { throw arg; } // preferred if (arg < 0) { error("arg must be positive!"); } Efficiency In CS106B, we value efficient choices in data structure and algorithms especially where there is significant payoff, but are not keen on micro-optimizations that serve to clutter the code for little gain. Better BigO class: Given a choice of options for implementing an algorithm, the preference is generally for the one with better Big O, i.e. an O(NlogN) algorithm is preferable to quadratic O(N^2), constant O(1) or logarithmic O(logN) beats out linear O(N). Choose best performing ADT for situation: For example, if you need to do many lookup operations on collection, Set would be preferable to Vector because of efficient contains operation. All Stack/Queue operations are O(1) making Stack an ideal choice if you only add/remove at top or Queue perfect if you remove from head and add at tail. There is a small win for choosing HashSet/HashMap over Set/Map when you do not require access to elements in sorted order. Save expensive call result and re-use: If you are calling an expensive function and using its result multiple times, save that result in a variable rather than having to call the function multiple times. This optimization is especially valuable inside a loop body. // computes search twice if (reallySlowSearch(term) >= 0) { remove(reallySlowSearch(term)); } // avoid recompute int index = reallySlowSearch(term); if (index >= 0) { remove(index); } Avoid copying large objects: When passing an object as a parameter or returning an object from a function, the entire object must be copied. Copying large objects, such as collection ADTs, can be expensive. Pass the object by reference avoid this expense. The client and the function then share access to the single instance. // slow because parameter is copied void process(Set<string> data) { ... } Vector<int> fillVector() { Vector<int> v; // add data to v ... return v; // makes copy } // pass by reference for efficiency void process(Set<string>& data) { ... } // shares vector without making copy void fillVector(Vector<int>& v) { // add data to v ... } Unify common code, avoid redundancy When drafting code, you may find that you repeat yourself or copy/paste blocks of code when you need to repeately perform the same/similar tasks. Unifying that repeated code into one passage simplifies your design and means only one piece of code to write, test, debug, update, and comment. Decompose to helper function: Extract common code and move to helper function. // repeated code if (g.inBounds(left) && g[left] && left != g[0][0] ) { return true; } else if g.inBounds(right) && g[right] && right != g[0][0] ) { return true; } // unify common into helper bool isViable(GridLocation loc, Grid<bool>& g) { return g.inBounds(loc) && g[loc] && loc != g[0][0]); } ... return isViable(left, g) || isViable(right, g); Factoring out common code: Factor out common code from different cases of a chained if-else or switch. // repeated code if (tool == CIRCLE) { setColor("black"); drawCircle(); waitForClick(); } else if (tool == SQUARE) { setColor("black"); drawSquare(); waitForClick(); } else if (tool == LINE) { setColor("black"); drawLine(); waitForClick(); } // factor out common setColor("black"); if (tool == CIRCLE) { drawCircle(); } else if (tool == SQUARE) { drawSquare(); } else if (tool == LINE) { drawLine(); } waitForClick(); Function design A well-designed function exhibits properties such as the following: Performs a single independent, coherent task. Does not do too large a share of the work. Is not unnecessarily entangled with other functions. Uses parameters for flexibility/re-use (rather that one-task tool). Clear relationship between information in (parameters) and out (return value) Function structure: An overly long function (say more than 20-30 lines) is unwieldy and should be decomposed into smaller sub-functions. If you try to describe the function's purpose and find yourself using the word "and" a lot, that probably means the function does too many things and should be subdivided. Value vs. reference parameters: Use reference parameters when need to modify value of parameter passed in, or to send information out from a function. Don't use reference parameters when it is not necessary or beneficial. Notice that a, b, and c are not reference parameters in the following function because they don't need to be. /* * Solves a quadratic equation ax^2 + bx + c = 0, * storing the results in output parameters root1 and root2. * Assumes that the given equation has two real roots. */ void quadratic(double a, double b, double c, double& root1, double& root2) { double discr = sqrt((b * b) -(4 * a * c); root1 = (-b + discr) / (2 * a); root2 = (-b - discr) / (2 * a); } Prefer return value over reference 'out' parameter for single value return: If a single value needs to be sent back from a function, it is cleaner to do with return value than a reference out parameter. // harder to follow void max(int a, int b, int& result) { if (a > b) { result = a; } else { result = b; } } // better as int max(int a, int b) { if (a > b) { return a; } else { return b; } } Avoid "chaining" calls, where many functions call each other in a chain without ever returning to main. Here is a diagram of call flow with (left) and without (right) chaining: // chained control flow main | +-- doGame | +-- initAndPlay | +-- configureAndPlay | +-- readCubes | +-- playGame | +-- doOneTurn // better structured as main | +-- welcome | +-- initializeGame | | | +-- configureBoard | | | +-- readCubes | +-- playGame | | | +-- doOneTurn Commenting Some of the best documentation comes from giving types, variables, functions, etc. meaningful names to begin and using straightforward and clear algorithms so the code speaks for itself. Certainly you will need comments where things get complex but don't bother writing a large number of low-content comments to explain self-evident code. The audience for all commenting is a C++-literate programmer. Therefore you should not explain the workings of C++ or basic programming techniques. Some programmers like to comment before writing any code, as it helps them establish what the program is going to do or how each function will be used. Others choose to comment at the end, now that all has been revealed. Some choose a combination of the two, commenting some at the beginning, some along the way, some at the end. You can decide what works best for you. But do watch that your final comments do match your final result. It's particularly unhelpful if the comment says one thing but the code does another thing. It's easy for such inconsistencies to creep in the course of developing and changing a function. Be careful to give your comments a once-over at the end to make sure they are still accurate to the final version of the program. File/class header: Each file should have an overview comment describing that file's purpose. For an assignment, this header should include your name, course/section, and a brief description of this file's relationship to the assignment. Citing sources: If your code was materially influenced by consulting an external resource (web page, book, another person, etc.), the source must be cited. Add citations in a comment at the top of the file. Be explicit about what assistance was received and how/where it influenced your code. Function header: Each function should have a header comment that describes the function's behavior at a high level, as well as information about: Parameters/return: Give type and purpose of each parameter going into function and type and purpose of return value. Preconditions/assumptions: Constraints/expectations that client should be aware of.("this function expects the file to be open for reading"). Errors: List any special cases or error conditions the function handles (e.g. "...raises error if divisor is 0", or "...returns the constant NOT_FOUND if the word doesn't exist"). Inline comments: Inline comments should be used sparingly where code complex or unusual enough to warrant such explanation. A good rule of thumb is: explain what the code accomplishes rather than repeat what the code says. If what the code accomplishes is obvious, then don't bother. // inline babbling just repeats what code already says, please don't! int counter; // declare a counter variable counter++; // increment counter while (index < length) // while index less than length TODOs: Remove any // TODO: comments from a program before turning it in. Commented-out code: It is considered bad style to submit a program with large chunks of code "commented out". It's fine to comment out code as you are working on a program, but if the program is done and such code is not needed, just remove it. Doc comments: You can use "doc" style (/** ... */) comment style if you like, but it is not required for this class. A style of commenting using // or /* ... */ is just fine.
http://cs106b.stanford.edu/resources/submit_checklist.html
This handy checklist is designed to help confirm your code is fully ready to go before you submit it. Before you send it in for grading, take a few minutes to work through this checklist. Functionality I've re-read the assignment writeup and verified my program matches the required specification. I've reviewed my code and understand each line of code I've written, why it's there, and why it's necessary. Test cases I've run all of the provided test cases against my code and resolved any test failures. I've supplemented the provided test cases with student tests of my own. For each part of the assignment, I've written at least one test case to make sure it works well in the common case. For each part of the assignment, I've thought of at least one edge case that could cause problems and written a student test for it. (And ideally, I've done this for multiple edge cases!) Style I've read the CS106B Style Guide at least once in its entirety and asked questions about any parts I don't understand. My code follows the guidelines from the Style Guide. My code meets the expectations for readability, decomposition, and program design and adheres to restrictions such as no global variables, no use of obscure language features, and so on. Each of my functions, conceptually, performs a single task. All of my variables and functions use clear and descriptive names. The dense parts of my code are commented, and those comments describe what the code accomplishes rather than restating the logic in plain English. Every function I've written has a comment preceding it that explains what the function does, what its parameters are, and what its return value (if any) means. Each file I am submitting has a header overview comment that clearly identifies the code authorship, including any necessary [citations][citation]. Final Submission I've auto-indented my code in Qt Creator. The final layout is clean and readable. I've completed the tasks from all TODO comments in the starter code and removed those comments. I've removed all print statements left in the code that I used for debugging purposes. I've removed all commented-out blocks of code that are no longer necessary. I've read the Submit section of the assignment handout and confirmed that I'm submitting all of the required files.
http://cs106b.stanford.edu/syllabus
Hi there and welcome to CS106B! CS106B Programming Abstractions is the second course in our introductory programming sequence. The prerequisite CS106A establishes a solid foundation in programming methodology and problem-solving in Python. With that under your belt, CS106B will acquaint you with the C++ programming language and introduce advanced programming techniques such as recursion, algorithm analysis, data abstraction, explore classic data structures and algorithms, and give you practice applying these tools to solving complex problems. We're excited to share this great material with you and have a superb team of section leaders that will support you through the challenges to come. We hope you will find the time worth your investment and that you enjoy your growing mastery of the art of programming! Teaching Team Cynthia Julie Neel Our wonderful undergraduate section leaders lead sections and staff LaIR helper hours. I) Course Essentials The central place for CS106B resources is the course website right here at https://cs106b.stanford.edu. The website is your go-to for lecture slides, assignments, sections, and other information, including the most up-to-the-date information and announcements. Lectures will be recorded and posted on the "Course Videos" tab of the course Canvas page. We use the course Canvas page only to distribute videos - all other material is published on the course website. We hope to build a lively online community on our Ed Discussion forum. You are encourage to use the forum to ask questions about lecture, section, assignments, and course logistics. Assignment submissions, feedback, and grading use the CS198 Paperless website. Here students submit assignments, view graded assignments, and sign up for LaIR (helper hours). II) Course Topics Learning Goals After completing CS106B, we hope you will have achieved the following learning goals: I am excited to use programming to solve real-world problems I encounter outside class. I recognize and understand common abstractions in computer science. I can identify programmatic concepts present in everyday technologies because I understand how computers process and organize information. I can break down complex problems into smaller subproblems by applying my algorithmic reasoning and recursive problem-solving skills. I can evaluate design tradeoffs when creating data structures and algorithms or utilizing them to implement technological solutions. We'll also be giving you tools to tackle the following questions (note that these don't have single right or wrong answers!): What is possible with technology and code? What isn't possible? How can I use programming to solve problems that I otherwise would not be able to? What makes for a "good" algorithm or data structure? Why? Which problems should I solve with algorithms and data structures? What does a responsible programmer do when using data about real people? Lecture Schedule While the below schedule is subject to change over the course of the quarter, we will cover the following topics (in approximate order): C++ basics Abstract data structures Recursion Classes and object-oriented programming Memory management and implementation-level abstractions Linked data structures Advanced algorithms Prerequisites The prerequisite for CS106B is completion of CS106A and readiness to move on to advanced programming topics. A comparable introductory programming course or experience (including high school AP courses) is often a reasonable substitute for Stanford's CS106A. If you are unsure if this course is the right for you, read more about course placement. III) Course Structure Units If you are an undergraduate, you must enroll in CS106B for 5 units (this is by department and university policy, no exceptions). If you are a graduate student, you may enroll in CS106B for 3 or 4 units to reduce your units for administrative reasons. Taking the course for reduced units has no change on the course workload. Lectures Lectures will take place on Monday, Wednesday, and Friday from 1:30-2:20pm in Hewlett 200. Lecture recordings will be available later on Canvas for future review. All students are expected attend lectures in person if at all feasible (i.e., not if you are sick or an SCPD remote student), in order to fully participate in class discussions and other synchronous activities. We understand that this isn't possible for every student every time, so if you are unable to attend in person we ask that you at least watch the video of class before the next class, so you are up to speed on all course topics and important announcements. We think staying on pace with the class in this way is so important (and we know you know it too-binge-watching videos just isn't it!) that we are offering a small incentive to help you motivate yourself to stay on top of it: 5% of the course grade is allocated for lecture participation. If you show up to a lecture in person, congratulations, you earned that day's credit! For other options, read more at about lectures. Note about recording consent: Video cameras located in the back of the room will capture the instructor presentations during lecture. These recordings might be reused in other Stanford courses, viewed by other Stanford students, faculty, or staff, or used for other education and research purposes. While the cameras are positioned with the intention of recording only the instructor, occasionally a part of your image or voice might be incidentally captured. If you have questions, please contact a member of the teaching team. Sections Each students is assigned to a weekly small group discussion section, led by an undergraduate section leader. Your section leader is your mentor, grader, and personal connection to the greater CS106B course staff. Sections begin the second week of classes, and attendance and participation are mandatory for all students. Your section leader will evaluate your section participation; this contributes to your course grade. Read more about section. Assignments There will be regular assignments, about one per week. An assignment may include written problems, hands-on exercises with the tools, coding tasks and/or a larger complete program. Assignments are to be completed individually. Programs are graded on "functionality" (is the program's behavior correct?) and "style" (is the code well-written and designed cleanly?). We use a bucket grading scale to focus attention on the qualitative rather than quantitative feedback. Read more about assignments and assignment grading. The course late policy has been designed to build in flexibility. Assignments submitted by the due date earn a small on-time bonus. After the due date, there is a "grace period" (typically 48 hours) where we will accept late submissions without penalty. Read more about the late policy. Assessments There will be exams at mid-quarter and end-quarter. The mid-quarter is a check-in to assess your understanding of core topics covered in the first half of the course and help you chart a path forward. The final is a comprehensive assessment of your mastery of the course learning goals. Mark these dates in your calendar now! Midterm: 7-9pm on Tuesday, November 1, 2022 Final: 8:30-11:30am on Monday, December 12, 2022 We will have more information about these assessments as the quarter progresses. The primary intention of the assessments it to allow you to demonstrate what you've learned in the class. Read more about assessments. Course Grades Final grades for the course will be determined using the following weights: 55% Programming assignments 15% Mid-quarter exam 20% End-quarter final exam 5% Section participation 5% Lecture participation We will compute your course grade once including lecture participation and again without (moving that weight to final exam). The weighting that results in the better outcome for you is the one we will use. Incompletes The university "I" grade ("incomplete") is appropriate for circumstances of significant personal or family emergency disruption that prevent a student from finishing course requirements on schedule. To be considered for an incomplete, you must have completed all assignments at a passing level up at the point of your request for an incomplete. You must have an extenuating circumstance that warrants an extension of time beyond the end of the quarter to complete the remaining work. Approval for an incomplete is at the instructors' discretion. Incompletes are not be considered for reasons such as poor performance in the course or over-commitment. IV) Course Resources Textbook Roberts, Eric. Programming Abstractions in C++. ISBN 978-0133454840. You can find different options to access the textbook here . Recommended readings for each lecture will be posted on our lecture schedule. Software The official CS106 programming environment is Qt Creator, which is an editor bundled with C++ compiler and libraries. The software runs on Windows, Mac, and Linux and is free for personal/student use. The Qt Installation Guide has instructions for installing the tools onto your computer. Getting help We want to enable everyone to succeed in this course and offer different paths to help. The instructors and Head TA will hold weekly office hours. The section leaders staff LaIR helper hours. The CS106B Ed Discussion forum allows public Q&A and discussion with your peers. Here is the Quick Start Guide to using Ed. Accommodations Students who need an academic accommodation based on the impact of a disability should initiate a request with the Office of Accessible Education. Professional staff will evaluate the request with required documentation, recommend reasonable accommodations, and prepare an Accommodation Letter dated in the current quarter. Students should contact the OAE as soon as possible since timely notice is needed to coordinate accommodations. The OAE has contact information on their web page: http://oae.stanford.edu. Once you obtain your OAE letter, please send it to the head TA. V) Honor Code As a student taking a Stanford course, you agree to abide by the Stanford Honor Code, and we expect you to read over and follow our specific CS106B Honor Code policy. The work you submit for grading must be your own original, independent effort and must not be based on, guided by, or jointly developed with the work of others. The CS department employs powerful automated plagiarism detection tools that compare assignment submissions with other submissions from the current and previous quarters, as well as related online resources. The tools also analyze your intermediate work, and we will run the tools on every assignment you submit. The vast majority of you are here to learn and will do honest work for an honest grade. We celebrate and honor your commitment. Because it's important that all cases of academic dishonesty are identified for the sake of those playing by the rules, we will refer all cases of concern to the Office of Community Standards.
http://cs106b.stanford.edu/resources/testing_guide.html
Why testing? Anybody that writes code for some purpose (whether as a researcher, a software engineer, or in any other profession) will get to the point where others are relying on their code. Bugs in software can be dangerous or even deadly. Additionally, users do not enjoy using software that is buggy and crashes, and fixing bugs once the software is in production is very costly. Most importantly, good engineers take pride in building things that work well and are robust. The key to writing working software is developing good tests. In this course we follow an approach called test-driven development. As you write code, you will also write companion tests. These tests are used to verify that the code you just finished writing works as intended. This strategy is sometimes called "test-as-you-go." You work in small steps, being sure to test thoroughly, and only move on after you having confirmed the correctness and fixed all issues. The beauty of this approach is that each step is relatively straightforward and easy to debug. Imagine the opposite approach: you write hundreds of lines of code, the code does not work, and now you need to figure out which one of those hundreds of lines of code isn't working as expected! That is the sort of frustration that we want to help you all avoid as you continue to develop your skills as programmers. SimpleTest For CS106B, we provide a unit-test framework called SimpleTest that you will use to test your code. This framework was pioneered by our ace colleague Keith Schwarz. SimpleTest provides a simple, clean approach to writing and running test cases. Here is an example of how you might see the SimpleTest framework used in the starter code of an assignment. // reversed(str) returns copy of str with characters in reverse order. string reversed(string s) { string result; for (int i = s.length() - 1; i >= 0; i--) { result += s[i]; } return result; } /* * * * * * Test Cases * * * * * */ PROVIDED_TEST("Demonstrate different SimpleTest use cases") { EXPECT_EQUAL(reversed("but"), "tub"); EXPECT_EQUAL(reversed("stanford"), "drofnats"); } When we provide tests for you in the starter code, each test case is wrapped in the special macro PROVIDED_TEST. The string argument in parentheses describes the purpose of the test, and the code block that follows (enclosed in curly braces) defines the actual test behavior. When you add your own test cases, you will wrap your test code blocks in the STUDENT_TEST macro instead. The STUDENT_TEST functionality and structure are exactly the same as PROVIDED_TEST; it simply distinguishes the tests you've written yourself from those we provide for the benefit of your grader. You will see many examples of this in the following sections. EXPECT_EQUAL The test macro EXPECT_EQUAL(your_result, expected_result) tests whether your result matches the expected. A typical use for EXPECT_EQUAL compares a value produced by your code (e.g. the return value from a call to one of your functions) to the expected result and confirms they are equal. As an example, consider the first test case from the code above: EXPECT_EQUAL(reversed("but"), "tub"); This test case compares the result of the call reversed("but") to the expected answer "tub". If the two are indeed equal, the test will be reported as Correct. If they do not match, the test is reported as a failure. See below the added STUDENT_TEST code block with three tests of your own. These test cases use EXPECT_EQUAL to try out further scenarios not covered by the provided tests. /* * * * * * Test Cases * * * * * */ PROVIDED_TEST("Demonstrate different SimpleTest use cases") { EXPECT_EQUAL(reversed("but"), "tub"); EXPECT_EQUAL(reversed("stanford"), "drofnats"); } STUDENT_TEST("my added cases not covered by the provided tests") { EXPECT_EQUAL(reversed("racecar"), "racecar"); EXPECT_EQUAL(reversed(""), ""); EXPECT_EQUAL(reversed("123456789"), "987654321"); } Important note: You should never modify the provided tests -these are the same tests that will be used for grading, so it is not in your best interest to modify them. When adding tests, put them in a new STUDENT_TEST block of your own. EXPECT The EXPECT(expression) test case confirms the truth of a single expression. If the expression evaluates to true, the test is reported as Correct. If false, it reports a test failure. For example, if you added the isPalindrome function to the above program, you could add a test case that uses EXPECT to confirm the correct result from isPalidrome, as shown below. // reversed(str) returns copy of str with characters in reverse order. string reversed(string s) { string result; for (int i = s.length() - 1; i >= 0; i--) { result += s[i]; } return result; } bool isPalindrome(string s) { return s == reversed(s); } /* * * * * * Test Cases * * * * * */ PROVIDED_TEST("Demonstrate different SimpleTest use case") { EXPECT_EQUAL(reversed("but"), "tub"); EXPECT_EQUAL(reversed("stanford"), "drofnats"); } STUDENT_TEST("test additional cases not covered by the provided tests") { EXPECT_EQUAL(reversed("racecar"), "racecar"); EXPECT_EQUAL(reversed(""), ""); EXPECT_EQUAL(reversed("123456789"), "987654321"); } STUDENT_TEST("test my isPalindrome function") { EXPECT(isPalindrome("racecar")); EXPECT(!isPalindrome("stanford")); } When would you use EXPECT instead of EXPECT_EQUAL? EXPECT_EQUAL is appropriate when you have a result that can be compared for equality to an expected result (e.g. two numbers, two strings, two Vectors, etc.). For most situations, confirming that your code "got the right answer" is exactly what you need. On the other hand, EXPECT allows you to express a wider variety of conditions beyond simple equality. For example, you could confirm the truth of a complex set of conditions by using a compound expression such as EXPECT(x > y && y != z || y == 0); EXPECT_ERROR The EXPECT_ERROR(expression) test macro is used to verify that evaluating the given expression raises an error (i.e. calls the error() function). If an error is raised, the test is reported as Correct. If not, the test is reported as a failure. As an example, EXPECT_ERROR(stringToInteger("cow")); would confirm that an error is raised when trying to convert the non-numeric string to a number value. EXPECT_ERROR is used in the specific situation of confirming expected handling of errors within your code. EXPECT_NO_ERROR The EXPECT_NO_ERROR(expression) is the opposite of the above. If the expression successfully runs to completion without raising an error, then the test is reported as Correct. The test is reported as a failure if the error() function is called. EXPECT_NO_ERROR is used in situations where you want to confirm that functions run to completion on correct input. TIME_OPERATION SimpleTest also has support for simple execution timing. The macro TIME_OPERATION(size, expression) is used to measure the time it takes to evaluate an expression, which is of the specified size. STUDENT_TEST("Time operation vector sort on tiny input") { Vector<int> v = {3, 7, 2, 45, 2, 6, 3, 56, 12}; TIME_OPERATION(v.size(), v.sort()); } The first argument to TIME_OPERATION is the input size; this is used to label this timing result in the output. The second argument is the expression to evaluate. TIME_OPERATION will start a new timer, evaluate the expression, stop the timer, and report the elapsed time. It is often useful to have a sequence of TIME_OPERATION on different sizes to see the larger pattern. Each operation is individually evaluated and timed. Below demonstrates use of TIME_OPERATION in a loop to time how long it takes to sort the items in successively larger vectors. STUDENT_TEST("Time operation vector sort over a range of input sizes") { for (int size = 50000; size < 1000000; size *= 2) { Vector<int> v; for (int i = 0; i < size; i++) { v.add(randomInteger(1, 1000)); // fill vector with random values } TIME_OPERATION(v.size(), v.sort()); } } Here are the test results for running the test cases above: Using TIME_OPERATION over a range of sizes lets you see how the time required for an operation changes with respect to input size - i.e. it predicts the algorithm's Big O. Handy! By default, a test case that uses TIME_OPERATION will be reported as Correct as long as the expression being evaluated does not result in an error or crash. If you want to verify the actual correctness of the result as well as time it, you can mix in regular use of EXPECT_EQUAL and EXPECT into the test case as shown below: STUDENT_TEST("Time operation vector sort on tiny input and verify is sorted") { Vector<int> v = {3, 7, 2, 45, 2, 6, 3, 56, 12}; TIME_OPERATION(v.size(), v.sort()); EXPECT(checkIsSorted(v)); } runSimpleTests The main function of our projects will begin by offering the user a choice in what to execute: run all the tests, select which tests to run, or run no tests and proceed with normal execution. It does this by calling the runSimpleTest function as shown below: int main() { if (runSimpleTests( <test choice argument> )) return 0; ... // rest of normal main() here } The argument to runSimpleTests is either: ALL_TESTS (run all tests for all files) SELECTED_TESTS (provide menu for user to select which tests to run) The user can enter zero for "no tests", which causes the program to continue with rest of main() Debugging a failing test Your goal when testing your code should be to get all of your tests to pass. However, if you get a failed test result, don't look at this as sad times; this test result is news you can use. The failing test case indicates an operation that behaved unexpectedly. This means you know where to focus your attention. Dig into that test case under the debugger to analyze how it has gone astray. Set a breakpoint inside the text code block, and choose to stop at the line that is at or before the failing EXPECT/EXPECT_EQUAL statement. Now run the tests using the debugger. When the program stops at the breakpoint, single-step through the code while watching the variables pane to observe how the state of your program changes, using a technique just like you did in the debugging tutorial in Assignment 0. After you understand the failure and apply a fix, run that test again. When you see the test pass, you can celebrate having squashed that bug! Debugging your test cases Your test cases are implemented as code, which means that they, too, can have bugs of their own. Having a bug in your test case can truly be a maddening experience! A test case that produces a false negative can lead you to investigate a non-existent defect and a false positive lulls you into overlooking a lurking one. You attribute the erroneous test result to the code being tested, yet the real issue is within the test case itself. Unlike hackneyed sitcom plots, hilarity does not ensue from this misunderstanding. For example, suppose you have written a function that returns the square of a number. You write some tests for it: int square(int n) { return n * n; } STUDENT_TEST("confirm my square function works correctly for 5, 10, and 15") { EXPECT_EQUAL(square(5), 25); EXPECT_EQUAL(square(10), 100); EXPECT_EQUAL(square(15), 275); // this test case is BUGGY! } The first two tests pass but the third will fail. The square of 15 is actually 225, not 275. The problem isn't with the square() function, but with the buggy test case that produces a false negative. Every programmer can relate to a time when a buggy test case reported an erroneous failure that led to wild goose chase to find a non-existent flaw in code that was correct all along, argh! There can also be tests that produce a false positive, i.e. report that code is Correct when it has a defect. This could be due to a buggy test case that compares to the wrong expected value, such as shown above. Another source of false positives is when your test cases are not sufficiently robust or comprehensive to surface the problem. If square() returned the wrong value only for negative inputs and your test cases only tested positive inputs, you would receive all Correct results and no mention of the lurking defect. Or perhaps you took a shortcut and wrote your test cases to only confirm that square() returned a non-negative value (e.g. EXPECT(square(15) >= 0)) without checking the specific value. These test cases are not buggy per se, but they are not thorough enough to fully vet the code being tested. A key takehome is that your test results are meaningful exactly and only if your test cases accurate and robust. Put extra care into verifying each test case is properly constructed and produces accurate results. Ensure your suite of test cases cover a comprehensive range of scenarios, including unusual inputs and edge conditions. Now when your program earns its clean sweep of Correct results, you can celebrate that success with confidence! Test-driven development We highly recommend employing test-driven development when working on your assignments. To do so, follow these steps: identify a small, concrete task (bug to fix, feature to add, desired change in behavior) construct tests for the desired outcome, add them to the file in which you're currently working, and verify the current code fails these tests implement the changes in your code to complete the task re-run your newly added tests and verify they now succeed test the rest of the system (by running all tests) to verify you didn't inadvertently break something else This process allows you to change only a small amount of code at once and validate your results with carefully constructed tests before and after. It keeps your development moving forward while ensuring you have a functional program at each step! Test cases and grading The SimpleTest framework will be supplied with each assignment, and there will be some initial test cases provided in the starter project, but you will also be expected to add your own tests. You will submit your tests along with the code, and the grader's review will consider the quality of your tests. We will also provide comments on your tests to help you improve your testing approach. Please incorporate our feedback into future assignments; it will improve your grade and, more importantly, your effectiveness as a programmer. We guarantee future employers will appreciate your ability to write good tests and well-tested code! Here are some things we look for in good tests. Are the tests comprehensive? Is all the functionality tested? Where possible, are the tests self-contained and independent? Did you anticipate potential problems, tricky cases, and boundary conditions? Did you develop the tests in a good order? Did you test basic functionality before more advanced functionality? Did you take small, carefully chosen steps? Common questions Should each EXPECT/EXPECT_EQUAL be in a STUDENT_TEST code block of its own or can I list several within one code block? For tests that are closely related, it may be convenient to group them together in the same code block under one test name. The tests will operate as one combined group and show up in the report as one aggregate success (if all pass) or one failure (if at least one fails). However, there are advantages to separating each individual test case into its own code block. You will be able to choose a clear, specific name for this block. The separation isolates each test so you can easily identify exactly which cases are passing and which are failing. For example, if you have, STUDENT_TEST("Many tests together") { EXPECT(... Test A ...) EXPECT(... Test B ...) EXPECT(... Test C ...) } then if Test B fails, Test C will never run and you won't be able to see the output - you won't know if Test C passed or failed. On the other hand, if you structure your tests like this STUDENT_TEST("Test A") { EXPECT(... Test A ...) } STUDENT_TEST("Test B") { EXPECT(... Test B ...) } STUDENT_TEST("Test C") { EXPECT(... Test C ...) } then all the tests will run individually, and even if Test B fails, you will still get independent information about Tests A and C. Having this sort of isolated behavior might make debugging any problems you encounter a little bit easier! When an assignment requirement says to "add 2 tests," do we count each STUDENT_TEST or each EXPECT_EQUAL? Each use of EXPECT/EXPECT_EQUAL is counted as one test case. Read the answer to the previous question for some things to consider when deciding whether to group multiple test cases under a single STUDENT_TEST group or keep separated. The font/sizes/colors in the Simple Test result window are not pleasing to me. Can I customize the display? Yes! Look in the Qt project browser under Other files->testing for a file named styles.css. This file is the CSS stylesheet for the Simple Test window. Edit this file to change the display styles. Each project has its own copy of the stylesheet. Copy the edited stylesheet from this project into a new project to carry those customizations forward.
http://cs106b.stanford.edu/tutoring
You are welcome to make use of tutoring resources, both on-campus and off-campus, to help solidify conceptual content, to deepen understanding of course topics, to fill gaps in understanding, and to work through ungraded practice problems. Tutoring can be a great way to work through topics you're having trouble understanding and to get input from someone with more experience than you. Tutoring, however, is not appropriate for help with work that will be submitted for a grade. When you have questions about the assignments or exams, please direct them to the course staff - we're happy to help out! Our formal tutoring policy is as follows. The word "tutoring" has a broad meaning, and we'll interpret it to mean any assistance with the course content or topics from someone who is not currently enrolled in the course and is not a member of the course staff. For example, CTL and AARC tutors and help websites like Chegg or Stack Overflow count as tutoring. Similarly, support or advice from past students (say, someone in your dorm who has already completed a course you're taking) counts as tutoring. We consider anyone who offers you support in this context is a tutor, regardless of formal titles. Tutoring must abide by the following rules for CS106B Tutors must be familiar with the Honor Code policy for a course prior to giving help. Importantly, not knowing the Honor Code and tutoring policies for a course does not protect you or your tutors from any Honor Code infractions. A student and tutor should review the course Honor Code policy together to set appropriate expectations and establish clear boundaries. Tutors must not review, look at, offer advice, or otherwise offer assistance with any work that you will submit for a grade or which could be reasonably expected to be submitted for a grade. This means, among other things, that tutors cannot help debug or write code for or related to programming assignments, review written proofs, etc. Tutors must not refer to course materials from previous offerings of the course. For example, tutors are not allowed to refer to lecture slides or solution sets used in previous quarters. They are permitted to reference the current quarter's materials, subject to the restrictions from (2). Tutors must not share any other student's work with you, including their own. For example, tutors may not share past submitted problem sets or coding assignments as a reference, even if the tutor was the original author of that work. Tutors and students are obligated under the Honor Code to "do their share and take an active part" in seeing to it that students and tutors do not ask for or receive unpermitted aid. Course instructors may, at their discretion, place additional limits on permitted tutoring. It is the responsibility of both students and tutors to be aware of and follow these rules. Failure to abide by these rules may constitute a violation of the Honor Code. If you have any questions about what is and is not permitted, either as a student or as a tutor, please contact the course instructor directly. We're happy to clarify our policies and help ensure folks get the support they need while keeping everything above board.
http://cs106b.stanford.edu/assignments/2-adt/warmup.html
For this course, you will write many C++ programs. But writing the code is only the first step; you also need strong skills in testing and debugging to bring a program to successful completion. Knowing your way around the debugger is key. Our assignments will feature warmup exercises that are designed to give you guided practice with the skills and tools for effective testing and debugging. This warmup exercise demonstrates use of the debugger and testing on the ADT types. You are to answer the questions posed below by writing your answers in the file short_answer.txt. This file is submitted with your assignment. 1) View ADTs in debugger (manually configure if needed) Look over the provided code in adtwarmup.cpp that operates on Stacks and Queues. Some of this code is buggy and provided for the purpose of practicing your testing and debugging skills. The reverse function is implemented correctly and does not have bugs. It uses a Stack to reverse the contents of a Queue. Build and run the program. When prompted, enter the choice to run tests from adtwarmup.cpp. The reverse function should pass its single test case. (Ignore the test failures reported for the other functions, we'll get to those in a bit). Use the reverse function to practice debugging an ADT. Set a breakpoint on the first while loop in reverse and run the program in Debug mode. When prompted, select the tests for adtwarmup.cpp. When the debugger stops at your breakpoint, look at the Variables pane in the upper-right of the Qt Creator window. You should see the variables num, q , and s. Expand q by clicking the triangle to the left of its name. The expanded contents of the Queue should look like this: IMPORTANT: If the Queue contents in your debugger look very different than the screenshot above, this likely means that your debugger is not configured to properly display variables of Stanford collection type. Stop here and follow the instructions to configure your Qt debugger. The Queue q was passed as an argument to reverse; its contents were initialized to {1, 2, 3, 4, 5}. The Stack s and integer num are declared as local variables in reverse. Neither of these variables is assigned an initial value. In the debugger variables pane, s is displayed as a Stack containing <0 items>. A Stack has a "default" initializer that configures a new stack that is not otherwise initialized. The default initializer for Stack makes an empty stack. In fact, all of the Stanford collection classes have a default initializer that creates an empty collection. Compare that behavior to what you see for the integer variable num. Like s, the variable num was declared without being initialized. Unlike the collection types, the int type does not have a default initializer. This means an int variable is left uninitialized. The debugger shows the "value" for num but that value is only the leftover contents in the memory location where num is being stored. In the screenshot above, the leftover value happened to be 28672, but you may see something different on your system. If your code erroneously tries to use the value of an uninitialized variable, you get an unpredictable result. For now, just file this fact away. If at some later point in debugging, you observe a variable holding a nonsensical value, check to see if the bug is a missing initialization. Use the Step Over button to single-step through the first iteration of the while loop. After executing the assignment to num, its value now becomes sensible. Stepping the next line pushes thaat value onto the stack. Expand the Stack s by clicking the triangle to the left of its name. You should now have both the stack and queue expanded. Continue single-stepping through the loop. As you step, keep your eye on the Variables pane and watch how the contents of the stack and queue are changing. Q1. The display of the Stack in the debugger uses the labels top and bottom to mark the two ends of the stack. How are the contents labeled when the Stack contains only one element? You now know how to inspect and interpret ADTs in the debugger. We hope that you will find this a useful skill when working on the rest of the assignment. 2) Test duplicateNegatives Testing and debugging are closely related. After writing a piece of code, you will want to test it to verify its behavior. If you encounter a problem, you run your test case under the debugger to further diagnose the bug. The intention of the function duplicateNegatives is to modify a Queue<int> to duplicate each negative number, i.e. turning the queue {3, -5, 10} into {3, -5, -5, 10}. The given code is buggy and does not behave correctly on all inputs. The provided test cases try inputs containing negative and positive numbers. Run those tests and take note of which tests pass and which do not. The test results show that the function works correctly only the input without negative numbers. Your hypothesis is that it is the presence of any negative number that triggers the bug. Before you start on debugging, you want to winnow down to the most minimal case. Thus you decide to try a queue of length one containing a single negative number. Write your own STUDENT_TEST for this case. Run the tests again. This new case seems to be taking a really, really long time to run. In fact what is happening is that the program is stuck in an infinite loop. A infinite loop is one that never reaches its stopping condition and thus never completes. When a program is stuck an infinite loop, your only recourse is to manually intervene. Pro tip: how to stop a stuck program If not running in Debug mode, you can stop the program by closing the console window or choosing "Quit" from the menu bar. This action forcibly exits the program. If running in Debug mode, you can interrupt the program. Find the debugger button (hover over to confirm tool tip action is "Interrupt"). Clicking this button will pause program execution and return control to the debugger. The program state is preserved and can be examined. This will allow you to gather more information to diagnose what's gone wrong. What have we learned so far from testing duplicateNegatives? The function works correctly for non-negative inputs. For other inputs, the function completes but produces the wrong result. Your new test shows there is also a third category, where some inputs go into an infinite loop. Precisely identifying what kind of inputs trigger a problem is very helpful, as this will focus the debugging process. You have observed that an input of a single negative number results in an infinite loop. Is there more to the pattern? Could it be specific to where the negative number occurs in the input, such as being the first or last ? Add more student test cases and re-run until you narrow in on the precise trigger for the infinite loop. Gather the results of your observations and answer the following questions in short_answer.txt: Q2. For which type of inputs does the function go into an infinite loop? Rather than identify one specific input, describe the general characteristics of all such inputs. 3) Debug duplicateNegatives Now that you've observed the buggy behavior and know what kind of input triggers it, let's use the debugger to diagnose the flaw. (You may have already seen the bug when reading over the code; if so, great! But the purpose of this exercise is to show you a methodology for using the debugger that will help you in later times when you cannot spot the bug just from reading the code.) Start with your test case that goes into an infinite loop. Set a breakpoint on the call to duplicateNegatives within the test case and run the program in Debug mode. When the breakpoint is hit, Step Into the call to duplicateNegatives and then use Step Over to single step through a few iterations of the for loop. Expand the variable q to see its contents and pay attention to the changing values for i and q. Trace out what is happening and work through why the loop never reaches its intended termination condition. Given the above detective work, come up with a fix for the duplicateNegatives code. Try out your fix and see that it resolves the problem with the infinite loop inputs. As a followup, re-test the inputs that terminated but produced incorrect results. You should find they are also working correctly now. In this case, the same underlying flaw was producing two seemingly unrelated symptoms. Debugger use for the win! Answer the following question in short_answer.txt: Q3. Show your edited code for duplicateNegatives that fixes the problem with the infinite loop. 4) Diagnose a test that raises an error The last part of the warmup is learning how to recognize when a test case fails due to raising an error. The function sumStack is intended to return the sum of the values in a Stack of integers. Run the provided test cases. The first test is successful, but the subsequent test goes down in flames due to an error. When an error is raised in the middle of a test, SimpleTest reports it like this: Test failed due to the program triggering an ErrorException. This means that the test did not fail because of a call to EXPECT() or EXPECT_ERROR() failing, but rather because some code explicitly called the error() function. When you see this message, it means a fatal error was raised during test which prevented the test from completing. The error was not expected and due to a bug in the code. Sometimes there is additional commentary which further explains the sepcifics of the error, e.g. index out of bounds, attempt to pop from an empty stack, or modification of a collection while iterating over it. Use the same debugging process for an error as a failing test case: add a breakpoint inside the test case and step through to see where it goes wrong. If you put a breakpoint inside the test case code before the call to sumStack, your stepping will go through the behind-the-scenes code that sets up for the function call, including make a copy of the stack (the parameter is pass-by-value). This code is a bit goopy, so if instead set breakpoint on the first line of sumStack, this will stop after those function call shenanigans and you can get on to stepping through the operation of sumStack. There is one added twist to be aware of when stepping through code that has an error - you can step up to, but cannot step through the actual operation that raises an error. When executing the statement in error, a clever bit of C++ "hyperjumps" control to an error-handling routine (or your program may terminate if the error is more catastrophic). You must restart the program and step over until the crash again to get back to the context right before the crash. Looking at the code and stepping in the debugger, you will see that the bug in sumStack is due to mishandling an empty stack. One possible fix to sumStack would be to add a special case, i.e. inserting this code at the top of the function: int sumStack(Stack<int> s) { if (s.isEmpty()) { return 0; } ... However, there is a better fix to the existing code that would make it work correctly for both empty and non-empty stacks, without adding a special case. Q4. What is the better fix to sumStack that corrects the bug? That's it for the warmup! Be sure to keep these debugging techniques in mind when you find yourself in a future jam- the debugger is a friend you don't want to be without!
