Docsity
Docsity

Prepare for your exams
Prepare for your exams

Study with the several resources on Docsity


Earn points to download
Earn points to download

Earn points by helping other students or get them with a premium plan


Guidelines and tips
Guidelines and tips

Object-Oriented Programming and Data Structures - Assignment | CS 2110, Assignments of Computer Science

Material Type: Assignment; Class: Object-Oriented Programming and Data Structures; Subject: Computer Science; University: Cornell University; Term: Spring 2009;

Typology: Assignments

Pre 2010

Uploaded on 08/30/2009

koofers-user-cxi-2
koofers-user-cxi-2 🇺🇸

10 documents

1 / 42

Toggle sidebar

Related documents


Partial preview of the text

Download Object-Oriented Programming and Data Structures - Assignment | CS 2110 and more Assignments Computer Science in PDF only on Docsity! CS 2110 Java structure • Classes ... making the manufacturer. It’s now time to turn our attention to the manufacturer of all these reference objects. As an example ... public class BankAcc { private float balance ; // data field to hold values for each BankAcc manufactured public float getBalance ( ) { // accessor method return balance ; } public void setBalance ( float bal ) { // mutator method balance = bal ; } public float spend ( float amt ) { // methods to do stuff balance -= amt ; } public float deposit ( float amt ) { balance += amt ; } public BankAcc ( ) { // default constructor balance = 0.0 ; } public BankAcc ( float amt ) { // another constructor balance = amt ; } } // end class BankAcc So then, how does this get used? 1 can’t access except via methods actually in the manufacturer class -- it’s a visibility modifier can access via ANY object of ‘type’ BankAcc but any use of data fields grabs only the values for that actual incarnation of the object no return type same name as the class name public class GRQ { public static void main ( String [ ] args ) { BankAcc owen = new BankAcc ( ) ; owen.deposit ( 5000.75 ) ; System.out.println ( “Owen has $” + owen.getBalance ( ) ) ; BankAcc feit = new BankAcc ( 2000.96 ) ; feit.spend ( 3000.50 ) ; System.out.println ( “Feit has $” + feit.getBalance ( ) ) ; } // end main method } // end class GRQ owen can’t get feit’s money, and v.v. In general it’s a very good idea to default to making as many of the data fields private as possible, and use accessor/mutator (settor/gettor) methods to control access to them - classes CS 2110 Java structure • We can enhance the previous class ... public class BankAcc { // static fields ‘belong’ to the manufacturer, not the made object, so only one copy of it is created, but each object can share it private static int numAccs = 0 ; // data field to hold CLASS values, initialised at compile time when the class is loaded private static int [ ] accNos = new int [ 100000 ] ; // data field to hold CLASS array for account numbers private static float totalAssets = 0.0 ; // to hold total assets of accounts, if it weren’t private then could get by BankAcc.totalAssets private float balance ; // data field to hold values for each instantiation static { // this is (surprisingly?) a method run at compile time (hence no name and no input parentheses!) for ( int i = 0 ; i < accNos.length ; i++ ) { accNos [ i ] = 100001 + i ; } // end for loop initialising account numbers } // end static compile-time method public static int getBankAssets ( ) { // this method can be called in main (for example) by BankAcc.getBankAssets ( ) ; return totalAssets ; } public float getBalance ( ) { // accessor method - perhaps should ask for authorisation? return balance ; } public void setBalance ( float balance ) { // mutator method - who should authorise this? totalAssets += balance; this.balance = balance ; } public float spend ( float amt ) { // methods to do stuff - should authenticate user? boolean bounce = ( balance < amt ) ; // bounce only exists within this method balance -= !bounce ? amt : 0.0 ; if (bounce) System.out.println ( “Sorry, not enough money there at the moment (: ” ) ; else totalAssets -= amt ; } public float deposit ( float amt ) { // perhaps should offer a receipt? totalAssets += balance ; balance += amt ; } public BankAcc ( float balance ) { // another constructor totalAssets += balance ; this.balance = balance ; numAccs++ ; } // note that this gets incremented each time a new account is created! public BankAcc ( ) { // default constructor this ( 0.0 ) ; } // note the use of this to refer to one of the constructor -- has to be first statement in the constructor } // end class BankAcc 2- classes local name limited to the scope of the method definition the ‘balance’ of the current object CS 2110 Java structure • Thankfully, Java provides the mechanism of inheritance to save us from too much repetition in dealing with this situation. The essential idea is that a child inherits everything a parent has, but can have some things of its own. This leads to the power of attorney rule: if in some situation you’re expecting a parent but only have a child, then that’s ok since a child can do everything a parent can; if however you’re expecting to see a child but only have a parent, then that is not ok since that child might have had properties the parent doesn’t have! Notice that in this model, no child can have more than one parent. • The idea then is to put as much commonality as high up in the family tree as possible, so that a Book would have an author , title , publisher , isbn , cryear . A Sectional would have an array of Chapters called chaps, etc. 5- classes CS 2110 Java structure • One point needs to be clarified: a child inherits the methods and fields of the parent, it does not inherit the values of any of the parent’s fields! If a parent has a bank account, the child inherits the ability to have a bank account, it doesn’t inherit the money in the parent’s account!! • One other messy detail ... we can only reach up one level in the family hierarchy via super . So if we have three classes with C extending B which extends A (so that A is the grandparent), and if x is a data field of A (thus inherited by B and C , then within the class C ... x variable x in class C super.x variable x in class B this.x ditto ( (B) this ).x ditto ( (A) this ).x variable x in class A super.super.x illegal statement, sorry!!! • Actually, every class is in a hierarchy since even if you don’t specify a parent via extends , Java provides a generic parent class Object ! Java does other things by default. If your first statement in a derived ‘child’ class constructor isn’t super , then Java calls super( ) with no arguments automatically. So if the superclass doesn’t have any constructors having no arguments, then the compiler will complain. This is also what happens is a non-explicitly-child class is formed; Java calls the default super( ) from the class Object , so providing a default constructor. 6- classes CS 2110 Java structure • Exceptions. Bad errors cause programs (and sometimes machines!) to crash. It’s better to design our programs to catch exceptional conditions before the become fatal errors. import java.io.* ; public class PrintInt { public static void main ( String [ ] args ) { InputStreamReader isr = new InputStreamReader ( System.in ) ; BufferedReader br = new BufferedReader ( isr ) ; PrintWriter pw = new PrintWriter ( System.out , true ) ; int x ; String s ; pw.println ( “Enter an integer.” ) ; try { s = br. readLine ( ) ; x = Integer.parseInt ( s ) ; pw.println ( “The integer was ” + x ) ; } // end try block catch ( Exception e ) { pw.println ( e ) ; } // end catch block for Exception } // end main method } // end class PrintInt As indicated in the example, the try block is run. If there are no problems then the catch block is ignored. If a problem occurs then the try block terminates immediately and any exception that’s thrown by the problem line gets caught by whichever catch line matches (or includes) the type of exception generated. 7- exceptions could generate an IOException could generate a NumberFormatException we could be more specific, but this will nab the interesting ones ... nice and general runs this section goes down here only if exceptions thrown any catches are immediate CS 2110 Java structure • Threads. So far, we have been running a single thread of control, but it’s often convenient to be able to run either several threads independently and concurrently, or have threads branch off and yet still communicate with one another. We’ll look at a few elementary approaches to multi-threading (for a more in-depth discussion, follow the link to the Java Specification on the course homepage and read ch 17) ... • There is a Thread class, so we could do ... Thread sausage = new Thread ( ) ; to create a new thread sausage which can be configured and run. However sausage.run( ); won’t do anything ... the computer doesn’t know anything special about running sausages!! Better would be to extend the Thread class and then redefine run( ) in the derived class ... public class PingPong extends Thread { // from the java.sun.com thread tutorial private String word ; // the word to print private int delay ; // the delay in millisecs to pause public PingPong ( String parole , int pendant ) { this.word = parole ; this.delay = pendant ; } // end constructor public void run ( ) { // overriding Thread’s run ( ) method try { // since sleep can throw an InterruptedException for ( ; ; ) { // never stop (unless interrupted) !!!! System.out.print ( word + “ ” ) ; sleep ( delay ) ; } } catch ( InterruptedException ie ) { return ; } } // end overriding of the run ( ) method } // end class PingPong 10- threads Then if we have ... public static void main ( String [ ] args ) { new PingPong ( “ping” , 333 ) . start ( ) ; new PingPong ( “PONG” , 1000 ) . start ( ) ; } // end main method we’ll get ping appearing on the screen every 1/3 second and PONG every second (they’ll have fractionally different start times) ... ping PONG ping ping ping PONG ping ping ping PONG ping Hence two separate and independently running threads. CS 2110 Java structure • There’s another more or less equivalent way of doing this, especially useful if you want to inherit from some class and don’t want to use up your one inheritance opportunity by having to extend the Thread class ... public class RunPingPong implements Runnable { // from the “java programming language” book private String word ; private int delay ; public RunPingPong ( String parole , int pendant ) { this.word = parole ; this.delay = pendant ; } // end constructor public void run ( ) { try { for ( ; ; ) { System.out.print ( word + “ ” ) ; Thread . sleep ( delay ) ; } // only exit from for loop is via an interrupt } catch ( InterruptedException ie ) { return ; } } // end implementing the run ( ) method } // end class RunPingPong • We’ll say more about interfaces like Runnable in the next section. For now you can think of them as mold-like superclasses which only have methods - declared, but never defined. They come with an implied contract to define every method they have if you want to implement (née extend) them. For the case of Runnable , it only declares one method, namely run( ) . 11- threads Then if we have ... public static void main ( String [ ] args ) { Runnable little , bigger ; little = new RunPingPong ( “ping” , 333 ) ; bigger = new RunPingPong ( “PONG” , 1000 ) ; new Thread ( little ) . start ( ) ; new Thread ( bigger ) . start ( ) ; } // end main method we’ll get the same behaviour as before. CS 2110 Java structure • Suppose we want some rudimentary control on when particular data fields can be accessed. Consider for example a bank account in a multi-threaded environment. So what if the same account data (e.g. balance) could be accessed simultaneously by independent threads? thread A ..... ......... ......... thread B ................... ........... .............. It could be argued that each thread ran ‘correctly’, but because thread B read the value of balance after thread A’s read but before thread A had completed its calculation, thread B incremented ‘the wrong’ value, hence leaving the balance as if that $2000 have never been deposited. Such a situation is called a race condition. • The same situation could occur even with a simple expression like oops++ ; which technically comprises three operations: read oops, add one to oops, write oops back into memory. Concurrent writes or read/writes on a value are dangerous!!! (Concurrent reads are safe.) To safeguard this situation ... 12- threads read the value of balance 93 deposit $2000 so balance 2093 write the value of balance 2093 read the value of balance 93 deposit $500 so balance 593 write the value of balance 593 timeline CS 2110 Data Structures • Interfaces. (cf “The Java Programming Language” book, ch 4.) These provide a very natural way to design a specification for code to solve a problem, think of them as being contracts for classes. Effectively, cracking a problem can be broken down into designing the solution and then implementing it; the design stage being stipulated by listing ‘skeletal’ classes having methods (but no definitions), and the implementation stage being split between implementing the contracts (classes) and writing code which uses them to actually solve the problem. Hence a programming team could be divided into these three groups, allowing them to work largely in parallel once the design has been set. • Imagine building a spec sheet for a queue ... folk join at the back and are served (and leave) from the front. Being aggressive, we could insist that after joining a queue, the only way to leave is from the front. This might lead to ... public interface Queue { // note that there aren’t even hints of method definitions below, save for the comments!!! void joinQ ( Person p ) ; // the person p gets added to the back of the queue Person leaveQ ( ) ; // to return the person at the front, and then dump them in favour of the next person in line int getLength ( ) ; // to return the number of people in the queue boolean isFull ( ) ; // to return true if there’s no room left in the queue boolean isEmpty ( ) ; // to return true if there’s no-one in the queue } // end interface Queue 15 - interfaces everything in an interface is presumed public CS 2110 Data Structures • Note that promising to implement an interface forces (via the compiler) an implementation of every method listed in that interface (but doesn’t insist on the method definitions being sensible!) ... public class Q1 implements Queue { void joinQ ( Person p ) { return ; } Person leaveQ ( ) { return new Person ( “anonymous” ) ; } int getLength ( ) { return Integer.MAX_VALUE ; } boolean isFull ( ) { return true ; } boolean isEmpty ( ) { return true ; } public Q1 ( int n ) { System.out.println ( “This queue is so efficient it takes up almost no space!!!” ) ; } // constructor public Q1 ( ) { this ( -47 ) ; } // equally silly default constructor } // end interface Queue Sadly, the compiler will be perfectly happy with this! Perhaps rather better might be ... public class Q2 implements Queue { private Person [ ] storage ; private int length , front , back , size ; private final int DEFAULT_SIZE = 100 ; void joinQ ( Person p ) { if ( ! isFull( ) ) { storage [ back++ ] = p ; length++ ; } else System.out.println ( “Sorry, full up” ) ; } // end joinQ method Person leaveQ ( ) { Person temp ; if ( ! isEmpty( ) ) { temp = storage [ front++ ] ; length-- ; return temp ; } else System.out.println ( “Sorry, queue empty” ) ; return null; } // end leaveQ method int getLength ( ) { return this.length ; } boolean isFull ( ) { return ( this.back == size ) ; } boolean isEmpty ( ) { return ( length == 0 ) ; } public Q2 ( int n ) { size = n ; storage = new Person [ n ] ; length = 0 ; front = 0 ; back = 0 ; } // hopefully n > 0 ! public Q2 ( ) { this ( DEFAULT_SIZE ) ; } // or some such default value } // end class Q2 16 - interfaces front back CS 2110 Data Structures • Interfaces make no presumption about implementations - indeed, there can be multiple strikingly different implementations of any given interface. As further examples (cf the Weiss textbook, ch 3) ... public class Q3 implements Queue { private Person [ ] storage ; private int length , front , back , size ; private final int DEFAULT_SIZE = 100 ; void joinQ ( Person p ) { if ( ! isFull( ) ) { storage [ ( back++ ) % size] = p ; length++ ; } else System.out.println ( “Sorry, full up” ) ; } // end joinQ method Person leaveQ ( ) { if ( ! isEmpty( ) ) { Person temp = storage [ ( front++ ) % size ] ; length-- ; return temp ; } else System.out.println ( “Sorry, queue empty” ) ; return null; } // end leaveQ method int getLength ( ) { return this.length ; } boolean isFull ( ) { return ( this.length == size ) ; } boolean isEmpty ( ) { return ( length == 0 ) ; } public Q3 ( int n ) { size = n ; storage = new Person [ size ] ; length = 0 ; front = 0 ; back = 0 ; } // hopefully n > 0 ! public Q3 ( ) { this ( DEFAULT_SIZE ) ; } } // end class Q3 The difference between the Q2 and Q3 implementations is that for Q2 we stored the data in a linear array (wasting space as it emptied, and losing queue re-usability), whereas for Q3 the storage was in a ‘quasi-circular’ array (allowing continual use). It’s worth noting that Q2 might be well-suited in a situation having limited resource (eg queueing for food or for concert tickets) whereas Q3 would be a better match for a renewable resource (eg printing or processor access, or customer service). • Thinking more structurally about a queue, we could build a more elegant flavour ... 17 - queues CS 2110 Data Structures • Actually this is a poor approach; having just one ‘current location’ will make it hard to sort a list (we’d need at least two ‘currents’ for that), and there’s some serious awkwardness in the way we manipulated current. Far better would be to divvy up the work between the list and the arrows pointing into the list (iterators) according to where things rightly belong, but we’ll delay doing this until we’ve introduced the other two main data structures. • A stack is another kind of emaciated list, this time only allowing entering and leaving from one end (the ‘top') ... 20 - stacks public interface Stack { void join ( Person p ) ; // p inserted at the top Person leave ( ) ; // the top is deleted int getLength ( ) ; // # of people in the stack boolean isFull ( ) ; boolean isEmpty ( ) ; } // end interface Stack public class S1 implements Stack { private Node top ; private int length ; public void join ( Person p ) { top = new Node ( p , isEmpty ( ) ? null ; top ; ) ; length++ ; } // end join method public Person leave ( ) { if ( ! isEmpty( ) ) { Person temp = top. getData ( ) ; top = top. getNext( ) ; length-- ; return temp ; } else System.out .println ( “Sorry, stack is empty” ) ; } // end leave method public int getLength ( ) { return this . length ; } public boolean isFull ( ) { return false ; } public boolean isEmpty ( ) { return ( length == 0 ) ; } public S1 ( int n ) { length = 0 ; } // n is irrelevant here public S1 ( ) { this ( 0 ) ; } } // end class S1 top null typically called push typically called pop CS 2110 Data Structures • Stacks are surprisingly powerful animals - much of what goes on in your computer is done via stacks (of commands, of values, etc.). However, as quick examples ... (i) Two ways of writing arithmetic expressions are infix, as in a + ((b-c)*((d+e)-(f/g))) and postfix (or reverse Polish), as in a b c - d e + f g / - * + . To evaluate the latter, we can put the values on a stack, and on seeing an operator, apply it by pulling the top off the stack (twice) and then putting the newly computed value on the stack ... (ii) As you might guess from the above, we can also use a stack to convert an infix to a postfix expression, essentially using the stack as a ‘storage-until-ready’ holding location ... 21 - stacks a b a c b a b-c a d b-c a e d b-c a d+e b-c a f d+e b-c a g f d+e b-c a f/g d+e b-c a d+e - (f/g) b-c a (b-c)* (d+e - (f/g)) a a+(b-c)* (d+e - (f/g)) - appears + appears / appears - appears * appears + appears This will produce the postfix version above from the infix flavour term-by-term under the algorithm: (i) output non-operands immediately (ii) rank operands by strength, so + and - are at the bottom, * and / are next, and “(” is the highest (iii) on reading an operand, pop from the stack (into the output) until seeing a stronger one, then push that operand (iv) on reading a “)”, pop everything from the stack until the “(”, which is discarded (popped but not output) (v) if there’s no more input, then pop from the stack until empty + ( + ( ( + +- ( ( + ( + * ( + ( * ( + ( ( * ( + + ( ( * ( + ( * ( + - ( * ( + ( - ( * ( + / ( - ( * ( + - ( * ( + * ( + CS 2110 Data Structures • Trees are essentially lists on steroids, with nodes having multiple ‘nexts’, a binary tree having two ‘nexts’ (a left and a right) from each node. 22 - trees public class BTNode { private Person data ; // holds the data private BTNode left , right ; // points to ‘children’ public Person getData ( ) { return data ; } public void setData ( Person p ) { data = p ; } public BTNode getLeft ( ) { return left ; } public void setLeft ( BTNode L ) { next = L ; } public BTNode getRight ( ) { return right ; } public void setRight ( BTNode R ) { right = R ; } public BTNode ( Person p , BTNode L , BTNode R ) { data = p ; left = L ; right = R ; } public BTNode ( Person p ) { this ( p , null , null ) ; } public BTNode ( ) ( this ( null ) ; } } // end class BTNode So a binary tree could be modeled using nodes as in the code on the right. A common application is for binary search trees, which assign choices on insertion to place left or right of a ‘current’ node depending on whether the new value to be inserted is smaller or larger than the ‘current’ one. This can allow for a log(n) search speed ... certainly faster than the linear search on a regular array!! For example, the sequence of numbers, 47, 72, 53, 26, 41, 97, 83, 14, would yield the following tree: root node 47 72 975341 26 14 null null null null null null null null null83 Of course, whilst insertion is easy, deletion of a value requires rather more care if the binary nature of the tree is to be preserved! Removing 83 from our example is trivial, or losing 97 instead is easy, but deleting 72 would need some finesse, especially if the parts of the tree below 53 and 97 were to be far more greatly populated. CS 2110 Data Structures • Now we can implement our list more purely as a storage device, utilising the list iterator to allow us to move around through the data. 25 - iterators public interface List { void join ( Person p , Itr t ) throws BadInsertionPoint ; void remove ( Person p ) throws ItemNotFound ; Itr find ( Person p ) throws ItemNotFound ; int getLength ( ) ; boolean isFull ( ) ; boolean isEmpty ( ) ; Itr first ( ) ; } // end interface List public class L2 implements List { private Node header ; private int length ; public Node getHeader ( ) { return this .header ; } public void join ( Person p , Litr t ) throws BadInsertionPoint { if ( t == null ) throw new BadInsertionPoint ( ) ; t .getNode ( ) . setNext ( new Node ( p , t .pointNext ( ) .getNode ( ) ) ) ; length++ ; } // end join method public Itr find ( Person p ) throws ItemNotFound { Itr t = isEmpty ( ) ? null : first ( ) ; try { while ( t . isInList ( ) && ! t .getData ( ) .equals ( p ) ) t .plusPlus ( ) ; } catch ( BadPointer bp ) { } // can't happen since t isInList if ( t == null ) throw new ItemNotFound ( ) ; return t ; } // end find method public void remove ( Person p ) throws ItemNotFound { if ( ! isEmpty ( ) ) { Itr t = find ( p ) ; // throws exception if not found t .minusMinus ( ) ; t .getNode ( ) . setNext ( t .pointNext ( ) .pointNext ( ) .getNode ( ) ) ; length-- ; } else System.out.println ( "Sorry, list is empty" ) ; } // end remove method public int getLength ( ) { return this . length ; } public boolean isFull ( ) { return false ; } public boolean isEmpty ( ) { return ( length == 0 ) ; } public Itr first ( ) { return new ItrP ( this , header.getNext ( ) ) ; } public L2 ( int n ) { header = new Node ( ) ; length = 0 ; } public L2 ( ) { this ( 0 ) ; } } // end class L2 This implementation has aspects which are unsatisfactory, for example having such ready access to the header node is dangerous. Also, it’s time we addressed the performance hit from restricting our attention to singly linked lists; the extra space taken by having doubly linked nodes is negligible compared to the linear cost (versus constant time) of finding a node’s previous! CS 2110 Data Structures • So assuming a node containing data (Person) and a next and prev (Node) permits the following more efficient implementation of our iterator interface (the L2 list implementation remains, though with the getHeader( ) method deleted). • You should also try writing classes to implement stacks, queues and binary trees with separate classes of iterators to facilitate manipulation of their data. However, in order to write a clean binary tree implementation using tree iterators to insert and delete values, we should first remind ourselves about recursion. 26 public class ItrP implements Itr { private Node current ; public boolean isInList ( ) { return current != null && current .getData ( ) != null ; } public boolean atEnd ( ) { return current .getNext ( ) == null ; } public Node getNode ( ) { return current ; } public Person getData ( ) throws BadPointer { if ( ! isInList ( ) ) throw new BadPointer ( ) ; return current .getData ( ) ; } public Itr getCopy ( ) { return new ItrP ( current ) ; } public Itr pointNext ( ) { return new ItrP ( current .getNext ( ) ) ; } public void plusPlus ( ) { if ( current != null ) current = current .getNext ( ) ; } public void minusMinus ( ) { // ignore current == null or the header if ( isInList ( ) ) current = current .getPrev ( ) ; } public ItrP ( Node n ) { this .current = n ; } public ItrP ( ) { this ( null ) ; } } // end class ItrP You might like to try rewriting these implementations with the getNode( ) method deleted, since that too is a tad unsafe (naturally this means adjusting the Itr interface!!). Clearly it would be more than a nuisance to have to write fresh versions of all these data structures for every kind of object (Person, Sausage, Rhubarb, ...). Whilst we could handle this by having our structures be based on Objects and then exploiting casting, there is a convenient solution to this via Java’s generic types, which we’ll introduce after a quick detour on recursion and induction. - iterators CS 2110 Data Structures • Suppose you are given a ‘rule’ such as and also know that Then we can use this to build a sequence of numbers which you recognise as factorials. It’s easy to see this by building up from the bottom although using this approach to show that this will really only produce factorials would take an infinite amount of time! 27 - recursion an = n an-1 a1 = 1 1 , 2 , 6 , 24 , 120 , 720 , . . . 1 , 2 x 1 , 3 x ( 2 x 1 ) , 4 x (3 x 2 x 1 ) , . . . a1 2 a2 3 a3 4 a4 CS 2110 Data Structures • In fact, any sequence defined by a recurrence relation can be converted very easily into recursive code. Without making any comments about efficiency (!), recursive code is typically very short. • As experiments, first you should run this code (previous page) to compute fact(10) and then fact(100) - but you might like to change from int to BigInteger to avoid the sad consequences of int wrap around! After that, try to find the 10th and the 100th term in the following Fibonacci sequence, and then look at the schematic of the recursive calls on the previous page to understand what’s (not) going on (and then fix it)! • It’s worth noting that very similar code can be used to compute Cn , r , the binomial coefficients (for the intuition behind this look at Pascal’s triangle) and powers of a (an example of a ‘divide and conquer’ approach) Note the vastly faster computation for powers when coded this way! 30 - recursion an = an-1 + an-2 a1 = 1 , and a2 = 1 Cn , r = Cn-1 , r + Cn-1 , r-1 with Cn , n = 1 , and Cn , 0 = 1 an = ( an/2 )2 for n even , an = a( an/2 )2 for n odd , and a0 = 1 CS 2110 Data Structures • It’s interesting to see how recursive methods could be implemented within the machine. The key idea is to use a stack to remember parameters and local variables across recursive calls, and for each method invocation to get its own stack frame. A stack frame contains storage for is pushed with each recursive call, and popped when the method returns (leaving any value on top of the stack). 31 - recursion (derived from Kozen 2008) ● local variables of the method ● return info (address and return value) ● parameters of the method ● any other bookkeeping info example showing the change of state of the stack of stack frames for computing the value of 25 recursively (as on the previous page) CS 2110 Data Structures • The processor has to keep track of all this . . . one approach is to have a frame base register (FBR), so that when a method is invoked, the FBR points to its stack frame. Then when the method invocation returns, the FBR is restored to its value before the invocation (courtesy of part of the return info in the stack frame). Computational activity only takes place in the topmost (most recently pushed) stack frame. • A rather nice application of recursion can be found when parsing ‘sentences’ constructed according to formal grammars. These actually underlie the formal structure of programming languages (as well as many other things!). 32 - recursion (derived from Kozen 2008) http://java.sun.com/docs/books/jls/third_edition/html/syntax.html CS 2110 Data Structures 35 - grammars (Kozen 2008) 8 Sentences with Periods PunctuatedSentence ! Sentence . Sentence ! Sentence and Sentence Sentence ! Sentence or Sentence Sentence ! Noun Verb Noun Noun ! boys Noun ! girls Noun ! bunnies Verb ! like Verb ! see ! Add a new rule that adds a period only at the end of the sentence. ! The tokens here are the 7 words plus the period (.) ! This grammar is ambiguous: boys like girls and girls like boys or girls like bunnies CS 2110 Data Structures 36 - grammars (Kozen 2008) 9 Grammar for Simple Expressions E ! integer E ! ( E + E ) ! Simple expressions: " An E can be an integer. " An E can be ‘(’ followed by an E followed by ‘+’ followed by an E followed by ‘)’ ! Set of expressions defined by this grammar is a recursively- defined set " Is language finite or infinite? " Do recursive grammars always yield infinite languages? ! Here are some legal expressions: " 2 " (3 + 34) " ((4+23) + 89) " ((89 + 23) + (23 + (34+12))) ! Here are some illegal expressions: " (3 " 3 + 4 ! The tokens in this grammar are (, +, ), and any integer CS 2110 Data Structures 37 - grammars (Kozen 2008) 10 Parsing ! Grammars can be used in two ways " A grammar defines a language (i.e., the set of properly structured sentences) " A grammar can be used to parse a sentence (thus, checking if the sentence is in the language) ! To parse a sentence is to build a parse tree " This is much like diagramming a sentence ! Example: Show that ((4+23) + 89) is a valid expression E by building a parse tree E ( E )E+ 89 ( E )E+ 4 23 CS 2110 Data Structures 40 - grammars (Kozen 2008) 20 Does Recursive Descent Always Work? ! There are some grammars that cannot be used as the basis for recursive descent " A trivial example (causes infinite recursion): # S ! b # S ! Sa ! Can rewrite grammar # S ! b # S ! bA # A ! a # A ! aA ! For some constructs, recursive descent is hard to use " Can use a more powerful parsing technique (there are several, but not in this course) CS 2110 Data Structures 41 - grammars (Kozen 2008) 21 Syntactic Ambiguity ! Sometimes a sentence has more than one parse tree S ! A | aaxB A ! x | aAb B ! b | bB " The string aaxbb can be parsed in two ways ! This kind of ambiguity sometimes shows up in programming languages if E1 then if E2 then S1 else S2 Which then does the else go with? ! This ambiguity actually affects the program’s meaning ! How do we resolve this? " Provide an extra non-grammar rule (e.g., the else goes with the closest if) " Modify the language (e.g., an if-statement must end with a ‘fi’ ) " Operator precedence (e.g. 1 + 2 * 3 should always be parsed as 1 + (2 * 3), not (1 + 2) * 3 " Other methods (e.g., Python uses amount of indentation) CS 2110 Data Structures • On page 26 we deferred coding tree navigation until after a reminder on recursion, however, instead of covering this in depth in lectures, we’ll leave coding these for the current homework set -- probably more helpful in enabling you to ‘get into’ the mindset of building ‘nano-code-bots’ to be released at the root of a tree and which spawn recursive bots as needed (active discussion in lecture describing the construction of in-order traversals). • Also on page 26 we referred to generics (introduced in Java 1.5) as a way to avoid having to build data structures for each particular type of object, so it’s time to see what they are and how to use them. 42 - generics
Docsity logo



Copyright Š 2024 Ladybird Srl - Via Leonardo da Vinci 16, 10126, Torino, Italy - VAT 10816460017 - All rights reserved