Java中使用Map and Fold進行功能性編程

在函數式編程中,Map和Fold是兩個非常有用的運算符,它們屬于每種函數式語言。 如果Map和Fold運算符是如此強大且必不可少,那么您如何解釋說即使Java編程語言缺少這兩個運算符,我們也可以使用Java來完成工作? 事實是,使用Java進行編碼時,您已經進行了Map和Fold,只是每次都使用循環手動進行操作。

免責聲明:我不是函數式編程的參考,本文不過是一個簡短的介紹; FP愛好者可能不太喜歡它。

您已經熟悉了

想象一下不含增值稅的數量的List <Double>。 我們希望將此列表轉換為包含增值稅的另一個相應列表。 首先,我們定義一種將增值稅加到一個單一金額中的方法:

public double addVAT(double amount, double rate) {return amount * (1 + rate);}

現在讓我們將此方法應用于列表中的每個金額:

public List<Double> addVAT(List<Double> amounts, double rate){final List<Double> amountsWithVAT = new ArrayList<Double>();for(double amount : amounts){amountsWithVAT.add(addVAT(amount, rate));}return amountsWithVAT;
}

在這里,我們創建了另一個輸出列表,對于輸入列表的每個元素,我們對其應用了addVAT()方法,然后將結果存儲到大小完全相同的輸出列表中。 恭喜,正如我們剛剛手工完成的,在方法addVAT()的輸入列表上有一個Map。 讓我們再做一次。

現在我們要使用貨幣匯率將每個金額轉換為另一種貨幣,因此我們需要一種新的方法:

