国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

設(shè)計模式學(xué)習(xí)筆記 - 開源實戰(zhàn)三(下):借助Google Guava學(xué)習(xí)三大編程范式中的函數(shù)式編程

這篇具有很好參考價值的文章主要介紹了設(shè)計模式學(xué)習(xí)筆記 - 開源實戰(zhàn)三(下):借助Google Guava學(xué)習(xí)三大編程范式中的函數(shù)式編程。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

概述

現(xiàn)在主流的編程范式主要有三種,面向過程、面向?qū)ο蠛秃瘮?shù)式編程。在理論部分,已經(jīng)介紹了前面兩種編程范式。本章再講講剩下的編程范式,函數(shù)式編程。

函數(shù)式編程并非是一個很新的東西,早在 50 年前就已經(jīng)出現(xiàn)。近幾年,函數(shù)式編程越來越被人關(guān)注,出現(xiàn)了很多新的函數(shù)式編程語言,比如 Clogure、Scala、Erlang 等。一些函數(shù)式編程語言也加入了很多特性、語法、類庫類支持函數(shù)式編程,比如 Java、Python、Ruby、JavaScript 等。此外,Google Guava 也有對函數(shù)式編程的增強功能。

函數(shù)式編程因其編程的特性,僅在科學(xué)計算、數(shù)據(jù)處理、統(tǒng)計分析等領(lǐng)域,才能更好地發(fā)揮它的優(yōu)勢,所以個人覺得,它并不能完全替代更加通用的面向?qū)ο缶幊谭妒健5?,作為一種補充,它也有很大存在、發(fā)展和學(xué)習(xí)的意義。


到底什么是函數(shù)式編程?

函數(shù)式編程的英文是 Functional Programming。那到底什么是函數(shù)式編程呢?

函數(shù)式編程也沒有一個嚴(yán)格的官方定義,所以,接下來就從特性上告訴你,什么是函數(shù)式編程。

嚴(yán)格來講,函數(shù)式編程中的 “函數(shù)”,并不是指我們編程語言中的 “函數(shù)” 概念,而是指數(shù)學(xué) “函數(shù)” 或者 “表達式”(比如,y=f(x)。不過,在編程實現(xiàn)時,對于數(shù)學(xué) “函數(shù)” 或者 “表達式”,一般習(xí)慣性地將它們設(shè)計成函數(shù)。所以,如果不深究的話,函數(shù)式編程中的 “函數(shù)” 也可以理解為編程語言中的 “函數(shù)”。

每個編程范式都有自己獨特的地方,這就是它們會被抽象出來作為一種范式的原因。面向?qū)ο缶幊痰淖畲筇攸c是:以類、對象作為組織代碼的單元以及它的四大特性。面向過程編程語言的最大特點是:以函數(shù)作為組織代碼的單元,數(shù)據(jù)與方法相分離。

函數(shù)式編程最獨特的地方在于它的編程思想。函數(shù)式編程任務(wù),程序可以用一系列數(shù)學(xué)函數(shù)或表達式的組合來表示。函數(shù)式編程是程序面向數(shù)學(xué)的更底層抽象,將計算過程描述為表達式。不過,這樣說你肯定會有疑問,真的可以把任何程序都表示成一組數(shù)學(xué)表達式嗎?

理論上是可以的。但是,并不是所有的程序都適合這么做。函數(shù)式編程有它自己適合的應(yīng)用場景,比如開篇提到的科學(xué)計算、數(shù)據(jù)處理、數(shù)據(jù)處理、統(tǒng)計分析等。在這些領(lǐng)域,程序往往容易使用數(shù)學(xué)表達式來表示,比起非函數(shù)式編程,實現(xiàn)同樣的功能,函數(shù)式編程可以 用很少的代碼就能搞定。但是,對于強業(yè)務(wù)相關(guān)的大型業(yè)務(wù)系統(tǒng)開發(fā)來說,耗費精力地將它抽象成數(shù)學(xué)表達式,硬要用函數(shù)式編程來實現(xiàn),顯然是自討苦吃。相反,在這種應(yīng)用場景下,面向?qū)ο缶幊谈雍线m,寫出來的代碼更加可讀、可維護。

剛剛講的是函數(shù)式編程的編程思想,如果落實到編程實現(xiàn),函數(shù)式編程跟面向過程編程一樣,也是以函數(shù)作為組織代碼的單元。不過,它跟面向過程編程的區(qū)別在于,它的函數(shù)是無狀態(tài)的。

