本文章向大家介紹如何在Java項(xiàng)目中實(shí)現(xiàn)惰性計(jì)算的基本知識(shí)點(diǎn)總結(jié)和需要注意事項(xiàng),具有一定的參考價(jià)值,需要的朋友可以參考一下。
創(chuàng)新互聯(lián)是專業(yè)的南譙網(wǎng)站建設(shè)公司,南譙接單;提供網(wǎng)站建設(shè)、成都網(wǎng)站建設(shè),網(wǎng)頁設(shè)計(jì),網(wǎng)站設(shè)計(jì),建網(wǎng)站,PHP網(wǎng)站建設(shè)等專業(yè)做網(wǎng)站服務(wù);采用PHP框架,可快速的進(jìn)行南譙網(wǎng)站開發(fā)網(wǎng)頁制作和功能擴(kuò)展;專業(yè)做搜索引擎喜愛的網(wǎng)站,專業(yè)的做網(wǎng)站團(tuán)隊(duì),希望更多企業(yè)前來合作!
Java主要應(yīng)用于:1. web開發(fā);2. Android開發(fā);3. 客戶端開發(fā);4. 網(wǎng)頁開發(fā);5. 企業(yè)級(jí)應(yīng)用開發(fā);6. Java大數(shù)據(jù)開發(fā);7.游戲開發(fā)等。
前言
惰性計(jì)算(盡可能延遲表達(dá)式求值)是許多函數(shù)式編程語言的特性。惰性集合在需要時(shí)提供其元素,無需預(yù)先計(jì)算它們,這帶來了一些好處。
首先,您可以將耗時(shí)的計(jì)算推遲到絕對(duì)需要的時(shí)候。其次,您可以創(chuàng)造無限個(gè)集合,只要它們繼續(xù)收到請(qǐng)求,就會(huì)繼續(xù)提供元素。第三,map 和 filter 等函數(shù)的惰性使用讓您能夠得到更高效的代碼(請(qǐng)參閱 參考資料 中的鏈接,加入由 Brian Goetz 組織的相關(guān)討論)。Java 并沒有為惰性提供原生支持,但一些框架和后繼語言支持這種惰性。
假定使用此偽代碼片段來打印列表的長度:
print length([2+1, 3*2, 1/0, 5-4])
如果您嘗試執(zhí)行此代碼,結(jié)果會(huì)因?yàn)榇a的編程語言類型的不同而有所不同:嚴(yán)格或不嚴(yán)格(也被稱為惰性)。在嚴(yán)格的編程語言中,執(zhí)行(或編譯)此代碼產(chǎn)生一個(gè) DivByZero 異常,原因是列表的第三個(gè)元素。在不嚴(yán)格的語言中,其結(jié)果是 4,它準(zhǔn)確地報(bào)告了列表中的項(xiàng)目數(shù)。
畢竟,我調(diào)用的方法是 length(),而不是 lengthAndThrowExceptionWhenDivByZero()!Haskell 是為數(shù)不多的仍在使用的不嚴(yán)格語言??上У氖牵琂ava 不支持不嚴(yán)格的計(jì)算,但您仍然可以在 Java 中使用惰性的概念。
在 Java 中的惰性迭代器
Java 缺乏對(duì)惰性集合的原生支持,但這并不意味著您不能使用 Iterator 模擬一個(gè)惰性集合。在本系列的前幾篇文章中,我使用了一個(gè)簡單的素?cái)?shù)算法來說明函數(shù)式概念。我會(huì)在 上期文章 中介紹的優(yōu)化類的基礎(chǔ)上展開本文的討論,同時(shí)提供清單 1 中展示的增強(qiáng):
清單 1. 確定素?cái)?shù)的簡單算法
import java.util.HashSet; import java.util.Set; import static java.lang.Math.sqrt; public class Prime { public static boolean isFactor(int potential, int number) { return number % potential == 0; } public static Set<Integer> getFactors(int number) { Set<Integer> factors = new HashSet<Integer>(); factors.add(1); factors.add(number); for (int i = 2; i < sqrt(number) + 1; i++) if (isFactor(i, number)) { factors.add(i); factors.add(number / i); } return factors; } public static int sumFactors(int number) { int sum = 0; for (int i : getFactors(number)) sum += i; return sum; } public static boolean isPrime(int number) { return number == 2 || sumFactors(number) == number + 1; } public static Integer nextPrimeFrom(int lastPrime) { lastPrime++; while (! isPrime(lastPrime)) lastPrime++; return lastPrime; } }
前面的一期文章 詳細(xì)討論了這個(gè)類是如何確定某個(gè)整數(shù)是否是素?cái)?shù)的細(xì)節(jié)。在 清單 1 中,我添加了 nextPrimeFrom() 方法,根據(jù)輸入的參數(shù)生成下一個(gè)素?cái)?shù)。該方法在本文即將出現(xiàn)的示例中發(fā)揮了重要的作用。
一般情況下,開發(fā)人員認(rèn)為迭代器會(huì)使用集合作為后備存儲(chǔ),但是支持 Iterator 接口的任何集合都符合這個(gè)條件。因此,我可以創(chuàng)建一個(gè)素?cái)?shù)的無限迭代器,如清單 2 所示:
清單 2. 創(chuàng)建一個(gè)惰性迭代器
public class PrimeIterator implements Iterator<Integer> { private int lastPrime = 1; public boolean hasNext() { return true; } public Integer next() { return lastPrime = Prime.nextPrimeFrom(lastPrime); } public void remove() { throw new RuntimeException("Can't change the fundamental nature of the universe!"); } }
在 清單 2 中,hasNext() 方法始終返回 true,因?yàn)榫臀覀兡壳八莆盏闹R(shí),素?cái)?shù)的數(shù)量是無限的。remove() 方法在此處不適用,所以在意外調(diào)用情況下,會(huì)拋出一個(gè)異常。沉穩(wěn)的做法是使用 next() 方法,它用一行代碼處理兩件事。第一,它調(diào)用我在 清單 1 中添加的 nextPrimeFrom() 方法,根據(jù)上一個(gè)素?cái)?shù)生成下一個(gè)素?cái)?shù)。第二,它利用了 Java 在單個(gè)語句中完成賦值與返回結(jié)果的能力,更新內(nèi)部的 lastPrime 字段。我在清單 3 中執(zhí)行惰性迭代器:
清單 3. 測試惰性迭代器
public class PrimeTest { private ArrayList<Integer> PRIMES_BELOW_50 = new ArrayList<Integer>() {{ add(2); add(3); add(5); add(7); add(11); add(13); add(17); add(19); add(23); add(29); add(31); add(37); add(41); add(43); add(47); }}; @Test public void prime_iterator() { Iterator<Integer> it = new PrimeIterator(); for (int i : PRIMES_BELOW_50) { assertTrue(i == it.next()); } } }
在 清單 3中,我創(chuàng)建了一個(gè) PrimeIterator,并驗(yàn)證它會(huì)報(bào)告前 50 個(gè)素?cái)?shù)。雖然這不是迭代器的典型用法,但它模仿一些惰性集合的有用行為。
使用 LazyList
Jakarta Commons 包括一個(gè) LazyList 類,它結(jié)合使用了裝飾設(shè)計(jì)模式和工廠。如果要使用 Commons LazyList,則必須包裝一個(gè)現(xiàn)有列表,使其具有惰性,并為新值創(chuàng)建一個(gè)工廠。請(qǐng)考慮使用清單 4 中的 LazyList:
清單 4. 測試 Commons LazyList
public class PrimeTest { private ArrayList<Integer> PRIMES_BELOW_50 = new ArrayList<Integer>() {{ add(2); add(3); add(5); add(7); add(11); add(13); add(17); add(19); add(23); add(29); add(31); add(37); add(41); add(43); add(47); }}; @Test public void prime_factory() { List<Integer> primes = new ArrayList<Integer>(); List<Integer> lazyPrimes = LazyList.decorate(primes, new PrimeFactory()); for (int i = 0; i < PRIMES_BELOW_50.size(); i++) assertEquals(PRIMES_BELOW_50.get(i), lazyPrimes.get(i)); } }
在 清單 4 中,我創(chuàng)建一個(gè)新的空白 ArrayList 并將它包裝在 Commons LazyList.decorate() 方法中,還有一個(gè)PrimeFactory 用于生成新值。Commons LazyList 使用列表中已存在的值,不論該值是什么,當(dāng)對(duì)一個(gè)尚未賦值的索引調(diào)用 get() 方法時(shí),LazyList 會(huì)使用工廠(在本例中為 PrimeFactory())生成和填充值。PrimeFactory 出現(xiàn)在清單 5 中:
清單 5. LazyList 使用的 PrimeFactory
public class PrimeFactory implements Factory { private int index = 0; @Override public Object create() { return Prime.indexedPrime(index++); } }
所有惰性列表都需要使用某種方法來生成后續(xù)的值。在 清單 2 中,我使用了 next() 方法和 Prime 的 nextPrimeFrom() 方法的組合。對(duì)于 清單 4 中的 Commons LazyList,我使用了 PrimeFactory 實(shí)例。
Commons LazyList 實(shí)現(xiàn)有一個(gè)特點(diǎn),就是在請(qǐng)求新值時(shí),沒有信息傳遞給工廠方法。根據(jù)設(shè)計(jì),它甚至沒有傳遞所請(qǐng)求元素的索引,強(qiáng)制在 PrimeFactory 類上維護(hù)當(dāng)前狀態(tài)。這產(chǎn)生了對(duì)返回列表的不必要的依賴(因?yàn)樗仨毘跏蓟癁榭?,以便讓索引?PrimeFactory 的內(nèi)部狀態(tài)保持同步)。Commons LazyList 是最好的基礎(chǔ)實(shí)現(xiàn),但還有更好的開源替代方案,如 Totally Lazy。
Totally Lazy
Totally Lazy 是一個(gè)框架,它將一流的惰性添加到 Java。在 前面的一期文章 中,我介紹過 Totally Lazy,但介紹得并不詳細(xì)。該框架的目標(biāo)之一是使用靜態(tài)導(dǎo)入集合來創(chuàng)建一個(gè)高度可讀的 Java 代碼。清單 6 中簡單的素?cái)?shù)查找程序在編寫時(shí)充分利用了這個(gè) Totally Lazy 特性:
清單 6. Totally Lazy,充分利用靜態(tài)導(dǎo)入
import com.googlecode.totallylazy.Predicate; import com.googlecode.totallylazy.Sequence; import static com.googlecode.totallylazy.Predicates.is; import static com.googlecode.totallylazy.numbers.Numbers.equalTo; import static com.googlecode.totallylazy.numbers.Numbers.increment; import static com.googlecode.totallylazy.numbers.Numbers.range; import static com.googlecode.totallylazy.numbers.Numbers.remainder; import static com.googlecode.totallylazy.numbers.Numbers.sum; import static com.googlecode.totallylazy.numbers.Numbers.zero; import static com.googlecode.totallylazy.predicates.WherePredicate.where; public class Prime { public static Predicate<Number> isFactor(Number n) { return where(remainder(n), is(zero)); } public static Sequence<Number> factors(Number n){ return range(1, n).filter(isFactor(n)); } public static Number sumFactors(Number n){ return factors(n).reduce(sum); } public static boolean isPrime(Number n){ return equalTo(increment(n), sumFactors(n)); } }
在 清單 6 中,在完成靜態(tài)導(dǎo)入后,代碼是非典型的 Java,但有很強(qiáng)的可讀性。Totally Lazy 的部分靈感來自 JUnit 的 Hamcrest 測試擴(kuò)展的接口。它還使用了 Hamcrest 的一些類。isFactor() 方法變成了對(duì) where() 的調(diào)用,結(jié)合使用了 Totally Lazy 的 remainder() 與 Hamcrest is() 方法。
同樣,factors() 方法變成了針對(duì) range() 對(duì)象調(diào)用 filter(),我使用現(xiàn)已熟悉的 reduce() 方法來確定總和。最后,isPrime() 方法使用 Hamcrest 的 equalTo() 方法來確定因數(shù)的總和是否等于遞增的數(shù)字。
細(xì)心的讀者會(huì)注意到,清單 6 中的實(shí)現(xiàn)的確優(yōu)化了我在前面的一期文章 中所提及的實(shí)現(xiàn),使用了更高效的算法來確定因數(shù)。優(yōu)化后的版本如清單 7 所示:
清單 7. 優(yōu)化的素?cái)?shù)查找程序的 Totally Lazy 實(shí)現(xiàn)
public class PrimeFast { public static Predicate<Number> isFactor(Number n) { return where(remainder(n), is(zero)); } public static Sequence<Number> getFactors(final Number n){ Sequence<Number> lowerRange = range(1, squareRoot(n)).filter(isFactor(n)); return lowerRange.join(lowerRange.map(divide().apply(n))); } public static Sequence<Number> factors(final Number n) { return getFactors(n).memorise(); } public static Number sumFactors(Number n){ return factors(n).reduce(sum); } public static boolean isPrime(Number n){ return equalTo(increment(n), sumFactors(n)); } }
清單 7 中有兩個(gè)主要變化。首先,我改進(jìn)了 getFactors() 算法,以獲得平方根下的因數(shù),然后生成平方根之上的對(duì)稱因數(shù)。在 Totally Lazy 中,即使像 divide() 這樣的操作也可以使用連貫接口樣式來表達(dá)。第二個(gè)變化涉及內(nèi)存,它會(huì)自動(dòng)緩存使用相同參數(shù)的函數(shù)調(diào)用,我已經(jīng)修改了 sumFactors() 方法,以便使用 getFactors() 方法,它是內(nèi)存化的 getFactors() 方法。Totally Lazy 將內(nèi)存實(shí)現(xiàn)為框架的一部分,因此,實(shí)現(xiàn)此優(yōu)化并不需要更多的代碼,但框架的作者將其拼寫為 memorise(),而不是更傳統(tǒng)的(如在 Groovy 中)memoize()。
像 Totally Lazy 這個(gè)名稱所聲明的那樣,Totally Lazy 試圖在整個(gè)框架中盡可能使用惰性。事實(shí)上,Totally Lazy 框架本身就包含了一個(gè) primes() 生成程序,它使用框架的構(gòu)建塊實(shí)現(xiàn)素?cái)?shù)的無限序列。請(qǐng)考慮 Numbers 類的節(jié)選代碼,如清單 8 所示:
清單 8. 實(shí)現(xiàn)無限素?cái)?shù)的 Totally Lazy 節(jié)選代碼
public static Function1<Number, Number> nextPrime = new Function1<Number, Number>() { @Override public Number call(Number number) throws Exception { return nextPrime(number); } }; public static Computation<Number> primes = computation(2, computation(3, nextPrime)); public static Sequence<Number> primes() { return primes; } public static LogicalPredicate<Number> prime = new LogicalPredicate<Number>() { public final boolean matches(final Number candidate) { return isPrime(candidate); } }; public static Number nextPrime(Number number) { return iterate(add(2), number).filter(prime).second(); }
nextPrime() 方法創(chuàng)建了一個(gè)新的 Function1,它是一個(gè)偽高階函數(shù)的 Totally Lazy 實(shí)現(xiàn),該方法旨在接受單一 Number 參數(shù),并產(chǎn)生一個(gè) Number 結(jié)果。在本例中,它返回 nextPrime() 方法的結(jié)果。primes 變量已創(chuàng)建,用于存儲(chǔ)素?cái)?shù)的狀態(tài),使用 2(第一個(gè)素?cái)?shù))作為種子值執(zhí)行計(jì)算,并使用一個(gè)新的計(jì)算來產(chǎn)生下一個(gè)素?cái)?shù)。這是惰性實(shí)現(xiàn)中的典型模式:保存下一個(gè)元素,加上用于產(chǎn)生隨后的值的方法。prime() 方法僅僅是一個(gè)包裝程序,圍繞之前執(zhí)行的 prime 計(jì)算。
為了確定 清單 8 中的 nextPrime(),Totally Lazy 創(chuàng)建了一個(gè)新的 LogicalPredicate,以封裝已確定的素?cái)?shù),然后創(chuàng)建 nextPrime() 方法,它在 Totally Lazy 中使用良好的接口來確定下一個(gè)素?cái)?shù)。
在 Java 中使用低層的靜態(tài)導(dǎo)入,以促進(jìn)可讀代碼的使用,Totally Lazy 在這方面表現(xiàn)得很出色。許多開發(fā)人員認(rèn)為 Java 對(duì)于內(nèi)部的域特定語言是較差的主機(jī),但 Totally Lazy 消除了這種態(tài)度。它積極地采用惰性,延緩所有可能的操作。
以上就是小編為大家?guī)淼娜绾卧贘ava項(xiàng)目中實(shí)現(xiàn)惰性計(jì)算的全部內(nèi)容了,希望大家多多支持創(chuàng)新互聯(lián)!
文章題目:如何在Java項(xiàng)目中實(shí)現(xiàn)惰性計(jì)算
本文URL:http://aaarwkj.com/article18/jpocgp.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供企業(yè)網(wǎng)站制作、網(wǎng)站維護(hù)、網(wǎng)站策劃、品牌網(wǎng)站制作、定制開發(fā)、網(wǎng)站收錄
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)