Telerik UI for ASP.NET AJAX
在面向?qū)ο缶幊蹋?/span>OOP)中,我們習(xí)慣于使用對(duì)象集合或簡(jiǎn)單數(shù)據(jù)類型。我們經(jīng)常使用LINQ對(duì)這些集合進(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è)例子。該場(chǎng)景概述了用于對(duì)撲克牌進(jìn)行評(píng)分的解決方案。我們將研究一種利用函數(shù)作為數(shù)據(jù)的解決方案的替代模式。通過這種新模式,我們將為游戲的評(píng)分機(jī)制提供靈活性。
(一)評(píng)分標(biāo)準(zhǔn)
首先,讓我們來看看用于產(chǎn)生最終得分的各個(gè)評(píng)分函數(shù)。每個(gè)功能都是一個(gè)規(guī)則,用于確定手牌是否符合標(biāo)準(zhǔn)。
private boolHasFlush(IEnumerable cards) => ...;
private boolHasRoyalFlush(IEnumerable cards) => ...;
private boolHasPair(IEnumerable cards) => ...;
private boolHasThreeOfAKind(IEnumerable cards) => ...;
private boolHasFourOfAKind(IEnumerable cards) => ...;
private boolHasFullHouse(IEnumerable cards) => ...;
private bool HasStraightFlush(IEnumerablecards) => ...;
private boolHasStraight(IEnumerable cards) => ...;
下圖說明了游戲得分的規(guī)則。雖然這些函數(shù)表明手是否符合標(biāo)準(zhǔn),但它們不會(huì)直接影響手的最終得分。我們需要安排規(guī)則并按重要性評(píng)估它們以產(chǎn)生分?jǐn)?shù)并將其分配給HandRank的枚舉型數(shù)據(jù)。

