Download presentation
Presentation is loading. Please wait.
Published byBarry Washington Modified over 9 years ago
1
Week 10: Loop Invariants, Code correctness Jimmy Voss Disclaimer: Some material may have been borrowed from both the Official Course slides as well as Anna Wolfe’s slides
2
Code Correctness Within the RESOLVE framework, code is considered to be correct if: 1.When you call any procedure / function, the requires clause (including convention / correspondence, etc.) is satisfied. 2.The procedure / function fulfills its contract (i.e., the ensures clause) when its contract, and the contracts of any procedures it calls are met. Notice, correct output does not mean correct code. Correct output is necessary but not sufficient.
3
Reasoning about Code RESOLVE formal comments give a means by which to reason about the correctness and behavior of code: – procedures/functions requires and ensures clauses – member functions of a class convention correspondence – recursive functions requires / ensures clauses decreases clause When code is correct, you can tell what a function call will do without looking at the code.
4
Loop Invariants The question remains: How do we reason about the behavior of a long, complicated loops? – Create a formal comment for the loop. – A loop runs multiple iterations. Thus the requires and ensures clauses must be the same. – Instead of using requires and ensures, we have a maintains clause. This clause states what is true at the beginning and end of each execution through the loop. – decreases clause – gives a non-negative valued expression that must get smaller after each iteration of the loop. This has the same purpose as in recursive functions.
5
Example global_procedure Concatenate ( alters Queue_Of_Integer & q1, alters Queue_Of_Integer & q2 ); /*! ensures q1 = #q1 * #q2 !*/
6
Example while ( q2.Length() > 0 ) /*! alters q1, q2 maintains q1 * q2 = #q1 * #q2 decreases |q2| !*/ { } How does this guarantee proper functionality? What code would I fill in here to get the desired functionality?
7
Example while ( q2.Length() > 0 ) /*! alters q1, q2 maintains q1 * q2 = #q1 * #q2 decreases |q2| !*/ { object catalyst Integer temp; q2.Dequeue( temp ); q1.Enqueue( temp ); }
8
Why Optimizations should Wait object Integer k = q2.Length() while ( k > 0 ) /*! alters q1, q2, k maintains q1 * q2 = #q1 * #q2 k = |q2| decreases k !*/ {... } The Introduction of k makes reasoning about the logic, and the loop invariant more complicated. However, it will help with the runtime.
9
Formal comments and loops Loops work on variables already in existence. – The preserves, alters, produces, and consumes modes need to be made explicit for variables operated on by the loop. – This is done in the formal comment for the loop. You should be able to reason about what a loop does based upon its formal comment.
10
Another Example global_function Real Power ( preserves Real x, preserves Integer p ); /*! requires p > 0 ensures Power = x ^ (p) !*/
11
Version 1 object Real result = x; object Integer this_p = 1; while ( this_p <= p/2 ) /*! preserves p alters result, this_p maintains result = x ^ (this_p) and this_p <= p and there exists k: integer (this_p = 2 ^ (k)) decreases p – this_p !*/ {... }...
12
Version 1 object Real result = x; object Integer this_p = 1; while ( this_p <= p/2 ) /*! preserves p alters result, this_p maintains result = x ^ (this_p) and this_p <= p and there exists k: integer (this_p = 2 ^ (k)) decreases p – this_p !*/ { this_p = this_p * 2; result = result * result; } if ( p == this_p ) { return result; } else { return result * Pow( x, p – this_p ); }
13
Version 2 object Real result = 1.0; object Real factor = x; object Real p_left = p; while ( p_left > 0 ) /*! alters result, factor, p_left maintains p_left >= 0 and result * factor ^ (p_left) = x ^ (p) decreases p_left !*/ {... }
14
Exercise – Write a Loop Invariant for the while loop in the following code: while (q.Length() > 1) { object Boolean left, right, combined; q.Dequeue(left); q.Dequeue(right); combined = (left != right); q.Enqueue(combined); } What does this loop do? By this, I mean, get into groups of 2-4 and write a loop invariant for the above. Assume that q is a Queue_Of_Boolean. Hint: think about how many copies of true and false will be in q after each iteration. You can use [informal descriptions] if it is helpful
15
Exercise (Answer) while (q.size() > 1) /*! alters q maintains [number of true values in q] mod 2 decreases |q| !*/ { object Boolean left, right, combined; q.Dequeue(left); q.Dequeue(right); combined = (left != right); q.Enqueue(combined); }
16
Recall Closed Lab 6 In closed lab 6, we were inputting / outputting a tree from text in a prefix notation. So, text objects like the following recorded trees in prefix notation: – a(()()) – a(b(()())c(()())) a a cb
17
Contract given in the Lab global_procedure Get_Tree( alters Text& tree_as_text, produces Binary_Tree_Of_Character& t ); /*! requires there exists x, y: string of character, t1: binary tree of character (tree_as_text = x * y and x = PREFIX_DISPLAY (t1)) ensures #tree_as_text = PREFIX_DISPLAY (t) * tree_as_text !*/ Notice the purpose of y is to allow there to be junk at the end of the tree as text.
18
Alternative Contract global_procedure Get_Tree( alters Text & tree_as_text, produces Binary_Tree_Of_Character & t ); /*! requires there exists x: string of character, t1: binary tree of character (tree_as_text = x and x = PREFIX_DISPLAY (t1)) ensures #tree_as_text = PREFIX_DISPLAY (t) * tree_as_text !*/ How does eliminating y change how this would be implemented?
19
Code Correctness in Lab 6 When implementing Get_Tree, it is simplest to use recursive logic. Notice what happens if we are given the tree as text: – a(b(()())c(()())) The easiest implementation would make a recursive call passing in the following text string: – b(()())c(()())) This would violate the ensures clause in the 2 nd version of the contract. The code will produce correct output, but it is not correct.
20
Why does this distinction matter? Perhaps this does not matter so much when implementing recursive logic, since when implementing the logic you will be embedded in both the logic of the function making the call as well as the function you are calling. When working with 2 different functions, this matters more. When you update a function, giving it a new (hopefully better) implementation, you should not need to worry about what it will do to client code that incorrectly calls it. When code works (i.e., produces correct output) but is incorrect, we have a concrete-to-concrete dependency.
21
Code Correctness Example 2: /*! requires ASCII(c) != 255 ensures ASCII(c) != ASCII(#c) !*/ procedure_body bar( alters Character& c ) { c++; } /*! requires ASCII(c) != 255 ensures ASCII(c) = ASCII(#c)+1 !*/ procedure_body foo( alters Character& c ) { bar(c); } Does this code work? Is it Correct?
22
Code Correctness Example 2: Does the code work? -- Yes. Is it correct – No. Notice that providing another correct implementation for foo would make bar fail. How could we make it correct?
23
Closing Remarks Thank you for listening. I have had a fun time teaching this course. Friday Closed Lab will be an Exam Review. Similar to the midterm review, I will want you to come with questions. I will not be directly preparing material. Final Exam: Monday, March 12, 11:30 AM - 1:18 PM, in DL 369 (this classroom, not the lab room). DO NOT SKIP. A passing grade on the final is required to pass the course. Are there any questions? – these only need to be relevant if there is not extra time.
Similar presentations
© 2025 SlidePlayer.com. Inc.
All rights reserved.