面向对象编程 —— java实现函数求导

先是声明一点,本文主要介绍的是面向对象(OO)的合计,顺便说下函数式编程,而未是使你怎样规范地、科学地用java求来函数在某些的导数。

 

一、引子

 

def d(f) :
    def calc(x) :
        dx = 0.000001  # 表示无穷小的Δx
        return (f(x+dx) - f(x)) / dx  # 计算斜率。注意,此处引用了外层作用域的变量 f
    return calc  # 此处用函数作为返回值(也就是函数 f 的导数)
# 计算二次函数 f(x) = x2 + x + 1的导数
f = lambda x : x**2 + x + 1  # 先把二次函数用代码表达出来
f1 = d(f)# 这个f1 就是 f 的一阶导数啦。注意,导数依然是个函数
# 计算x=3的斜率
f1(3)
# 二阶导数
f2 = d(f1)

率先,直接上平等段落python代码,请大家先分析下方面代码是故啊艺术求导的。请不要受当下段代码吓到,你随便需纠结它的语法,只要知道它们的求导思路。

以上代码引用自《呢啥我推荐 Python[4]:作为函数式编程语言的
Python》,这首博客是促使自己写篇稿子的重大缘由。

博主说“如果非用 FP,改用 OOP,上述要求该怎么实现?俺当吧,用 OOP
来求导,这代码写起多半是以可恨又可恨。”

自己以信将疑,于是便用面向对象的java试了试,最后也远非小代码。如果因此java8或者之后版本,代码更不见。

伸手大家想一个题目,如何用面向对象的笔触改写这个次。请预好好想想,尝试编个程序还累往生看。

设想到目是题目上的同窗大多是拟过java的,下面我用java,用面向对象的笔触一步步分析者题材。

 

二、求导

 

章开始我都接近声明了了,本文不是来讨论数学的,求导只是本人于是来验证面向目标的一个例证。

若果你曾经淡忘了开头那段代码的求导思路,请回头再探,看看用python是何等求导的。

深信不疑你若听说了求导,肯定一眼便见到开头那段代码是因此导数概念求导的。

C语言 1

代码中就是将无穷小Δx粗略地到底做一个比小之值0.000001。

 

其三、最初的想法

 

//自定义函数
public class Function {
    //函数:f(x) = 3x^3 + 2x^2 + x + 1
    public double f(double x) {
        return 3 * x * x * x + 2 * x * x + x + 1;
    }
}

//一元函数导函数
public class DerivedFunction {
    //表示无穷小的Δx
    private static final double DELTA_X = 0.000001;
    //待求导的函数
    private Function function;

    public DerivedFunction(Function function) {
        this.function = function;
    }

    /**
     * 获取function在点x处的导数
     * @param x 待求导的点
     * @return 导数
     */
    public double get(double x) {
        return (function.f(x + DELTA_X) - function.f(x)) / DELTA_X;
    }
}

public class Main {
    public static void main(String[] args) {
        //一阶导函数
        DerivedFunction derivative = new DerivedFunction(new Function());
        //打印函数在x=2处的一阶导数
        System.out.println(derivative.get(2));
    }
}

事先声明一点,考虑到博客篇幅,我下了无规范的代码注释,希望大家不用为自己误导。

本身思如果大家好思考了,应该至少会想到这步吧。代码我便未说明了,我只是用java改写了篇开始的那段python代码,做了一个大概的翻译工作。再要大家着想生以上代码的题目。

刚巧起,我考虑是问题想开的是构筑一个名叫吧Function的类,类中起一个名为f的章程。但考虑到要每次要求新的函数导数时即得改变这个f方法的贯彻,明显不便于扩展,这违反了开闭原则。

量有同学没有听了之词,我就是解释下:”靶(类,模块,函数等)应本着扩大开放,但针对修改封闭“。

遂自己就是从未有过累写下来,但为让大家直观的感触及这想法,我勾勒就首博客时就落实了瞬间这想法。

吁大家想一下怎样重构代码以化解扩展性问题。

 

季、初步的想法

 

估价学过面向对象的同学会想到将Function类改成为接口或抽象类,以后每次上加新的函数时要再写这个接口或抽象类吃之f方法,这虽是面向接口编程,符合仗反转原则,下面的代码就是这么做的。

再也声称一点,考虑到篇幅的题目,后面的代码我会省去与之前代码重复的笺注,有无明了的地方还请看上一个设法被之代码。

//一元函数
public interface Function {
    double f(double x);
}

//自定义的函数
public class MyFunction implements Function {
    @Override
    public double f(double x) {
        return 3 * x * x * x + 2 * x * x + x + 1;
    }
}

public class DerivedFunction {
    private static final double DELTA_X = 0.000001;
    private Function function;

    public DerivedFunction(Function function) {
        this.function = function;
    }

    public double get(double x) {
        return (function.f(x + DELTA_X) - function.f(x)) / DELTA_X;
    }
}

