Module 0078: Local var., by value, or by reference?

Tak Auyeung, Ph.D.

November 8, 2018

Contents

1 About this module
2 Abstracting a subroutine
 2.1 Recognizing similarities
 2.2 Identify differences
 2.3 Assign names
 2.4 Get rid of literal numbers
3 Attributes of a name
 3.1 A read-only name
 3.2 A read-first name
 3.3 A write-only name
 3.4 A write-first name
 3.5 A write-last name
 3.6 A name that ends with a constant value
4 Examples
 4.1 Bound-checked number input
 4.2 Factoring code

1 About this module

2 Abstracting a subroutine

As much as we want to practice top-down design (module 0049), it is often not possible design a program completely using the top-down method. Chances are that we will write code that is too long, too tedious, and too repetitive. As a result, we need to know how to restructure programs to make programs easier to maintain.

Let us begin with an example in listing 1.


Listing 1: tedious
1// listing 1 
2// tedious code to accept numbers with range checking 
3local L 
4local H 
5repeat  
6  print "enter a number between 1 and 100" 
7  read L  
8  if (L<1)(L>100) then  
9    print "error: the number is not between 1 and 100" 
10  end if 
11until (1L)(L100)  
12repeat  
13  print "enter a number between " L " and 100" 
14  read H  
15  if (H<L)(H>100) then  
16    print "error: the number is not between " L " and 100" 
17  end if 
18until (LH)(H100)  
19local i 
20iL 
21while iH do 
22  print i 
23  ii+1 
24end while

The code in listing 1 prompts the user to enter two numbers. L is the lower bound, and H is the upper bound. Both numbers should be between 1 and 100, with the additional requirement that H cannot be less than L (they can be the same). The last loop prints a range of numbers from L to H.

2.1 Recognizing similarities

Referring back to listing 1, the post-checking loop staring on line 5 is structurally similar to the post-checking loop starting on line 12:

Because there are so many similarities, we can consider abstracting both post-checking loops as a single subroutine.

2.2 Identify differences

Our next step is to identify the differences between the two post-checking loops. Listing 2 is a “template” that fits both post-checking loops. When there is a difference, we replace the disagreement with “?”.


Listing 2: template
1// listing 2 
2// a template with differences marked as ? 
3repeat 
4  print "enter a number between " ? " and 100" 
5  read ? 
6  if (?<?)(?>100) then 
7    print "error: the number is not between " ? " and 100" 
8  end if 
9until (??)(?100)

2.3 Assign names

Once we identify the differences, we also have to find consistent uses of the same name at the “?” points. If both post-checking loops consistently use the same name within the post-checking loop, we use a unique name in the template code.

For example, wherever we use the variable L on lines 7, 7 and 11 in the first post-checking loop, we also use the variable H consistently on lines 14, 14 and 18 in the second post-checking loop.

As a result, where L appears in the first loop, and H appears in the second loop, we can replace those “?” marks with a consistent name (n) in the template, yielding the code in listing 3.


Listing 3: template
1// listing 3 
2// template with a name "n" 
3repeat 
4  print "enter a number between " ? " and 100" 
5  read n 
6  if (n<?)(n>100) then 
7    print "error: the number is not between " ? " and 100" 
8  end if 
9until (?n)(n100)

Using the same method, we also identify that wherever “1” appears in the first loop, the second loop consistently use variable L. As a result, we can generalize that place holder as a new name b. This yields the code in listing 4


Listing 4: template
1// listing 4 
2// template with a names "n" and "b" 
3repeat 
4  print "enter a number between " b " and 100" 
5  read n 
6  if (n<b)(n>100) then 
7    print "error: the number is not between " b " and 100" 
8  end if 
9until (bn)(n100)

2.4 Get rid of literal numbers

Generally speaking, it is a good idea to remove literal constant terms in an algorithm. In our example, this is the “100” that appears a few times consistently in both loops. We proceed to replace all occurances of “100” with the name e. This yields the code in listing 5.


Listing 5: template
1// listing 5 
2// template with names "n", "b" and "e" 
3repeat 
4  print "enter a number between " b " and " e 
5  read n  
6  if (n<b)(n>e) then 
7    print "error: the number is not between " b " and " e 
8  end if 
9until (bn)(ne)

At this point, we have a template that is quite general. We use n to represent the number to be entered (by a user), b to specify the lower bound of the number, and e to specify the upper bound of the number.

3 Attributes of a name

In this section, we try to identify what name(s) in an algorithm should be a local variable, a by-value parameter, or a by-reference parameter. Some of these are rules of thumb, while others are more strict.

The following rules should be evaluated in the specified order. In other words, if a name fits an earlier rule, its type should be determined by the earlier rule. Evaluate more rules only if a name does not fit earlier rules.

3.1 A read-only name

A name that is read-only in an algorithm should be a by-value parameter.

The following are the rationale:

3.2 A read-first name

A name that has a read access as the first access cannot be a local variable. This is because a local variable does not have any specific initial value, therefore it make no sense to read a local variable as the first access.

3.3 A write-only name

A name that is only written to should be a by-reference parameter.

Rationale as follows:

3.4 A write-first name

A name that is written to as the first operation is not a by-value parameter. This is because the benefit of a by-value parameter is that the initial value is determined by the invoking code. If this value is overwritten anyway first thing in the code, then it does not make sense to make the name a by-value parameter.

3.5 A write-last name

A name that is written to as the last operation is usually a by-reference parameter.

Here is the rationale:

3.6 A name that ends with a constant value

A name that always ends with the same constant value when a subroutine returns is usually a local variable. However, if the name is already ruled out as a local variable, it is likely to be a by-value parameter.

Here is the rationale:

4 Examples

4.1 Bound-checked number input

Let us return to the code in listing 5, and determine the nature of each name.

As a result, the template code in listing 5 becomes a subroutine in listing 6.


Listing 6: sub
1// listing 6 
2// subroutine of listing 5 
3define sub readnum 
4  by value b 
5  by value e 
6  by reference n 
7  repeat 
8    print "enter a number between " b " and " e 
9    read n 
10    if (n<b)(n>e) then 
11      print "error: the number is not between " b " and " e 
12    end if 
13  until (bn)(ne) 
14end define sub

Once we have subroutine “readnum”, we can proceed to change the code in listing 1 to utilize the subroutine. The cleaned up code is in listing 7.


Listing 7: clean
1// listing 7 
2// cleaned up code using a subroutine call to replace listing 1 
3local L 
4local H 
5invoke readnum 1b,100e,Ln 
6invoke readnum Lb,100e,Hn 
7local i 
8iL 
9while iH do 
10  print i 
11  ii+1 
12end while

4.2 Factoring code

Let us revisit the code to factor a number from module 0049. It is relisted here as listing 8.


Listing 8: factoring
1repeat 
2  invoke findfactor ff,nn 
3  print f 
4  nn f 
5until n=1

Let us analyze the use of each name: