c++匿名函數,java 匿名函數_國外程序員用的火熱的Vavr是什么鬼?讓函數式編程更簡單!

 2023-10-15 阅读 31 评论 0

摘要:引言相信很多人關注 Vavr 的原因,還是因為 Hystrix 庫。Hystrix 不更新了,并在 GitHub 主頁上推薦了 Resilience4j,而 Vavr 作為 Resilience4j 的唯一依賴被提及。對于 Resilience4j 這個以輕依賴作為特色之一的容錯庫,為什么還會引用 Vavr 呢&#

引言

相信很多人關注 Vavr 的原因,還是因為 Hystrix 庫。Hystrix 不更新了,并在 GitHub 主頁上推薦了 Resilience4j,而 Vavr 作為 Resilience4j 的唯一依賴被提及。對于 Resilience4j 這個以輕依賴作為特色之一的容錯庫,為什么還會引用 Vavr 呢?

以下是 Resilience4j 官方原文:

?

Resilience4j is a lightweight fault tolerance library inspired by Netflix Hystrix, but designed for Java 8 and functional programming. Lightweight, because the library only uses Vavr, which does not have any other external library dependencies.

?

Resilience4j 除了輕量,另一特點是對 Java 8 函數式編程的支持,經過一番了解,Vavr 正是為了提升 Java 函數式編程體驗而開發的,通過它可以幫助我們編寫出更簡潔、高效的函數風格代碼。

c++匿名函數。限于篇幅,該系列分為上、下兩篇:上篇著重回顧函數式編程的一些基礎知識,以及 Vavr 總體介紹、Vavr 對元組、函數的支持,通過上篇的學習;下篇著重講述 Vavr 中對各種值類型、增強集合、參數檢查、模式匹配等特性。力求通過這兩篇文章,把 Vavr 的總體特性呈現給大家,讓大家對 Vavr 有個全面的認識。

簡介

Vavr是 Java 8+ 函數式編程的增強庫。提供了不可變數據類型和函數式控制結構,旨在讓 Java 函數編程更便捷高效。特別是功能豐富的集合庫,可以與Java的標準集合平滑集成。

Vavr 的讀音是 /?we?.v?/,早期版本叫 Javaslang,由于和 Java? 商標沖突(類似國內的 JavaEye 改名),所以把 Java 倒過來取名。de7887451f078185f380b3d054698002.png

函數式編程

學習 Vavr 之前,我們先回顧下 Java 函數式編程及 Lambda (λ) ?表達式的一些相關內容。

Java 8 開始,在原有面向對象、命令式編程范式的基礎上,增加了函數式編程支持,其核心是行為參數化,把行為具體理解為一個程序函數(方法),即是將函數作為其它函數的參數傳遞,組成高階函數。

javascript匿名函數、舉個例子,人(People)這個類存在一個年齡(age)屬性,我們想根據每個人的年齡進行排序比較。

首先看下 Java 8 之前的做法:

Comparator comparator = new Comparator() {  @Override  public int compare(People p1, People p2) {    return Integer.compare(p1.getAge(), p2.getAge());  }};

再看 Java 8 之后的做法:

Comparator comparator    = (p1, p2) -> Integer.compare(p1.getAge(), p2.getAge());

利用 Lambda 表達式的語法,確實少了很多模板代碼,看起來更加簡潔了。關于 Java 的函數式編程及 Lambda 表達式語法,有以下需要掌握的知識點:

函數式接口

函數式接口 (Functional Interface) 就是一個有且僅有一個抽象方法,但是可以有多個非抽象方法的接口,通常會用 @FunctionalInterface 進行標注,但不是必須的。Java 8 自帶了常用的函數式接口,存放在 java.util.function 包下,包括 FunctionSupplierConsumerPredicate 等,此外在其它地方也用到很多函數式接口,比如前面演示的 Comparator

Lambda 表達式

Java匿名對象,Lambda 表達式是一種匿名函數,在 Java 中,定義一個匿名函數的實質依然是函數式接口的匿名實現類,它沒有名稱,只有參數列表、函數主體、返回類型,可能還有一個異常列表聲明。Lambda 表達式有以下重要特征:

  • 可選類型聲明:不需要聲明參數類型,編譯器可以進行類型識別;
  • 可選的參數圓括號:一個參數無需定義圓括號,但多個參數需要定義圓括號;
  • 可選的花括號:如果主體包含了一個語句,就不需要使用花括號;
  • 可選的 return 關鍵字:如果主體只有一個表達式返回值,則編譯器會自動返回值,加了花括號需要指定表達式返回一個數值。
