HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavaMinor

Project Euler: Autorun problems

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
problemsautorunprojecteuler

Problem

I've found that it's a huge pain to have to write the boilerplate code for each Project Euler problem I write (timing code, main method, what about running every problem instead of just one?). So I decided to create a program to run the problems for me.

Main.java

package projecteuler;

import projecteuler.autorun.Executor;

import java.util.stream.IntStream;

public class Main {
    public static void main(String[] args) {
        Executor executor = new Executor();
        executor.addAll(IntStream.range(1, 500).boxed()::iterator);
        executor.run();
    }
}


Problem.java

```
package projecteuler.autorun;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.Objects;
import java.util.concurrent.Callable;

public class Problem implements Runnable {
private final String name;
private final Callable problem;
private String result;
private double time;

private Problem(String name, Callable problem) {
this.name = name;
this.problem = problem;
}

/**
* Runs the problem, timing it and storing the result. This must
* be called before the getters on result and time
*/
@Override
public void run() {
try {
long start = System.nanoTime();
result = problem.call();
time = (System.nanoTime() - start) * 1e-9;
} catch (Exception e) {
e.printStackTrace(); // Swallow; we want to continue executing problems even if this problem failed.
// I think that better would be to use a logger to log
// this, but I'm unsure how to go about doing so.
}
}

/**
* Attempts to create a problem for the given number. If no suitable problem exists, throws an Exception.
*
* The given Problem must exist under the package projecteuler.problems under the name
* ProblemNUM, where NUM is the problem's number

Solution

Type safety

The problem implementations must follow several unwritten rules:

  • Class must be in package projecteuler.problems



  • Class must be named following the scheme Problem%03d



  • Class must have a parameterless public constructor



  • Class must have a public parameterless method named problem(), returning int



As these are unwritten rules,
you might violate them without so much as a warning from the compiler.
Depending on which Executor.add* method you call,
any problems thrown while creating an instance may be silently ignored,
which can be especially frustrating to debug.
Another consequence of the current approach is that the implementation of Problem.number is non-trivial,
using language features that are generally discouraged,
such as Class.newInstance.

You can make it type-safe:

  • Create a Problem interface with a solve method



  • Make the Problem%03d classes implement Problem



  • Of course, you no longer need to follow the Problem%03d naming scheme



  • One simple reason for ditching the naming scheme is the ability for multiple alternative implementations of the same problem, for example Problem003WithSieveOfEratosthenes and Problem003WithSieveOfSundaram or Problem003WithBruteForce.



  • Add a register(Problem) method in Executor to register implementations by passing a single instance



This approach will solve the above issues:

-
Class must be in package projecteuler.problems -> restriction lifted, implementations of Problem can be anywhere

-
Class must be named following the scheme Problem%03d -> restriction lifted, classes can be named anything, multiple alternative implementations possible

-
Class must have a parameterless public constructor -> instantiation is no longer a concern, the responsibility is left up to callers of Executor.register

-
Class must have a public parameterless method named problem(), returning int -> now the compiler can enforce it

-
Tricky Problem.number -> instantiation is no longer a concern, the overall implementation can be greatly simplified

Lastly,
I also concern the original requirements a usability issue (limiting reuse).

Utility

I can't help but wonder, what's the point of this?
Why run many solutions at once?
When I implement a solution for an online contest,
For example, I have a very different need:
writing in my local IDE (where it's the most comfortable),
in a way that's easy to re-copy-paste to the contest web interface.
My 2 cents.

Context

StackExchange Code Review Q#101203, answer score: 5

Revisions (0)

No revisions yet.