(二)確定分?jǐn)?shù)
使用規(guī)則,我們可以通過幾種不同的方式確定最終得分值。以下每個(gè)示例在技術(shù)上都是正確的,并提供其自身的可讀性和簡(jiǎn)單性。每種方法的消極方面是規(guī)則執(zhí)行的順序是“hard coded”。
2.1維護(hù)狀態(tài)
這種評(píng)估分?jǐn)?shù)的方法使用臨時(shí)占位符值來跟蹤分?jǐn)?shù)。每次評(píng)估都會(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;
}
2.2返回值
使用返回早期模式允許我們編寫直觀的代碼,通過在發(fā)現(xiàn)評(píng)估為真時(shí)立即從函數(shù)返回來返回最佳HandRank。由于應(yīng)用程序需要新規(guī)則,因此該方法易于閱讀且易于修改。
public HandRank GetScore(Hand hand)
{
if (HasRoyalFlush()) return HandRank.RoyalFlush;
...
if (HasPair()) return HandRank.Pair;
return HandRank.HighCard;
}
2.3三元表達(dá)式
可以使用三元運(yùn)算符將函數(shù)寫為單個(gè)表達(dá)式。這與返回早期方法具有類似的效果,但代碼更少。對(duì)于某些人來說,這種方法的可讀性可能比其他人更容易。
在所有前面的例子中,操作順序是至關(guān)重要的。如果我們決定在此評(píng)分函數(shù)中添加新規(guī)則,那么我們需要確保以正確的順序插入它們以確定正確的分?jǐn)?shù)。
(二)思考功能
GetScore操作正逐步完成標(biāo)準(zhǔn)評(píng)估,并將結(jié)果為true的第一個(gè)規(guī)則與匹配的HandRank匹配。我們可以從函數(shù)式編程思維方式中解決問題,而不是將函數(shù)作為單獨(dú)的語句進(jìn)行評(píng)估。讓我們通過將函數(shù)視為數(shù)據(jù)來改變我們對(duì)問題的看法。
如果我們將各個(gè)評(píng)分函數(shù)看作數(shù)據(jù),我們就可以識(shí)別出一種模式??紤]以下評(píng)分函數(shù)的signature。
private boolHasFlush(IEnumerable cards) => ...;
private bool HasRoyalFlush(IEnumerablecards) => ...;
private boolHasPair(IEnumerable cards) => ...;
private boolHasThreeOfAKind(IEnumerable cards) => ...;
private boolHasFourOfAKind(IEnumerable cards) => ...;
private boolHasFullHouse(IEnumerable cards) => ...;
private boolHasStraightFlush(IEnumerable cards) => ...;
private boolHasStraight(IEnumerable cards) => ...;
每個(gè)功能都屬于同一類型Func, bool>。由于我們有許多相同類型的數(shù)據(jù),我們可以將它們安排在一個(gè)集合或數(shù)組中。接下來,我們需要將每個(gè)函數(shù)與它所代表的HandRank相匹配。例如:HasPair將得分為HandRank.Pair。使用元組,我們可以輕松創(chuàng)建此映射,而無需專門的類。在C#7.1中,我們可以通過簡(jiǎn)單地在括號(hào)中包含多個(gè)值來創(chuàng)建元組。使用函數(shù)及其映射的枚舉器,我們可以構(gòu)建集合。
privateList<(Func, bool> eval, HandRank rank)>GameRules() =>
new List<(Func, bool> eval, HandRankrank)>
{
(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方法之外,可以修改或替換新的評(píng)估和排名。對(duì)于可能的最低排名,我們將簡(jiǎn)單地使用true來表示默認(rèn)評(píng)估。
(三)使用LINQ進(jìn)行重構(gòu)
現(xiàn)在我們將使用LINQ重寫GetScore方法來評(píng)估列表。通過將列表中的項(xiàng)目視為數(shù)據(jù),我們可以利用排序來確保它們以正確的順序執(zhí)行。我們不再需要擔(dān)心“硬編碼”執(zhí)行順序。我們可以使用.OrderByDescending(card => card.rank)將評(píng)估從最強(qiáng)等級(jí)排序到最弱,因?yàn)?/span>HandRank.RoyalFlush具有最高值。
public HandRank GetScore(Hand hand) =>GameRules()
.OrderByDescending(rule=> rule.rank)
.First(rule => rule.eval(hand.Cards)).rank;
最后,為了得到結(jié)果,我們將進(jìn)行評(píng)估。最有效的方法是使用First LINQ方法。由于First是短路運(yùn)算符,因此只要找到返回true的第一個(gè)項(xiàng)目,它就會(huì)停止對(duì)項(xiàng)目進(jìn)行評(píng)估。當(dāng)?shù)谝豁?xiàng)計(jì)算結(jié)果為true時(shí),我們將從數(shù)據(jù)集中獲取元組的排名值并返回它。排名值是我們的最終得分。
(四)結(jié)論
C#中的函數(shù)通常被認(rèn)為是靜態(tài)語句,我們的應(yīng)用程序可以使用它來更改系統(tǒng)中的數(shù)據(jù)狀態(tài)。通過將我們的觀點(diǎn)從命令性轉(zhuǎn)變?yōu)楣δ苄?,我們可以找到替代解決方案。為問題帶來功能性思維的一種方法是記住函數(shù)也是數(shù)據(jù),并且符合許多與C#中其他數(shù)據(jù)類型相同的規(guī)則。在這個(gè)例子中,我們看到了一種功能方法如何將基于語句的硬編碼評(píng)估轉(zhuǎn)換為靈活的排序和基于地圖的評(píng)估。這個(gè)簡(jiǎn)單的更改擴(kuò)展了應(yīng)用程序的功能,并在添加新條件時(shí)減少了摩擦,因?yàn)闆]有預(yù)定義操作順序。
京ICP備09015132號(hào)-996 | 違法和不良信息舉報(bào)電話:4006561155
© Copyright 2000-2026 北京哲想軟件有限公司版權(quán)所有 | 地址:北京市海淀區(qū)西三環(huán)北路50號(hào)豪柏大廈C2座11層1105室
北京哲想軟件集團(tuán)旗下網(wǎng)站:哲想軟件 | 哲想動(dòng)畫