// 1. 不需要參數,返回值為 1() -> 1// 2. 接收一個參數(數字類型),返回值為 x + 1x -> x + 1// 3. 接受2個參數(數字),返回值為 x + y (x, y) -> x + y// 4. 接收2個int型整數,返回值為 x + y (int x, int y) -> x + y// 5. 接受一個 String 對象,并在控制臺打印,不返回任何值(返回 void)  (String s) -> System.out.print(s)

副作用(Side-Effects)

?

如果一個操作、函數或表達式在其本地環境之外修改了某個狀態變量值,則會產生副作用,也就是說,除了向操作的調用者返回一個值(主要效果)之外,還會產生可觀察到的效果。

?

例如,一個函數產生異常,并且這個異常向上傳遞,就是一種影響程序的副作用,此外,異常就像一種非本地的 goto 語句,打斷了正常的程序流程。具體看以下代碼:

int divide(int dividend, int divisor) {    // 如果除數為0,會產生異常    return dividend / divisor;}

怎么處理這種副作用呢?在 Vavr 中,可以把它封裝到一個 Try 實例,具體實現:

// = Success(result) or Failure(exception)Try safeDivide(Integer dividend, Integer divisor) {    return Try.of(() -> divide(dividend, divisor));}

這個版本的除法函數不再拋出異常,我們通過 Try 類型明確了可能的錯誤。

引用透明(Referential Transparency)

?

Vm是狀態函數嗎?引用透明的概念與函數的副作用相關,且受其影響。如果程序中任意兩處具有相同輸入值的函數調用能夠互相置換,而不影響程序的動作,那么該程序就具有引用透明性。

?

從以下示例可以看出,第一種實現,隨機數是根據可變的外部狀態生成的,所以每次調用產生的結果都不同,無法做到引用透明;第二種實現,我們給隨機對象指定一個隨機種子,這樣保證不受外部狀態的影響,達到引用透明的效果:

// 非引用透明Math.random();// 引用透明new Random(1).nextDouble();

不可變對象

不可變對象是指其狀態在創建后不能修改的對象。它有以下好處:

  • 本質上是線程安全的,因此不需要同步;
  • 對于equals和hashCode來說是穩定的,因此是可靠的哈希鍵;
  • 不需要克隆;
  • 在未檢查的協變強制轉換(特定于java)中使用時表現為類型安全。

使用 Vavr

受限于 Java 標準庫的通用性要求及體量大小考慮,JDK API 對函數式編程的支持比較有限,這時候可以引入 Vavr 來提供更便捷的安全集合類型、支持更多的 stream 流操作、豐富函數式接口類型……

在 Vavr 中,所有類型都是基于 Tuple, Value, λ 構建的:

d4b893a8f0b81ae36b50d02e1529a7b2.png
圖片來自 Vavr 官網

引入依賴

函數vlookup?這里使用 Maven 項目構建,完整的 pom.xml 配置如下:

  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">  4.0.0  com.example  demo-vavr  0.0.1      UTF-8    1.8    1.8    0.9.3              io.vavr      vavr      ${vavr.version}      

Java 必須是 1.8 以上版本,這是使用 Java 函數式編程的前提,另外 Vavr 使用的是 0.9.3 版本。Vavr 本身沒有其它外部依賴,Jar 包大小僅有 800+K,相當輕量。

元組(Tuple)

Java 自身并沒有元組的概念,元組是將固定數量的元素組合在一起,這樣它們就可以作為一個整體傳遞,但它與數組或集合的區別是,元組能包含不同類型的對象,且是不可變的。

Vavr 提供了 Tuple1Tuple2Tuple8 等8個具體的元組類型,分別代表可存儲1~8個元素的元組,并可以通過 _1_2..._8 等屬性訪問對應元素。

以下是創建并獲取元組的示例:

// 通過 Tuple.of() 靜態方法創建一個二元組Tuple2 people = Tuple.of("Bob", 18);// 獲取第一個元素,名稱:BobString name = people._1;// 獲取第二個元素,年齡:18Integer age = people._2;

V函數構建?元組也提供了對元素映射處理的能力,以下兩種寫法效果是相同的:

// ("Hello, Bob", 9)people.map(  name -> "Hello, " + name,  age-> age / 2);// ("Hello, Bob", 9)	people.map(  (name, age) -> Tuple.of("Hello, " + name, age / 2));

此外,元組還提供了基于元素內容轉換創建新的類型:

// 返回 name: Bob, age: 18String str = people.apply(  (name, age) -> "name: " + name  + ", age: " + age);

函數(Function)

Java 8 僅提供了接受一個參數的函數式接口 Function 和接受兩個參數的函數式接口 BiFunction,vavr 則提供了最多可以接受8個參數的函數式接口:Function0Function1Function2...Function8。如果需要拋出受檢異常的函數,可以使用 CheckedFunction{0...8} 版本。

以下是使用函數的示例:

// 聲明一個接收兩個參數的函數Function2 description = (name, age) -> "name: " + name  + ", age: " + age;// 返回 "name: Bob, age: 18"String str = description.apply("Bob", 18);

Vavr 函數是 Java 8 函數的增強,它提供了以下特性:

組合(Composition)

V條件函數,組合是將一個函數 f(x) 的結果作為另一個函數 g(y) 的參數,產生新函數 h: g(f(x)) 的操作,可以使用 andThencompose 方法實現函數組合:

Function1 plusOne = a -> a + 1;Function1 multiplyByTwo = a -> a * 2;// 以下兩種寫法結果一致,都是 z -> (z + 1) * 2Function1 addOneAndMultiplyByTwo1 = plusOne.andThen(multiplyByTwo);Function1 addOneAndMultiplyByTwo2 = plusOne.andThen(multiplyByTwo);

提升(Lifting)

提升是針對部分函數(partial function)的操作,如果一個函數 f(x) 的定義域是 x,另一個函數 g(y) 跟 f(x) 定義相同,只是定義域 y 是 x 的子集,就說 f(x) 是全函數(total function),g(y) 是部分函數。函數的提升會返回當前函數的全函數,返回類型為 Option,以下是一個部分函數定義:

// 當除數 0 時,將導致程序異常Function2 divide = (a, b) -> a / b;

我們再利用liftdivide提升為可以接收所有輸入的全函數:

Function2> safeDivide = Function2.lift(divide);// = NoneOption i1 = safeDivide.apply(1, 0);// = Some(2)Option i2 = safeDivide.apply(4, 2);

通過以上示例可以看出,如果使用不允許的輸入值調用提升后的全函數,則返回None而不是引發異常。

部分應用(Partial application)

部分應用是通過固定函數的前n個參數值,產生一個新函數,該新函數參數為原函數總參數個數減n,具體示例如下:

Function2 sum = (a, b) -> a + b;Function1 add2 = sum.apply(2);

V區間函數。sum函數通過部分應用,第一個參數被固定為2,并產生新的add2函數。

柯里化(Currying)

柯里化是把接受多個參數的函數變換成接受一個單一參數(最初函數的第一個參數)的函數,并且返回接受余下的參數而且返回結果的新函數的技術。

c34bca8d7e0b0f86c6a5024a85d3f6fe.png
Function8 的 JavaDoc說明

Function2被柯里化時,結果與部分應用沒有區別,因為兩者都會產生單參數的函數。但函數參數多于2個,就能明顯看出柯里化的不同:

Function3 sum = (a, b, c) -> a + b + c;final Function1> add2 = sum.curried().apply(2);// = 9Integer i = add2.apply(4).apply(3);

記憶(Memoization)

函數記憶是利用緩存技術,第一次執行后,將結果緩存起來,后續從緩存返回結果。以下函數在第一次調用時,生成隨機數并進行緩存,第二次調用直接從緩存返回結果,所以多次返回的隨機數是同一個:

Function0 hashCache = Function0.of(Math::random).memoized();double randomValue1 = hashCache.apply();double randomValue2 = hashCache.apply();

總結

今天對 Vavr 的介紹先到這里,下篇我們將會接著介紹另外一些特性:

  • 值類型(Values)
  • 集合(Collections)
  • 參數檢查(Property Checking)
  • 模式匹配(Pattern Matching)
41600b8c885b54b0c54f3c12e8136b1d.gifEND41600b8c885b54b0c54f3c12e8136b1d.gif

java工作、歷史推薦

1、三分鐘快速記住 選擇排序算法!

2、1.5W 字搞懂 Spring Cloud,太牛了!

3、一文帶你全方位弄懂 Java8 流式操作!

4、三分鐘快速記住冒泡排序算法!

fab12fa2eaadb36e0fb30eeec1b242af.png

java程序?公眾號ID|javabaiwen

小編微信|619531440

每天分享技術干貨

視頻 | 電子書 | 面試題?|?開發經驗

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://hbdhgg.com/1/137964.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 匯編語言學習筆記 Inc. 保留所有权利。

底部版权信息