何為無狀態(tài)?簡單地講,函數(shù)內(nèi)部涉及的變量都是局部變量,不會像面向?qū)ο缶幊棠菢?,共享類成員變量,也不會像面向過程編程那樣,共享全局變量。函數(shù)的執(zhí)行結(jié)果只與入?yún)⒂嘘P(guān),跟其他任何外部變量無關(guān)。同樣的入?yún)ⅲ还茉趺磮?zhí)行,得到的結(jié)果是一樣的。這實際上就是數(shù)學(xué)表達式或數(shù)學(xué)函數(shù)的基本要求。下面舉個簡單的例子來解釋下:

// 有狀態(tài)函數(shù):執(zhí)行結(jié)果依賴b的值是多少,即便入?yún)⑾嗤?,多次?zhí)行函數(shù),函數(shù)的返回值有可能不同,因為b值有可能不同
int b
int increase(int a) {
	return a + b;
}

// 無狀態(tài)函數(shù):執(zhí)行結(jié)果不依賴任何外部變量,只要入?yún)⑾嗤?,不管?zhí)行多少次,函數(shù)的返回值就相同
int increase(int a, int b) {
	return a + b;
}

不同的編程范式之間并不是截然不同的,總有一些相同的編程規(guī)則。比如,不管是面向過程、面向?qū)ο筮€是函數(shù)式編程,它們都有變量、函數(shù)的概念,最頂層都要有 main 函數(shù)執(zhí)行入口,來組裝編程單元(類、函數(shù)等)。只不過,面向?qū)ο蟮木幊虇卧穷惢驅(qū)ο?,面向過程的編程單元室函數(shù),函數(shù)式編程的編程單元是無狀態(tài)函數(shù)。

Java 對函數(shù)式編程的支持

前面章節(jié)講過,實現(xiàn)面向?qū)ο缶幊滩灰欢ǚ堑檬褂妹嫦驅(qū)ο缶幊陶Z言。同理,實現(xiàn)函數(shù)式編程也不一定非得使用函數(shù)式編程語言?,F(xiàn)在,很多面向?qū)ο缶幊陶Z言,也提供了相應(yīng)的語法、類庫來支持函數(shù)式編程。

接下來,看下 Java 這種面向?qū)ο缶幊陶Z言,對函數(shù)式編程的支持,加深一下你對函數(shù)式編程的理解。下面是一段非常典型地 Java 函數(shù)式編程的代碼。

public class FPDemo {
    public static void main(String[] args) {
        Optional<Integer> result = Stream.of("f", "a", "hello")
                .map(s -> s.length())
                .filter(l -> l <= 3)
                .max((o1, o2) -> o1 - o2);
        System.out.println(result.get()); // 輸出2
    }
}

這段代碼的作用是從一組字符串?dāng)?shù)組中,過濾出長度小于等于 3 的字符串,并且求得這其中的最大長度。

如果你不了解 Java 函數(shù)式編程的語法,看了上面的代碼或許會有些懵,主要的原因是 Java 為函數(shù)式編程引入了三個新的語法概念:StreamLambda 表達式函數(shù)接口(Function Interface)。

  • Stream 類用來支持通過 “.” 級聯(lián)多個函數(shù)操作的代碼編寫方式;
  • 引入 Lambda 表達式的作用是簡化代碼編寫;
  • 函數(shù)接口的作用是可以把函數(shù)包裹成函數(shù)接口,來實現(xiàn)把函數(shù)當(dāng)做參數(shù)一樣來使用(Java 不像 C 一樣支持函數(shù)指針,可以把函數(shù)當(dāng)做參數(shù)來使用)。

首先,看下 Stream 類

假設(shè)要計算這樣一個表達式:(3-1)*2 + 5。如果按照普通的函數(shù)調(diào)用方式寫出來,就是下面這個樣子:

add(multiply(subtract(3, 1), 2), 5);

這樣看起來,代碼會比較難理解,換個更易懂的寫法:

subtract(3, 1).multiply(2).add(5);

在 Java 中, “.” 用來表示某個對象的方法。為了支持上面這種級聯(lián)調(diào)用方式,我們讓每個函數(shù)都返回一個通用的類 型:Stream 類對象。在 Stream 類上的操作有兩種:中間操作和終止操作。中間操作仍返回 Stream 類對象,而終止操作返回的是確定的值結(jié)果。

再看下前面的例子。我們對代碼做了注釋解釋,如下所示。其中,map、filter 是中間操作,返回 Stream 類對象,可以繼續(xù)級聯(lián)其他操作;max 是終止操作,返回的不是 Stream 類對象,無法繼續(xù)往下級聯(lián)處理了。

public class FPDemo {
    public static void main(String[] args) {
        Optional<Integer> result = Stream.of("f", "a", "hello") // of返回Stream<String>對象
                .map(s -> s.length()) // map返回Stream<Integer>對象
                .filter(l -> l <= 3) // filter返回Stream<Integer>對象
                .max((o1, o2) -> o1 - o2); // max 終止操作:返回Option<Integer>
        System.out.println(result.get()); // 輸出2
    }
}

其次,再看下 Lambda 表達式

Java 引入 Lambda 表達式的主要作用是簡化代碼編寫。實際上,我們也可以不用 Lambda 表達式來書寫書中的例子。我們拿其中的 map 函數(shù)來舉例說明下。

// Stream中map的定義:
public interface Stream<T> extends BaseStream<T, Stream<T>> {
    <R> Stream<R> map(Function<? super T, ? extends R> mapper);
    // 省略其他函數(shù)...
}

// Stream中map的使用方法:
Stream.of("fo", "far", "hello").map(new Function<String, Integer>() {
   @Override
    public Integer apply(String s) {
        return s.length();
    }
});

// 用Lambda表達式簡化后的寫法:
Stream.of("fo", "far", "hello").map(s -> s.length());

Lambda 表達式語法不是學(xué)習(xí)的重點,這里只是稍微介紹下。

Lambda 表達式包含三部分:輸入、函數(shù)體、輸出。表示出來就是下面這個 樣子:

(a, b) -> { 語句1; 語句2; ...; return 輸出;} // a,b是輸入?yún)?shù)

實際上,Lambda 表達式的寫法非常靈活。剛剛給出的是標(biāo)準(zhǔn)寫法,還有很多簡化寫法。比如,如果輸入?yún)?shù)只有一個,可以省略 (),直接寫成 a ->{...};如果沒有入?yún)ⅲ梢灾苯訉⑤斎牒图^都省略只保留函數(shù)體;如果函數(shù)體只有一個語句,可以將 {} 省略掉; 如果函數(shù)沒有返回值, return 語句就可以不用寫了。

如果把之前的例子中的 Lambda 表達式,全部替換為函數(shù)接口的實現(xiàn)方式,就是下面這樣子的。代碼是不是多了很多?

Optional<Integer> result = Stream.of("f", "a", "hello")
        .map(s -> s.length())
        .filter(l -> l <= 3)
        .max((o1, o2) -> o1 - o2);

// 還原為函數(shù)接口的實現(xiàn)方式
Optional<Integer> result2 = Stream.of("fo", "far", "hello")
	.map(new Function<String, Integer>() {
	    @Override
	    public Integer apply(String s) {
	        return s.length();
	    }
	})
	.filter(new Predicate<Integer>() {
	    @Override
	    public boolean test(Integer integer) {
	        return integer <= 3;
	    }
	})
	.max(new Comparator<Integer>() {
	    @Override
	    public int compare(Integer o1, Integer o2) {
	        return o1 - o2;
	    }
	});

最后,看下函數(shù)接口

實際上,上面一段代碼中的 Function、PredicateComparator 都是函數(shù)接口。我們知道,C 語言支持函數(shù)指針,它可以把函數(shù)直接當(dāng)變量來使用。但是,Java 沒有函數(shù)指針這樣的語法。所以,它通過接口函數(shù),將函數(shù)包裹在接口中,當(dāng)做變量來使用。

實際上,函數(shù)接口就是接口。不過,它有自己特別的地方,那就是要求只包含一個未實現(xiàn)的方法。因為,只有這樣,Lambda 表達式才能明確知道匹配的是哪個接口。如果有兩個為實現(xiàn)的方式,并且接口入?yún)ⅰ⒎祷刂刀家粯?,?Java 在翻譯 Lambda 表達式時,就不知道表達式對應(yīng)哪個方法了。

