Skip to content

Instantly share code, notes, and snippets.

@bananu7
Created November 9, 2020 12:21
Show Gist options
  • Select an option

  • Save bananu7/0d15a47730266ba0a6c74ee9425d6865 to your computer and use it in GitHub Desktop.

Select an option

Save bananu7/0d15a47730266ba0a6c74ee9425d6865 to your computer and use it in GitHub Desktop.

When presented with such a problem, it helps to break it down into smaller sub-problems first to solve individually, before composing together into the solution. While it's technically quite possible to write it as one function, it can help in understanding the issue and implementation and ultimately yield cleaner code.

We know that we have to write a program that:

  • Reads the data for two rectangles,
  • Computes the common area,
  • Prints it out.

Reading the data is actually often more complex than writing it in programming. This is because the data is often coming in a different format than what we need it for our purposes. In this case, the rectangle data is gonna arrive as characters on standard input (presumably separated by whitespace), and those characters will encode the x,y, w and h of two rectangles. For our purposes, it's best to have them stored as numbers in memory, so we need to parse the input, converting into our format.

Since the 4 numbers denoting a rectangle essentially belong together, it's also gonna be helpful to introduce a model of a rectangle. One way to do that is by using a struct; this will give us a much cleaner representation than e.g. 4 separate variables, and will make the code more readable. In fact, that's what we'll start with:

struct Rectangle {
    int x;
    int y;
    int w;
    int h;
};

Now that we have a formal (understandable by the compiler, expressed in real code) model, we can formalize the other parts. Here's where we're going to design the interfaces of our functions. We're not writing the implementation yet, just thinking about the program structure. This part is important, because it will influence how the rest of the program will look like greatly.

For reading the rectangles, we probably want a function like that - taking no parameters, and returning a rectangle:

Rectangle readRectangle();

This isn't the only way to write it, but for our simple purposes it's enough. Head to the end to read more about it.

For computing the common area, the signature can look like this:

int commonArea(Rectangle a, Rectangle b);

This, again, could have more considerations, but we'll roll with that for now. Simplicity is what we're after, since this is a simple task. Printing the resulting integer is an operation simple enough that it doesn't warrant a separate subroutine in our program.

Combining those parts should be straightforward enough; we call readRectangle twice to obtain the necessary data and parse it into our model. Then we pass the two rectangles to commonArea to get the value, and ultimately we print the value to the standard output and exit.

Let's take a look at what we have so far, putting it into a program skeleton you should already know from "hello, world!":

#include <iostream>

struct Rectangle {
    int x;
    int y;
    int w;
    int h;
};

Rectangle readRectangle() {
    // TODO: implement
}

int commonArea(Rectangle a, Rectangle b) {
    // TODO: implement
}

int main() {
    Rectangle a = readRectangle();
    Rectangle b = readRectangle();
    
    int area = commonArea(a, b);
    
    std::cout << area << '\n';
}

You can see how the approach we took at the start is paying off; we wrote no actual code that solves the problem yet, but we have a really good understanding of the problem itself, and the path that we need to take to solve it. This is what's often called a "top-down" approach. Now that we have the structure, we can fill the "bottom" of our program by implementing the two necessary functions.

Note that those two functions lie in complete isolation from the rest of the program. This is extremely important, because it greatly simplifies our job. We need not be concerned with anything that came before or what's gonna come after. Right now, when implementing one function, we can think only about its interface.

Let's start with readRectangle. A common way to read data from the standard input is by using the cin stream object. If we create a temporary variable of a type Rectangle, we can read directly into its members, like so:

Rectangle readRectangle() {
    Rectangle temp;
    std::cin >> temp.x >> temp.y >> temp.w >> temp.h;
    return temp;
}

We're assuming that the input is well-formed - which means that it will always match exactly what we're expecting. If it's not, the program will behave in unpredicted ways. If this program had the possibility of dealing with invalid input, we would need to add more code that checks the input validity, and deals with the case where the input is actually invalid, e.g. "10 20 thirty 40".

The final step is implementing commonArea.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment