patternjavaMinor
Project Euler: Autorun problems
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
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
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:
As these are unwritten rules,
you might violate them without so much as a warning from the compiler.
Depending on which
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
using language features that are generally discouraged,
such as
You can make it type-safe:
This approach will solve the above issues:
-
Class must be in package
-
Class must be named following the scheme
-
Class must have a parameterless public constructor -> instantiation is no longer a concern, the responsibility is left up to callers of
-
Class must have a public parameterless method named
-
Tricky
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.
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(), returningint
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
Probleminterface with asolvemethod
- Make the
Problem%03dclasses implementProblem
- Of course, you no longer need to follow the
Problem%03dnaming scheme
- One simple reason for ditching the naming scheme is the ability for multiple alternative implementations of the same problem, for example
Problem003WithSieveOfEratosthenesandProblem003WithSieveOfSundaramorProblem003WithBruteForce.
- Add a
register(Problem)method inExecutorto 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 simplifiedLastly,
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.