公共double convertCurrency(double

public double convertCurrency(double amount, double currencyRate){return amount / currencyRate;}

現在,我們可以將此方法應用于列表中的每個元素:

public List<Double> convertCurrency(List<Double> amounts, double currencyRate){final List<Double> amountsInCurrency = new ArrayList<Double>();for(double amount : amounts){amountsInCurrency.add(convertCurrency(amount, currencyRate));}return amountsInCurrency;
}

注意,除了在步驟2處調用的方法外,兩種接受列表的方法是如何相似的:

  1. 創建一個輸出列表,
  2. 為輸入列表中的每個元素調用給定方法 ,并將結果存儲到輸出列表中
  3. 返回輸出列表。

您經常在Java中執行此操作,而這恰恰是Map運算符:將給定方法someMethod (T):T應用于list <T>的每個元素,這將為您提供另一個相同大小的list <T>。

功能語言認識到這種特殊需求(在集合的每個元素上應用一種方法)非常普遍,因此他們將其直接封裝到內置的Map運算符中。 這樣,給定addVAT(double,double)方法,我們可以使用Map運算符直接編寫如下代碼:

List amountsWithVAT = map (addVAT, amounts, rate)

是的,第一個參數是一個函數,因為函數是函數語言中的一等公民,因此可以將它們作為參數傳遞。 與for循環相比,使用Map運算符更簡潔,更不易出錯,其意圖也更加明確,但是我們在Java中沒有它……

因此,這些示例的要點是,您已經不熟悉甚至不知道函數式編程的關鍵概念:Map運算符。

現在是Fold運算符

回到金額列表,現在我們需要將總金額計算為每個金額的總和。 超級容易,讓我們循環執行一下:

public double totalAmount(List<Double> amounts){double sum = 0;for(double amount : amounts){sum += amount;}return sum;
}

基本上,我們只是在列表上進行了折疊,使用函數“ + =”將每個元素折疊成一個元素,這里是一個遞增的數字,一次折疊一個。 這類似于Map運算符,除了結果不是列表而是單個元素(標量)。

這又是您通常用Java編寫的那種代碼,現在您已經用功能語言為其命名:“ Fold ”或“ Reduce”。 Fold運算符通常在函數式語言中是遞歸的,因此我們在此不再對其進行描述。 但是,我們可以使用某種可變狀態在迭代之間累加結果,從而以迭代形式實現相同的目的。 在這種方法中,Fold采用一種內部可變狀態的方法,該方法期望一個元素,例如someMethod(T),并將其反復應用于輸入列表<T>中的每個元素,直到我們得到一個單個元素T,即折疊操作的結果。

與Fold一起使用的典型函數是求和,邏輯與和或,List.add()或List.addAll(),StringBuilder.append(),max或min等。Fold的思維方式類似于SQL中的聚合函數 。

形狀思考

直觀地思考(帶有草率的圖片),Map接收一個大小為n的列表,并返回另一個大小相同的列表:

另一方面,Fold獲取大小為n的列表,并返回單個元素(標量):

您可能還記得我以前關于謂詞的文章 ,這些文章通常用于將集合過濾為元素較少的集合。 實際上,此過濾器運算符是在大多數功能語言中補充Map和Fold的第三種標準運算符。

Eclipse模板

由于Map和Fold很常見,因此有必要為它們創建Eclipse模板,例如Map:

在Java中更接近地圖和折疊

Map和Fold是期望函數作為參數的構造,而在Java中,傳遞方法的唯一方法是將其包裝到接口中。

在Apache Commons Collections中,有兩個接口對于我們的需求特別有趣: Transformer (具有一個方法transform(T):T )和Closure (具有一個方法execute(T):void) 。 類CollectionUtils提供了collect(Iterator,Transformer)方法(它基本上是Java集合的窮人Map運算符)以及forAllDo()方法,該方法可以使用閉包來模擬Fold運算符。

使用Google Guava, Iterables類提供了靜態方法transform(Iterable,Function) ,該方法基本上是Map運算符。

List<Double> exVat = Arrays.asList(new Double[] { 99., 127., 35. });Iterable<Double> incVat = Iterables.transform(exVat, new Function<Double, Double>() {public Double apply(Double exVat) {return exVat * (1.196);}});System.out.println(incVat); //print [118.404, 151.892, 41.86]

類似的變換()方法,也可在類解釋為解釋和地圖為地圖。

要在Java中模擬Fold運算符,可以使用Closure接口,例如Apache Commons Collection中的Closure接口,僅使用一個帶有一個參數的單一方法,因此您必須在內部保留當前的-mutable-狀態,就像'+ =確實。

不幸的是,盡管Guava中沒有Fold,盡管它經常被要求提供 ,甚至沒有類似閉包的函數,但是創建自己的函數并不難,例如,您可以使用以下方法實現上述總計:

// the closure interface with same input/output type
public interface Closure<T> {T execute(T value);
}// an example of a concrete closure
public class SummingClosure implements Closure<Double> {private double sum = 0;public Double execute(Double amount) {sum += amount; // apply '+=' operatorreturn sum; // return current accumulated value}
}// the poor man Fold operator
public final static <T> T foreach(Iterable<T> list, Closure<T> closure) {T result = null;for (T t : list) {result = closure.execute(t);}return result;
}@Test // example of use
public void testFold() throws Exception {SummingClosure closure = new SummingClosure();List<Double> exVat = Arrays.asList(new Double[] { 99., 127., 35. });Double result = foreach(exVat, closure);System.out.println(result); // print 261.0
}

不僅用于收藏:可折疊在樹木和其他建筑物上

Map and Fold的功能不僅限于簡單的集合,還可以擴展到任何可導航的結構,尤其是樹和圖。

想象一棵使用節點類及其子節點的樹。 將深度優先搜索和廣度優先搜索(DFS和BFS)編碼為兩個接受Closure作為單個參數的通用方法,可能是一個好主意:

public class Node ...{...public void dfs(Closure closure){...}public void bfs(Closure closure){...}
}

我過去經常使用這種技術,我可以說它可以大大減少類的大小,僅使用一種通用方法,而不是許多看起來相似的方法(每個方法都會重做自己的樹遍歷)。 更重要的是,可以使用模擬閉包對遍歷本身進行單元測試。 每個封蓋也可以獨立進行單元測試,所有這些都使您的生活變得更加簡單。

訪客模式可以實現非常相似的想法,您可能已經很熟悉。 我在我的代碼和其他幾個團隊的代碼中多次看到,Visitor非常適合在遍歷數據結構期間累積狀態。 在這種情況下,Visitor只是閉合中傳遞給折疊的一種特殊情況。

Map-Reduce上的一個字

您可能聽說過Map-Reduce模式,是的,其中的“ Map”和“ Reduce”一詞指的是我們剛剛看到的相同的函數運算符Map和Fold(也稱為Reduce)。 即使實際應用更加復雜,也很容易注意到Map 令人尷尬地是并行的,這對并行計算有很大幫助。

參考: 與我們的JCG合作伙伴一起 在您的日常Java中使用Map和Fold進行函數式編程的思考 ? Cyrille Martraire 博客上的Cyrille Martraire 。


翻譯自: https://www.javacodegeeks.com/2012/03/functional-programming-with-map-and.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/373566.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/373566.shtml
英文地址,請注明出處:http://en.pswp.cn/news/373566.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Linux 文件壓縮解壓縮

文章來自&#xff1a;http://www.xuexiyuan.cn/article/detail/53.html *.tar格式 解包1&#xff1a;$ tar -xvf FileName.tar解包2&#xff1a;$ tar -xvf FileName.tar -C DirName# tar解壓縮到指定目錄打包&#xff1a;$ tar -cvf FileName.tar DirName# tar是打包&#x…

Mysql 分頁語句Limit用法

Mysql 分頁語句Limit用法 1、Mysql的limit用法 在我們使用查詢語句的時候&#xff0c;經常要返回前幾條或者中間某幾行數據&#xff0c;這個時候怎么辦呢&#xff1f;不用擔心&#xff0c;mysql已經為我們提供了這樣一個功能。 Sql代碼 SELECT * FROM table LIMIT [offset,] r…

sqlmap指定cookie_利用SQLMap進行cookie注入

SQLMap被稱為注入神器&#xff0c;N多大神都使用SQLmap來進行注入測試&#xff0c;我等小菜當然也會用來裝一下A*C&#xff0c;用了N久SQLMAP了&#xff0c;但是極少用 到cookie注入&#xff0c;一遇到cookie注入就去使用注入中轉工具&#xff0c;比較麻煩。剛好今天群里的USB問…

c語言else匹配問題

1 #include <stdio.h>2 #include <stdlib.h>3 4 //實現 依次輸入三個遞增的數 然后正確輸出5 6 //為什么得不到我們想要的結果呢 這就是else匹配的問題 當然了 在編譯器里面他會自動給你匹配7 //但是如果沒有了編譯器 筆試的時候呢。。。。8 //原因為&#xff1a;e…

Java:偽造工廠的閉包以創建域對象

最近&#xff0c;我們想要創建一個域對象&#xff0c;該對象需要具有外部依賴關系才能進行計算&#xff0c;并且希望能夠在測試中解決該依賴關系。 最初&#xff0c;我們只是在領域類中新建依賴項&#xff0c;但這使得無法在測試中控制其值。 同樣&#xff0c;我們似乎不應該將…

利用scp 遠程上傳下載文件/文件夾

利用scp傳輸文件 1、從服務器下載文件 scp usernameservername:/path/filename /tmp/local_destination 例如scp codinglog192.168.0.101:/home/kimi/test.txt 把192.168.0.101上的/home/kimi/test.txt 的文件下載到 /tmp/local_destination 2、上傳本地文件到服務器 scp /…

KEIL編譯錯誤總結:

1 expected an identifier&#xff1a;#define宏定義常量后&#xff0c;如果再用前面定義的常量作為枚舉常量就會報錯&#xff0c;方法&#xff0c;去掉#define宏定義 2 ERROR L118: REFERENCE MADE TO ERRONEOUS EXTERNAL 定義的變量和外部聲明調用的變量存儲類型不一致&#…

視覺平衡與物理平衡_設計中的平衡理論為什么這么重要?

原標題&#xff1a;設計中的平衡理論為什么這么重要&#xff1f;在平面設計中很重要的理論就是關于平衡的應用。無論在logo設計還是網頁設計還是海報設計中&#xff0c;一個好的設計一定會兼顧視覺的平衡。今天123標志網就跟大家一起看看平衡的力量。構圖平衡主要意味著調整設計…

Tomcat、JDK 歷史版本下載地址

Tomcat 歷史版本下載地址http://archive.apache.org/dist/tomcat/ JDK 歷史版本下載地址 https://www.oracle.com/technetwork/java/javase/archive-139210.html 個人博客&#xff1a;學習園 原文地址&#xff1a;http://www.xuexiyuan.cn/article/detail/190.html

JavaFX移動應用程序最佳實踐,第2部分

警告&#xff1a;我在這里給出的技巧對于JavaFX Mobile的當前版本是正確的&#xff0c;該版本是JavaFX 1.1 SDK的一部分。 在將來的版本中&#xff0c;行為將改變&#xff0c;上述工件的當前不良性能將被優化或至少得到顯著改善。 我在這里寫的所有內容都是快照&#xff0c;不應…

14軟件工程第一次作業

你認為一些軍事方面的軟件系統采用什么樣的開發模型比較合適&#xff1f; 我認為設計軍事方面的軟件采用螺旋式的開發模型比較好。因為螺旋模型減少了過多測試或者是測試不足所帶來的風險&#xff0c;能夠使軟件在無法排除重大風險時有機會停止&#xff0c;減少損失。對于軍事方…

波紋擴散_C4D_動畫amp;RS波紋擴散效果J_014

C4D-效果擴散效果&#xff0c;Redshift混合冰材質&#xff1b;利用頂點貼圖擴散效果制作&#xff0c;RS混合調用頂點貼圖。視頻教程時長22分鐘。對象為可編輯對象才能用頂點貼圖。冰結域的擴展是這個動畫的重點&#xff0c;在這個模式下&#xff0c;權重會根據半徑向外擴展&…

軟件測試工程師簡歷項目經驗怎么寫?--9999個已成功入職的軟件測試工程師真實簡歷

簡歷是我們求職的第一步&#xff0c;也是非常重要的一步。 青云叔叔看過太多簡歷&#xff0c;最快3秒就淘汰一份簡歷&#xff0c;因為其實我們每天要收到很多簡歷進行篩選&#xff0c;那么面試官其實也是會很快進行對簡歷進行判斷的&#xff0c;如果你對簡歷寫的一塌糊涂&…

【poj2464】樹狀數組

這道題。。太特么多細節了。。 題意&#xff1a;在平面直角坐標系中給你N個點&#xff0c;stan和ollie玩一個游戲&#xff0c;首先stan在豎直方向上畫一條直線&#xff0c;該直線必須要過其中的某個點&#xff0c;然后ollie在水平方向上畫一條直線&#xff0c;該直線的要求是要…

mkdir -p命令

如果要創建目錄A并創建目錄A的子目錄B&#xff0c;沒有用-p的情況下mkdir 逐個的創建目錄(mkdir A && mkdir A/B); 如果用-p 可以直接創建2個目錄 mkdir -p A/B(如果父目錄A不存在就創建); 來自個人博客&#xff1a; http://www.xuexiyuan.cn/article/detail/182.html

Eclipse在過去十年中的主要成就

正如我所寫的那樣 &#xff0c;Eclipse在11月慶祝了10年來的開源和社區。 Eclipse社區已經形成了許多里程碑 &#xff0c;但是主要成就是什么&#xff1f; Eclipse為實際改變軟件行業做了什么&#xff1f; 這是Eclipse的一些關鍵成就。 1.主導的Java IDE。 Eclipse最初是一個非…

azure git怎么使用_Azure(一)Azure Traffic Manager為我們的Web項目提供負載均衡

一&#xff0c;引言上一篇講到我們將自己的Net Core Web 項目部署到 Azure 的 Web App 的一項 pass 服務&#xff0c;假如隨著項目的日益增長的訪問量&#xff0c;之前部署到單節點的應用可能無法保證其穩定性&#xff0c;可能會導致系統宕機等等問題&#xff0c;這個時候&…

hiho1257 Snake Carpet

題目鏈接&#xff1a;http://hihocoder.com/problemset/problem/1257 題目大意&#xff1a;有n條蛇 編號為1-n 每條蛇的長度跟編號相等 奇數編號的蛇必須拐奇數次&#xff08;除了第一條&#xff09;偶數編號的蛇必須拐偶數次&#xff08;除了第二條&#xff09;問能不能在這種…

POJ 3680_Intervals

題意&#xff1a; 給定區間和該區間對應的權值&#xff0c;挑選一些區間&#xff0c;求使得每個數都不被K個區間覆蓋的最大權值和。 分析&#xff1a; 如果K1&#xff0c;即為區間圖的最大權獨立集問題。可以對區間所有端點排序后利用動態規劃的方法&#xff0c;設dp[i]為只考慮…

MongoDB 數據類型查詢——$type使用

在MongoDB中根據字段的數量類型來查詢數據使用$type操作符來實現&#xff0c;具體使用法語&#xff1a;1db.集合名.find({$type:類型值}) //這里的類型值能使用Number也能使用alias舉個例子&#xff1a;12db.person.find({address:{$type:2}}) //查詢address字段數據…