public class Main {
    public static void main(String[] args) {
        //一阶导函数:f'(x) = 9x^2 + 4x + 1
        DerivedFunction derivative = new DerivedFunction(new MyFunction());
        System.out.println(derivative.get(2));
    }
}

自家怀念认真看的同班可能会见意识一个问题,我的翻译做的尚非做到,开头那段python代码还好轻松地请求来二阶导函数(导数的导数),而自己之代码却百般。

实际要稍微修改上述代码的一个地方即可以轻松实现求其次阶导,请复想想片刻。

 

五、后来之想法

 

当自己形容有方的代码时,我感觉到完全可以矢口否认“用 OOP
来求导,这代码写起来多半是又可恨又可恨”的理念。但还未可知请其次阶导,我有硌不甘心。

遂自己不怕动笔,列了瞬间用定义求一阶导和求二阶导的相,想了纪念少独姿态的区别及联络,突然想到导函数为是函数。

DerivedFunction的get方法以及Function的f方法的参数和返回值一样,DerivedFunction可以兑现Function接口,于是发出了底的代码。

public interface Function {
    double f(double x);
}

public class DerivedFunction implements Function {
    private static final double DELTA_X = 0.000001;
    private Function function;

    public DerivedFunction(Function function) {
        this.function = function;
    }

    @Override
    public double f(double x) {
        return (function.f(x + DELTA_X) - function.f(x)) / DELTA_X;
    }
}

public class Main {
    public static void main(String[] args) {
        Function f1 = new DerivedFunction(new Function() {
            @Override
            public double f(double x) {
                return 3 * x * x * x + 2 * x * x + x + 1;
            }
        });
        System.out.println(f1.f(2));
        //二阶导函数:f''(x) = 18x + 4
        Function f2 = new DerivedFunction(f1);
        //打印函数f(x) = 3x^3 + 2x^2 + x + 1在x=2处的二阶导数
        System.out.println(f2.f(2));
    }
}

设想到一些同学没有学过java8还是以上版本,以上代码没有用java8函数式编程的新特点。 

若是你沾过java8,请考虑如何改写以上代码,使该还精简。

 

六、最后的想法

 

public class DerivedFunction implements Function<Double, Double> {
    private static final double DELTA_X = 0.000001;
    private Function<Double, Double> function;

    public DerivedFunction(Function<Double, Double> function) {
        this.function = function;
    }

    @Override
    public Double apply(Double x) {
        return (function.apply(x + DELTA_X) - function.apply(x)) / DELTA_X;
    }
}

public class Main {
    public static void main(String[] args) {
        //打印函数在x=2处的二阶导
        System.out.println(new DerivedFunction(new DerivedFunction(x -> 3 * x * x * x + 2 * x * x + x + 1)).apply(2.0));
    }
}

事先几乎只想法为了扩大Function接口,使用了标类、匿名类的措施,其实也足以据此中类。而立在此处,我之所以了lambda表达式,是匪是再简短了。

这边用之Function接口用之凡jdk自带的,我们无需好定义了。因为及时是一个函数式接口,我们得用lambda方便地贯彻。后来发觉,其实这里用UnaryOperator是接口更方便。

现行大家来没有产生发现,用java、用OOP也得非常简洁地实现求导,并无可比初步的那段python代码麻烦大多。

 

七、编程范式

 

在我看来,编程范式粗略的话就是是编程的同一栽模式,一种植风格。

自家先行介绍其中的老三个,你多就掌握它的意义了。

 

7.1 面向对象程序设计(OOP)

盼此的同室应该针对面向对象有了再度直观的认。在面向对象编程中,万物皆对象,抽象出类的定义。基本特点是包裹、继承、多态,认识不雅的同校可以重复失去自己事先的代码中搜寻找就三独特性。

本身事先还介绍了面向对象的几只尺码:开闭原则、倚反转原则。其他还有纯任务规范、里氏替换原则、接口隔离原则。这是面向对象的5个基本尺度,合称SOLID)。

 

7.2 函数编程语言(FP)

本文开始那段代码用之饶是python函数式编程的语法,后来自家又用java8套数式编程的语法翻译了这段代码。

相信您早已直观地感受及它们的洗练,以函数为主干,几行代码就迎刃而解了求导的题材。

 

7.3 过程式编程(Procedural programming)

粗粗学了编程都如法炮制过C,C语言就是平等种过程式编程语言。在我看来,过程式编程大概就是是为好一个需求,像记流水帐一样,平铺直叙下去。 

       

八、结尾

 

由自身初学java,目前只好想到这样多。如果大家发重好之想法要觉的自家面说之起题目,欢迎评论,望各位不吝赐教。

当即是我的第一首技术博客,但愿我说清楚了面向对象。如果对您生出帮衬,请点单赞或者评论下,给自身碰持续写的动力。