成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

如何在函數式編程中處理可變狀態和副作用?

開發
函數式編程所強調的的不可變性和無副作用,能夠幫助我們編寫出更加穩定可靠的代碼,構建更加健壯的系統。

函數式編程的不可變和無副作用

首先函數式編程中的比較鮮明的特性就是不可變性和無副作用。

可變 VS 不可變

不可變性簡單點說,就是不會改變已經定義的變量

1.變幻莫測的對象狀態

在面向對象或者面向過程式的編程中,當遇到一些需要計算累計值的時候,我們通常會定義某個變量,再對變量的賦值不斷更新,最后輸出變量的最終結果。

假設需要計算班級某門課程平均分,學生分數結構如下:


class StudentScore {
    public String id;

    public String studentId;

    public String courseId;

    public String classId;

    public Double score;

    public StudentScore(String id, String studentId, String courseId, String classId, Double score) {
        this.id = id;
        this.studentId = studentId;
        this.courseId = courseId;
        this.classId = classId;
        this.score = score;
    }

    public Double getScore() {
        return score;
    }
}

在面向對象或者面向過程式的編程中,我們通常會將計算平均分的方法的實現寫成下面這樣:

    public Double avgScore(List<StudentScore> studentScores) {
        Double sumScore = 0d;
        for (StudentScore studentScore : studentScores) {
            sumScore += studentScore.getScore();
        }
        return sumScore / studentScores.size();
    }

2.函數式與不可變

在函數式編程中,某個變量被定義了之后就不會再改變。同樣的計算累計值的場景,在函數式編程中則可以被定義為一連串的函數的鏈式調用,最后返回最終的結果。

    public Double avgScoreFP(List<StudentScore> studentScores) {
        return studentScores.stream().map(StudentScore::getScore)
            .reduce((d1, d2) -> (d1 + d2) / 2).orElse(0d);
    }

這么做的好處就是,代碼會更加健壯可靠,對于問題的調查也會更加容易。當我們發現某個計算值有誤時,在可變變量的場景中,我們就需要結合實際代碼,調查變量所有引用和改動的地方。

當定義的變量都不可變時,問題只會出現在某個較小的函數的計算當中,這部分計算邏輯中。我們只需要關注函數的輸入和輸出就能調查出具體問題是出在函數調用鏈的哪個環節上。然后再針對該函數編寫相應的單元測試用例,便能保證代碼的穩定性。

而且鏈式的函數調用,每個函數都是較小的計算單元,測試用例的場景也會相對較小,編寫單元測試用例時,也會加簡單容易。

無副作用 VS 副作用

無副作用是指函數的實現時,不應該對入參做任何更改,并保證對系統是無影響的。這和面向對象編程是有很大差別的。

1.副作用

比如需要更新學生成績時,面向對象編程,則可能是學生成績類,會具有一個可以直接設置新成績的方法來更新學生成績。

    public void updateScore(StudentScore studentScore, Double newScore) {
        studentScore.setScore(newScore);
    }

這種實現方式,無疑已經對入參 studenScore 造成了影響。如果有更復雜的邏輯,多次更新 studentScore 的 score 屬性的值,那么最終,誰也無法預知原先的這個 studentScore 的最終狀態是什么樣子。

面向對象的最大問題,就是對象狀態的不確定性。某個對象經過一連串的方法調用后,很難判斷出對象的最終狀態,其中如果涉及到緩存,并發等問題,問題的調查則會更加困難。

2.無副作用

在函數式編程中,則完全不同,我們需要定義一個函數,入參為原學生科目信息,和需要更改的成績最新值,返回值則將是另一個新的學生成績實例。

  public StudentScore updatedScoreFP(StudentScore studentScore, Double newScore) {
        return new StudentScore(studentScore.id, studentScore.studentId, studentScore.courseId, studentScore.classId, newScore);
    }

而上面的這種寫法,我們能夠保證原先的 studentScore 是不會被更改的,這個函數無論入參怎么更換,最終的輸出都是一個新的 StudentScore 對象。這個函數無論入參怎么變化,無論被調用多少次,對外部系統都是無影響的。

函數式編程所強調的無副作用,是指函數的調用不會對系統、入參造成任何函數功能以外的影響。同一個對象無論調用某個函數多少次,該對象的屬性依舊不變。對象新的狀態則是通過新的對象體現。這雖然會耗費一些資源,但是能使我們編寫的代碼更加穩定可靠。

函數式編程的語法支持

對于變量不可變性的實踐,java中可以盡量在變量的定義時使用final關鍵字修飾。對于無副作用的實踐,java中并沒有專門的語法糖支持,但是JDK1.8之后的 Stream 操作( map, reduce, groupBy 等)以及相關的函數式編程相關的支持都是值得去實踐的。

