views:

450

answers:

4

During the last months I have tried to code using the functional programming paradigm. Now I have a solution in OOP and I am trying to find a functional solution.

The problem is simple. I have an algorithm, which produces two different arrays as result (a and b). Now, I want to check how good the results are. Therefore I write several evaluation criteria for them. I hope pseudo-java source code is okay for you!

// first the Algorithm class
class Algorithm {
    private []a;
    private []b;

    Algorithm(input) {
        computeResult();
    }

    getA(){return a;}
    getB(){return b;}

    void computeResult() { 
        ... 
        ... // time-consuming operations 
        ... // set values for a and b
        ...
    }
}

// this class tests the Algorithm using a list of evaluation criteria
class AlgorithmTest {

    AlgorithmTest() {
        ... 
        ... // Definition of input
        ... // Definition of list of evaluation criteria evals
        ...
        Algorithm algorithm = new Algorithm(input); // Compute the result

        for (EvaluationCriterion eval : evals) {
            System.out.println(eval.getClassSimpleName()); // Print name of class
            System.out.println(eval.evaluate(algorithm));  // Print evaluation result
        }
    }

    main () {
        new AlgorithmTest();
    }
}

interface EvaluationCriterion {
    double evaluate(Algorithm a);
}

// an example implementation of a criterion
class EvaluationA implements EvalutationCriterion{
    double evaluation(Algorithm algorithm) {
        a = algorithm.getA();
        b = algorithm.getB();
        double c = anotherComputation(a, b);
        return c;
    }

    double anotherComputation(a, result){
        ... // compute and return result
    }
}

Is it possible to "transform" this source code using functional programming paradigm? I am sure it it, but can you still add new evaluation criteria easily like in the OOP approach?

I could write a module called algorithm that includes pure functions that compute either a or b. In this case I have to compute it twice, which requires to much time.

But how to do the evaluation step using multiple evaluation functions?

+11  A: 

But how to do the evaluation step using multiple evaluation functions?

You'd pass in the functions you want to use, as first-class values.

Your type EvaluationCriterion is basically just the function type Algorithm -> Double (in Haskell syntax). EvaluationA, EvaluationB, etc. don't need to be new types. They're just values of type EvaluationCriterion. You can pass them as values, build a list of type [EvaluationCriterion], etc.

The irony here is that you've already used first-class functions in your solution. This abstraction-weak OOP language lacks first-class functions, so you had to apply a standardized workaround ("design pattern"). Converting it to a functional language (or just a reasonable OOP language) is a matter of removing complexity.

Now, as to how to eliminate the statefulness of Algorithm, I haven't thought about that. But do remember that FP does not mean "no state ever". Caching pure function results is quite a common thing to do.

keegan
Thanks! That helped me a lot!
TintuSa
+1  A: 

I think your Algorithm-class could be simply reduced to (Scala pseudocode - I think this is easier to understand than Haskell or Clojure as it is closer to Java)

def computeResult(input: Input): (List[Result1], List[Result2]) = ...

where (a,b) is a Tuple, a simple wrapper around two values a and b.

EvaluationCriterion could be

trait EvaluationCriterion {
   def evaluate(algo : Input => (List[Result1], List[Result2])): Double
}

If you have a Sequence (e.g. a List) of these criterias, you can write

evaluationCriterias.map(crit => (crit.getClass.toString, crit.evaluate(computeResult _)))

which would result in something like Seq((CritClass1, 1.2), (CritClass2, 0.99), (CritClass3, 0.54))

Landei
+3  A: 

Something like this:

type Algorithm = Input -> (A,B)

type Accuracy  = Double
type EvaluationCriterion = Algorithm -> Accuracy

example :: EvaluationCriterion
example f = size a / (size a + size b)
    where
    (a,b) = f 42

This particular example criterion is random nonsense, of course; you have to supply suitable functionality and types Input, A, B.

Heinrich Apfelmus
+1  A: 

Since you mentioned Clojure, here's an easy way to do it:

(def algorithm-1 
  {:a some-calculate-a-for-algorithm-1
   :b some-calculate-b-for-algorithm-2})

(defn evaluation-1 [algorithm input]
  (some-computation-1 ((:a algorithm) input) ((:b algorithm) input)))

Basically you define your algorithms as being a map containing two functions (labelled :a and :b), you could easily add other data or functions into the algorithms if desired.

Then the evaluation functions just execute a computation on the results of calling the two functions for a given algorithm on the input.

If you want to get really clever, then you can create a macro as follows:

(defmacro build-evaluation-function [computation]
  `(fn [algorithm# input#] (~computation ((:a algorithm#) input#) ((:b algorithm#) input#))))

And then you can build as many evaluation functions as you like, using any function that takes two parameters as the computation:

(def evaluation-2 (build-evaluation-function add))

(def evaluation-3 (build-evaluation-function str))

; etc....
mikera
p.s. noticed that in the question the approach does the result computation once - easy to modify the above code to do this by adding an extra :computation field to the algorithms and calling this function first in the evaluation functions, then passing the result to :a and :b instead of the input.
mikera