我們把 Java 提供的 Function、Predicate 這兩個函數(shù)接口的源碼,摘抄過來貼到了下面,你可以看下。

@FunctionalInterface
public interface Function<T, R> {
    R apply(T t);

    default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
        Objects.requireNonNull(before);
        return (V v) -> apply(before.apply(v));
    }

    default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t) -> after.apply(apply(t));
    }

    static <T> Function<T, T> identity() {
        return t -> t;
    }
}

@FunctionalInterface
public interface Predicate<T> {
    boolean test(T t);

    default Predicate<T> and(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) && other.test(t);
    }

    default Predicate<T> negate() {
        return (t) -> !test(t);
    }
    
    default Predicate<T> or(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) || other.test(t);
    }

    static <T> Predicate<T> isEqual(Object targetRef) {
        return (null == targetRef)
                ? Objects::isNull
                : object -> targetRef.equals(object);
    }
}

以上講的就是 Java 對函數(shù)式編程的語法支持。

Guava 對函數(shù)式編程的增強

如果你是 Google Guava 的設(shè)計者,對于 Java 函數(shù)式編程,Google Guava 還能做些什么呢?

Google Guava 并沒有提供太多函數(shù)式編程的支持,僅封裝了幾個遍歷集合操作的接口,代碼如下所示:

Iterables.transform(Iterable, Function);
Iterables.transform(Iterator, Function);
Collections.transform(Collection, Function);
List.transform(List, Function);
Maps.transform(Map, Function);
Multimaps.transform(Multimap, Function);
...

Iterables.filter(Iterable, Predicate);
Iterators.filter(Iterable, Predicate);
Collections2.filter(Collection, Predicate);
...

從 Google 的 Wiki 中,我們發(fā)現(xiàn),Google 對于函數(shù)式編程的使用還是很謹(jǐn)慎的,認(rèn)為過度使用函數(shù)式編程,會導(dǎo)致代碼的可讀性變差,強調(diào)不濫用。所以,在函數(shù)式編程方面,Google Guava 并沒有提供太多的支持。

之所以對遍歷集合操作做了優(yōu)化,主要是因為函數(shù)式編程一個重要的應(yīng)用場景就是遍歷集合。如果不適用函數(shù)式編程,我們只能 for 循環(huán),一個一個的處理集合中的屬性。使用函數(shù)式編程,可以大大簡化遍歷集合操作的代碼編寫,一行代碼就能搞定,而且在可讀性方面也沒有太大的損失。

總結(jié)

本章,講了一下三大編程范式中的最后一個,函數(shù)式編程。盡管越來越多的編程語言開始支持函數(shù)式編程,但我個人覺得,它只能是其他編程語范式的補充,用在一些特殊的領(lǐng)域發(fā)揮它的特殊作用,沒法完全替代面向?qū)ο?、面向過程編程范式。

關(guān)于什么是函數(shù)式編程,實際上不是很好理解。函數(shù)式編程中的 “函數(shù)”,并不是只我們編程語言中的 “函數(shù)” 概念,而是數(shù)學(xué)中的 “函數(shù)” 或者 “表達式” 概念。函數(shù)式編程認(rèn)為,程序可以用一系列數(shù)學(xué)函數(shù)或者表達式的組合來表示。

具體到編程實現(xiàn),函數(shù)式編程以無狀態(tài)函數(shù)作為組織代碼的單元。函數(shù)的執(zhí)行結(jié)果只與入?yún)⒂嘘P(guān),跟其他任何外部變量無關(guān)。同樣的入?yún)?,不管怎么?zhí)行,得到的結(jié)果都是一樣的。

具體到 Java 語言,它提供了三個語法機制來支持函數(shù)式編程。它們分別是 Stream 類、Lambda 表達式和函數(shù)接口。Google Guava 對函數(shù)式編程的一個重要應(yīng)用場景,遍歷集合,做了優(yōu)化,但并沒有太多的支持,并且強調(diào),不要為了節(jié)省代碼行數(shù),濫用函數(shù)式編程,導(dǎo)致代碼可讀性變差。文章來源地址http://www.zghlxwxcb.cn/news/detail-855511.html

到了這里,關(guān)于設(shè)計模式學(xué)習(xí)筆記 - 開源實戰(zhàn)三(下):借助Google Guava學(xué)習(xí)三大編程范式中的函數(shù)式編程的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費用

相關(guān)文章

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包