翻譯|使用教程|編輯:王香|2018-10-11 09:30:13.000|閱讀 597 次
概述:通過撲克自牌游戲評分游戲來概述使用LINQ對這些集合進(jìn)行排序和過濾。
# 界面/圖表報(bào)表/文檔/IDE等千款熱門軟控件火熱銷售中 >>
相關(guān)鏈接:
【下載Telerik UI for ASP.NET AJAX最新版本】
在面向?qū)ο缶幊蹋∣OP)中,我們習(xí)慣于使用對象集合或簡單數(shù)據(jù)類型。我們經(jīng)常使用LINQ對這些集合進(jìn)行排序和過濾,作為業(yè)務(wù)邏輯行為或數(shù)據(jù)轉(zhuǎn)換的一部分。雖然這些是我們經(jīng)常執(zhí)行的有用任務(wù),但很容易忘記C#中的函數(shù)可以被視為數(shù)據(jù)。如果我們重新考慮作為數(shù)據(jù)的函數(shù)思考,它使我們能夠發(fā)現(xiàn)OOP中標(biāo)準(zhǔn)問題的替代解決方案。
在本文中,我們將看一下C#Functional Programming研討會(huì)的一個(gè)例子。該場景概述了用于對撲克牌進(jìn)行評分的解決方案。我們將研究一種利用函數(shù)作為數(shù)據(jù)的解決方案的替代模式。通過這種新模式,我們將為游戲的評分機(jī)制提供靈活性。
首先,讓我們來看看用于產(chǎn)生最終得分的各個(gè)評分函數(shù)。每個(gè)功能都是一個(gè)規(guī)則,用于確定手牌是否符合標(biāo)準(zhǔn)。
private bool HasFlush(IEnumerable<Card> cards) => ...; private bool HasRoyalFlush(IEnumerable<Card> cards) => ...; private bool HasPair(IEnumerable<Card> cards) => ...; private bool HasThreeOfAKind(IEnumerable<Card> cards) => ...; private bool HasFourOfAKind(IEnumerable<Card> cards) => ...; private bool HasFullHouse(IEnumerable<Card> cards) => ...; private bool HasStraightFlush(IEnumerable<Card> cards) => ...; private bool HasStraight(IEnumerable<Card> cards) => ...;
下圖說明了游戲得分的規(guī)則。雖然這些函數(shù)表明手是否符合標(biāo)準(zhǔn),但它們不會(huì)直接影響手的最終得分。我們需要安排規(guī)則并按重要性評估它們以產(chǎn)生分?jǐn)?shù)并將其分配給HandRank的枚舉型數(shù)據(jù)。
使用規(guī)則,我們可以通過幾種不同的方式確定最終得分值。以下每個(gè)示例在技術(shù)上都是正確的,并提供其自身的可讀性和簡單性。每種方法的消極方面是規(guī)則執(zhí)行的順序是“hard coded”。
這種評估分?jǐn)?shù)的方法使用臨時(shí)占位符值來跟蹤分?jǐn)?shù)。每次評估都會(huì)進(jìn)行,score并使用最好的HandRank進(jìn)行更新。此方法非常明確,但涉及完成任務(wù)所不需要的額外代碼和變量。
public HandRank GetScore(Hand hand) { var score = HandRank.HighCard; if (HasPair(hand.Cards)) { score = HandRank.Pair; } ... if (HasRoyalFlush(hand.Cards)) { score = HandRank.RoyalFlush; } return score; }
使用返回早期模式允許我們編寫直觀的代碼,通過在發(fā)現(xiàn)評估為真時(shí)立即從函數(shù)返回來返回最佳HandRank。由于應(yīng)用程序需要新規(guī)則,因此該方法易于閱讀且易于修改。
public HandRank GetScore(Hand hand) { if (HasRoyalFlush()) return HandRank.RoyalFlush; ... if (HasPair()) return HandRank.Pair; return HandRank.HighCard; }
可以使用三元運(yùn)算符將函數(shù)寫為單個(gè)表達(dá)式。這與返回早期方法具有類似的效果,但代碼更少。對于某些人來說,這種方法的可讀性可能比其他人更容易。
在所有前面的例子中,操作順序是至關(guān)重要的。如果我們決定在此評分函數(shù)中添加新規(guī)則,那么我們需要確保以正確的順序插入它們以確定正確的分?jǐn)?shù)。
GetScore操作正逐步完成標(biāo)準(zhǔn)評估,并將結(jié)果為true的第一個(gè)規(guī)則與匹配的HandRank匹配。我們可以從函數(shù)式編程思維方式中解決問題,而不是將函數(shù)作為單獨(dú)的語句進(jìn)行評估。讓我們通過將函數(shù)視為數(shù)據(jù)來改變我們對問題的看法。
如果我們將各個(gè)評分函數(shù)看作數(shù)據(jù),我們就可以識別出一種模式。考慮以下評分函數(shù)的signature。
private bool HasFlush(IEnumerable<Card> cards) => ...; private bool HasRoyalFlush(IEnumerable<Card> cards) => ...; private bool HasPair(IEnumerable<Card> cards) => ...; private bool HasThreeOfAKind(IEnumerable<Card> cards) => ...; private bool HasFourOfAKind(IEnumerable<Card> cards) => ...; private bool HasFullHouse(IEnumerable<Card> cards) => ...; private bool HasStraightFlush(IEnumerable<Card> cards) => ...; private bool HasStraight(IEnumerable<Card> cards) => ...;
每個(gè)功能都屬于同一類型Func, bool>。由于我們有許多相同類型的數(shù)據(jù),我們可以將它們安排在一個(gè)集合或數(shù)組中。接下來,我們需要將每個(gè)函數(shù)與它所代表的HandRank相匹配。例如:HasPair將得分為HandRank.Pair。使用元組,我們可以輕松創(chuàng)建此映射,而無需專門的類。在C#7.1中,我們可以通過簡單地在括號中包含多個(gè)值來創(chuàng)建元組。使用函數(shù)及其映射的枚舉器,我們可以構(gòu)建集合。
private List<(Func<IEnumerable<Card>, bool> eval, HandRank rank)> GameRules() => new List<(Func<IEnumerable<Card>, bool> eval, HandRank rank)> { (cards => HasRoyalFlush(cards), HandRank.RoyalFlush), (cards => HasStraightFlush(cards), HandRank.StraightFlush), (cards => HasFourOfAKind(cards), HandRank.FourOfAKind), (cards => HasFullHouse(cards), HandRank.FullHouse), (cards => HasFlush(cards), HandRank.Flush), (cards => HasStraight(cards), HandRank.Straight), (cards => HasThreeOfAKind(cards), HandRank.ThreeOfAKind), (cards => HasPair(cards), HandRank.Pair), (cards => true, HandRank.HighCard), };
為了保持代碼整潔,我們將把集合的構(gòu)造包裝在一個(gè)名為GameRules的函數(shù)中。我們以后可以將其作為其他游戲規(guī)則的可擴(kuò)展點(diǎn)。通過將排名系統(tǒng)移到GetScore方法之外,可以修改或替換新的評估和排名。對于可能的最低排名,我們將簡單地使用true來表示默認(rèn)評估。
現(xiàn)在我們將使用LINQ重寫GetScore方法來評估列表。通過將列表中的項(xiàng)目視為數(shù)據(jù),我們可以利用排序來確保它們以正確的順序執(zhí)行。我們不再需要擔(dān)心“硬編碼”執(zhí)行順序。我們可以使用.OrderByDescending(card => card.rank)將評估從最強(qiáng)等級排序到最弱,因?yàn)镠andRank.RoyalFlush具有最高值。
public HandRank GetScore(Hand hand) => GameRules() .OrderByDescending(rule => rule.rank) .First(rule => rule..rank;
最后,為了得到結(jié)果,我們將進(jìn)行評估。最有效的方法是使用First LINQ方法。由于First是短路運(yùn)算符,因此只要找到返回true的第一個(gè)項(xiàng)目,它就會(huì)停止對項(xiàng)目進(jìn)行評估。當(dāng)?shù)谝豁?xiàng)計(jì)算結(jié)果為true時(shí),我們將從數(shù)據(jù)集中獲取元組的排名值并返回它。排名值是我們的最終得分。
C#中的函數(shù)通常被認(rèn)為是靜態(tài)語句,我們的應(yīng)用程序可以使用它來更改系統(tǒng)中的數(shù)據(jù)狀態(tài)。通過將我們的觀點(diǎn)從命令性轉(zhuǎn)變?yōu)楣δ苄裕覀兛梢哉业教娲鉀Q方案。為問題帶來功能性思維的一種方法是記住函數(shù)也是數(shù)據(jù),并且符合許多與C#中其他數(shù)據(jù)類型相同的規(guī)則。在這個(gè)例子中,我們看到了一種功能方法如何將基于語句的硬編碼評估轉(zhuǎn)換為靈活的排序和基于地圖的評估。這個(gè)簡單的更改擴(kuò)展了應(yīng)用程序的功能,并在添加新條件時(shí)減少了摩擦,因?yàn)闆]有預(yù)定義操作順序。
本站文章除注明轉(zhuǎn)載外,均為本站原創(chuàng)或翻譯。歡迎任何形式的轉(zhuǎn)載,但請務(wù)必注明出處、不得修改原文相關(guān)鏈接,如果存在內(nèi)容上的異議請郵件反饋至chenjj@fc6vip.cn