在Scala中對于對象的不可變性,可以通過 case class 來定義純數據類,保證相關的數據類實例的不可變性,對于一般變量則通過 var 和 val 區分變量是否可變,一般變量盡量使用val關鍵字修飾以保證其不可變。無副作用的實踐上,Scala中對于類對象的操作則可以封裝在類的伴生對象中。當然也需要自己在開發過程中具備保證函數無副作用的意識。

上面例子的Scala 2實現如下:


package demo.basic

case class StudentScore(id: String,
                        studentId: String,
                        courseId: String,
                        classId: String,
                        score: Double) {
    override def toString: String =
        s"StudentScore:{id: ${id}, studentId:${studentId}, courseId:${courseId}, score: ${score}}"
}

object StudentScore {
    def avgScore(studentScores: Array[StudentScore]): Double = {
        studentScores.map[Double](s => s.score).reduce((s1, s2) => (s1 + s2) / 2)
    }

    def updateScore(studentScore: StudentScore, newScore: Double): StudentScore = {
        StudentScore(studentScore.id, studentScore.studentId, studentScore.courseId, studentScore.classId, newScore)
    }
}

object FunctionalProgramingDemo {
    def main(args: Array[String]): Unit = {
        val s1 = StudentScore("id-0001", "student-0001", "course-0001", "class-0001", 83.5)
        val s2 = StudentScore("id-0002", "student-0002", "course-0001", "class-0001", 82.0)
        val s3 = StudentScore("id-0002", "student-0003", "course-0001", "class-0001", 81.0)
        val scores = Array(s1, s2, s3)
        val avgScore = StudentScore.avgScore(scores)
        println(avgScore)
        val s3New = StudentScore.updateScore(s3, 79.5)
        println(s3)
        println(s3New)
        /*
        81.875
        StudentScore:{id: id-0002, studentId:student-0003, courseId:course-0001, score: 81.0}
        StudentScore:{id: id-0002, studentId:student-0003, courseId:course-0001, score: 79.5}
        * */
    }
}

總結

總之函數式編程所強調的的不可變性和無副作用,能夠幫助我們編寫出更加穩定可靠的代碼,構建更加健壯的系統。

  • 以上涉及到Java部分的代碼的 GitHub 鏈接:https://github.com/stevenzearo/ichat/blob/master/demo/java-demo/src/main/java/basic/FunctionalProgramingDemo.java
  • 涉及到Scala部分的代碼的 GitHub 鏈接:https://github.com/stevenzearo/scala-gradle/blob/master/src/main/scala/demo/basic/FunctionalProgramingDemo.scala
責任編輯:趙寧寧 來源: FrenziedJavaLand
相關推薦

2020-03-28 14:57:29

JavaScrip代碼函數

2018-08-27 14:50:46

LinuxShellBash

2018-11-12 09:50:56

Python函數式編程數據結構

2011-04-21 17:32:15

CC++

2011-08-24 09:13:40

編程

2023-10-07 00:01:02

Java函數

2020-09-22 11:00:11

Java技術開發

2020-09-23 07:50:45

Java函數式編程

2020-09-23 16:07:52

JavaScript函數柯里化

2020-02-06 19:12:36

Java函數式編程編程語言

2021-09-03 09:06:42

代碼時間開發

2024-01-17 06:23:35

SwiftTypeScript定義函數

2011-09-22 09:38:27

CIO云計算

2011-09-24 12:26:41

2012-03-21 09:30:11

ibmdw

2011-10-19 15:47:13

2010-06-22 13:32:26

函數式編程JavaScript

2022-05-20 08:35:59

useEffect函數式組件React

2009-09-27 15:23:00

Scala講座函數式編程Scala

2011-03-21 12:41:41

JavaScript
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 日本福利视频免费观看 | 国产精品久久久久久久久久东京 | 久久欧美精品 | 91免费在线 | 久久国产精品亚洲 | 亚洲欧洲综合av | 精品91 | 人人澡人人爱 | 中文字幕 在线观看 | 久久国产精品免费一区二区三区 | 一区二区中文 | 久久精品欧美一区二区三区不卡 | 久久出精品 | 天天操人人干 | 久久国产精品网站 | 精久久久 | 午夜电影在线播放 | 日韩视频―中文字幕 | 久久成人18免费网站 | 一区二区三区四区视频 | 成人a网| 色资源在线 | 91精品国产综合久久久久久漫画 | 成人一区二区三区 | 国内精品久久久久久久影视简单 | 久久av一区二区三区 | 美国一级黄色片 | 午夜电影福利 | 欧美日韩一卡二卡 | 亚洲视频一区在线观看 | 四虎成人av | 91精品国产综合久久久久蜜臀 | 亚洲成人av | 91成人在线 | 在线观看国产wwwa级羞羞视频 | 久久精品国产一区二区三区不卡 | 国产精品99久久久久久动医院 | 亚洲成人免费 | 美女视频一区 | 女同久久 | 免费黄色片在线观看 |