在过去的几个月里,我尝试使用函数式编程范例进行编码。现在我有一个OOP解决方案,我正在尝试找到一个功能性解决方案。
问题很简单。我有一个算法,它产生两个不同的数组作为结果(a和b)。现在,我想检查结果有多好。因此,我为他们写了几个评估标准。我希望伪java源代码对你有用!
// 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
}
}
是否可以使用函数式编程范例“转换”此源代码? 我确信这一点,但您是否仍然可以像在OOP方法中那样轻松添加新的评估标准?
我可以编写一个名为algorithm的模块,其中包含计算a或b的纯函数。在这种情况下,我必须计算两次,这需要很长时间。
但是如何使用多个评估函数进行评估步骤?
答案 0 :(得分:11)
但是如何使用多个评估函数进行评估步骤?
您将要使用的函数作为一等值传递。
您的类型EvaluationCriterion
基本上只是函数类型Algorithm -> Double
(在Haskell语法中)。 EvaluationA
,EvaluationB
等不需要是新类型。它们只是EvaluationCriterion
类型的值。您可以将它们作为值传递,构建类型[EvaluationCriterion]
等的列表
具有讽刺意味的是,您已经在解决方案中使用了一流的功能。这种抽象弱的OOP语言缺乏一流的功能,因此您必须应用标准化的解决方法(“设计模式”)。将其转换为函数式语言(或仅仅是合理的OOP语言)是删除复杂性的问题。
现在,至于如何消除Algorithm
的状态,我没有想到这一点。但请记住,FP并不意味着“没有状态”。缓存纯函数结果是很常见的事情。
答案 1 :(得分:3)
这样的事情:
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
这个特殊的例子标准当然是随意的无意义;您必须提供合适的功能和类型Input
,A
,B
。
答案 2 :(得分:1)
我认为你的算法类可以简单地简化为(Scala伪代码 - 我认为这比Haskell或Clojure更容易理解,因为它更接近Java)
def computeResult(input: Input): (List[Result1], List[Result2]) = ...
其中(a,b)是一个元组,一个围绕两个值a和b的简单包装器。
EvaluationCriterion可以是
trait EvaluationCriterion {
def evaluate(algo : Input => (List[Result1], List[Result2])): Double
}
如果你有这些标准的序列(例如列表),你可以写
evaluationCriterias.map(crit => (crit.getClass.toString, crit.evaluate(computeResult _)))
会产生类似Seq((CritClass1, 1.2), (CritClass2, 0.99), (CritClass3, 0.54))
答案 3 :(得分:1)
既然你提到了Clojure,这是一个简单的方法:
(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)))
基本上,您将算法定义为包含两个函数的地图(标记为:a和:b),如果需要,您可以轻松地将其他数据或函数添加到算法中。
然后评估函数只对输入上给定算法调用两个函数的结果执行计算。
如果你想变得非常聪明,那么你可以按如下方式创建一个宏:
(defmacro build-evaluation-function [computation]
`(fn [algorithm# input#] (~computation ((:a algorithm#) input#) ((:b algorithm#) input#))))
然后你可以使用任何带有两个参数作为计算的函数来构建任意数量的评估函数:
(def evaluation-2 (build-evaluation-function add))
(def evaluation-3 (build-evaluation-function str))
; etc....