A.1. The shortest I came up with    [Golfing]

When code golfing, it’s important to establish the constraints you’re supposed to respect. A looser interpretation of the rules may lead to a shorter solution, but you don’t want to end up with a class that works only with a specific use case. Let’s establish the boundaries of this exercise:

  • We want a Container class that fulfills the standard use case established in chapter 1 and repeated throughout the book.
  • This class must also respect the functional specifications laid out in chapter 1.
  • We don’t require anything else: no robustness, no performance constraints, and especially no readability.

In my solution, I represent a group of connected containers using a circular list, just like Speed2. Instance field n (for next) is a pointer to the next container in the group.

Also like Speed2, when you add water with addWater, the amount is stored locally in the instance field, a, and is never actually distributed among the other connected containers. As a consequence, every call to getAmount needs to scan the whole group, sum up the amounts that every container holds, and finally return the total amount divided by the size of the group.

Before I present the actual code, here’s a legend for the five instance fields:

  • a—Total amount ever added to this container.
  • s,t—Temporary variables that getAmount needs; under normal circumstances, they’d be local variables of that method; moreover, s should really be an integer. I’m declaring them as fields because doing so saves a few characters.
  • n—Pointer to the next container within the list representing the group of this container.
  • c—Temporary variable that both connectTo and getAmount use; when not executing those methods, c equals n.

Take a look at the following code for the compact Container implementation. I left some basic white space and indentation for readability. If I remove all unnecessary white space, the class measures 223 bytes and still works as intended. For a comparison, Reference takes 1322 bytes, including white space.

Listing A.1. Golfing: Water containers in 223 bytes
public class Container {
   float a,s,t;  1 s,t are used like local variables
   Container n=this,c=n;
   public float getAmount() {
      for(s=t=0;s<1||c!=n;c=c.n,s++)  2 Notice the comma
         t+=c.a;
      return t/s;
   }
   public void connectTo(Container o) {
      c=o.n; o.n=o.c=n; n=c;  3 Swaps next pointers
   }
   public void addWater(float w) {
      a+=w;  4 Just accumulates locally
   }
}

To understand this obscure implementation, start by reading the addWater method, which is the easiest. The newly added water is summed to the a (for amount) field, and no other line modifies that field. Hence, the a field of a given container indicates the amount of water ever added to that container.

Then, move to the method connectTo. Recall from chapter 3 that merging two circular lists starting from arbitrary nodes is particularly easy: it suffices to swap their “next” pointers. The method connectTo does exactly that. Moreover, it updates the value of the support variables c and o.c to be equal to the new (that is, swapped) value of n and o.n, respectively.

Nested Assignments

As in C, you can concatenate multiple assignments in Java. Such a sequence is evaluated right to left, so all variables in the sequence are assigned the value of the rightmost expression.

Finally, there’s the rather daunting loop in getAmount. Its purpose is to compute the total amount in all containers in this group, while at the same time measuring the size of the group. After the loop, you can find the total amount in the variable t and the size of the group in s, which explains why the method returns t/s.

Commas in for Loops

In C, the expression exp1,exp2 evaluates both expressions in order and then returns the value of the second one. In Java, a similar syntax is allowed only inside the first and third clause of a for loop. It’s meant to gracefully support loops with multiple indices:

for (i=0, j=n; i<n; i++, j-{}-) ...

With this in mind, the loop initialization and update parts should be quite clear. Both the size and the total amount start at zero. For each iteration, the size is incremented by one, and the container pointer c moves to the next container in the group.

The staying condition requires some explanation. The loop must stop when it has visited the whole circular list, that is, when the container pointer goes back to its original value. In our case, the container pointer c starts with the value of the next pointer n. As a result, the loop must continue as long as c!=n. But there’s a catch: the for loop checks its staying condition before each iteration. To force the loop to perform at least one iteration, I had to add the staying condition s<1.

It’s very likely that shorter solutions exist. Can you find one? If you do, drop me a line, I’d be glad to hear about it!

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.226.187.24