c#中的yield?return用法詳解
在一個(gè)知名企業(yè)贊助的足球聯(lián)賽中,有256支球隊(duì)參賽。為了確保比賽的順利進(jìn)行,企業(yè)指派了小悅負(fù)責(zé)熬夜加班制定每一個(gè)球隊(duì)的賽程。盡管她對(duì)足球的了解并不多,但是她對(duì)待工作的認(rèn)真態(tài)度卻讓人欽佩。
在小悅的努力下,她順利完成了第一輪、第二輪和第三輪的比賽安排。然而,在大賽開(kāi)始前的模擬比賽中,她發(fā)現(xiàn)了一個(gè)嚴(yán)重的問(wèn)題:由于參賽球隊(duì)過(guò)多,人為的安排總會(huì)導(dǎo)致一些參賽球隊(duì)被遺漏了比賽。這讓她十分焦慮,因?yàn)槿绻荒鼙M快解決這個(gè)問(wèn)題,聯(lián)賽的公平性和競(jìng)爭(zhēng)性將受到嚴(yán)重影響。
為了解決這個(gè)問(wèn)題,小悅開(kāi)始了她的電話咨詢之旅。她先是聯(lián)系了賽事主辦方,了解參賽球隊(duì)的具體情況。隨后,她又聯(lián)系了計(jì)算機(jī)專家,希望找到一個(gè)解決辦法,確保每個(gè)參賽球隊(duì)都能順利比賽。
在與計(jì)算機(jī)專家溝通的過(guò)程中,小悅了解到這個(gè)問(wèn)題并不簡(jiǎn)單,因?yàn)橐紤]到的因素非常多。不過(guò),在專家的幫助下,她逐漸找到了問(wèn)題的根源,并聽(tīng)取專家的意見(jiàn)采取了一些有效的措施來(lái)解決它。最終,經(jīng)過(guò)小悅的不懈努力和計(jì)算機(jī)專家的協(xié)助,每個(gè)參賽球隊(duì)的賽程都被安排得合理而公正。
小悅在熬夜加班制定賽程時(shí),扎著馬尾辮,聚精會(huì)神地盯著電腦屏幕,手指在鍵盤上飛快地敲擊著。她的眼神中閃爍著智慧的光芒,仿佛在告訴人們:她有能力解決任何問(wèn)題。當(dāng)她下意識(shí)挽起頭發(fā)時(shí),這個(gè)簡(jiǎn)單的動(dòng)作,展現(xiàn)了她的優(yōu)雅和美麗。
在這個(gè)過(guò)程中,小悅學(xué)到了很多關(guān)于足球和賽程安排的知識(shí)。她也深刻體會(huì)到團(tuán)隊(duì)合作的重要性,學(xué)會(huì)了如何與同事合作解決問(wèn)題。最終,她的努力得到了上司和公司的肯定,也為她帶來(lái)了不少寶貴的經(jīng)驗(yàn)。
小悅面臨的問(wèn)題如下:錦標(biāo)賽由256支隊(duì)伍組成。這是一場(chǎng)循環(huán)賽(全部比賽),所以有255輪,每支球隊(duì)每輪比賽一次。每支球隊(duì)在錦標(biāo)賽中與其他球隊(duì)對(duì)抗一次(每場(chǎng)比賽在錦標(biāo)賽中不會(huì)重復(fù))。
她的任務(wù)是實(shí)現(xiàn)一個(gè)函數(shù)buildMatchesTable,它接收?qǐng)F(tuán)隊(duì)的數(shù)量(總是正數(shù)和偶數(shù))并返回一個(gè)矩陣。
矩陣中的每一行代表一輪。矩陣的每一列表示一個(gè)匹配。這場(chǎng)比賽是由兩支隊(duì)伍組成的一個(gè)陣列。每個(gè)團(tuán)隊(duì)都用一個(gè)數(shù)字表示,從1開(kāi)始直到團(tuán)隊(duì)數(shù)量。
示例(假設(shè)4只球隊(duì)參賽): 構(gòu)建匹配表(4)
應(yīng)該返回一個(gè)元組矩陣,如下所示:
{ new[]{(1,2),(3,4)},//第一輪:1對(duì)2,3對(duì)4 new[]{(1,3),(2,4)},//第二輪:1對(duì)3,2對(duì)4 new[]{(1,4),(2,3)}//第三輪:1對(duì)4,2對(duì)3 }
算法實(shí)現(xiàn):
public static (int, int)[][] BuildMatchesTable(int n) { var matches = new (int, int)[n - 1][]; // 創(chuàng)建一個(gè)二維數(shù)組,表示比賽表 var teams = FairCycle(n).GetEnumerator(); // 獲取一個(gè)循環(huán)迭代器,用于生成比賽對(duì)陣 for (int i = 0; i < n - 1; i++) { var round = new (int, int)[n / 2]; // 創(chuàng)建一個(gè)一維數(shù)組,表示當(dāng)前輪次的比賽對(duì)陣 for (int t = 0; t < n / 2; t++) { teams.MoveNext(); // 迭代到下一個(gè)比賽對(duì)陣 round[t] = teams.Current; // 將當(dāng)前比賽對(duì)陣添加到當(dāng)前輪次的比賽表中 } matches[i] = round; // 將當(dāng)前輪次的比賽表添加到總的比賽表中 // FairCycle函數(shù)會(huì)在這里負(fù)責(zé)循環(huán)到下一個(gè)比賽對(duì)陣 } return matches; // 返回生成的比賽表 } static IEnumerable<(int,int)> FairCycle(int n) { // 將數(shù)字1到n-1按順時(shí)針?lè)绞脚帕? // 從12點(diǎn)鐘方向開(kāi)始,1位于12點(diǎn)鐘位置 int p = 1; while (true) { // 返回n和12點(diǎn)鐘位置的數(shù)字 yield return (p, n); // 然后返回12點(diǎn)鐘左右兩側(cè)的數(shù)字配對(duì) // 例如,11和1點(diǎn)鐘,10和2點(diǎn)鐘,以此類推 for (int i = 1; i < n / 2; i++) { int l = p + i; l = l > (n - 1) ? l - (n - 1) : l; // 對(duì)n進(jìn)行循環(huán),但不包括n本身 int r = p + (n - 1) - i; r = r > (n - 1) ? r - (n - 1) : r; yield return (l, r); } // 將時(shí)鐘旋轉(zhuǎn)n/2,使得p + n/2現(xiàn)在位于12點(diǎn)鐘位置 p += n / 2; p = p > (n - 1) ? p - (n - 1) : p; // 這將開(kāi)始下一輪的配對(duì) } }
這段代碼實(shí)現(xiàn)了一個(gè)生成比賽表的函數(shù)`BuildMatchesTable`和一個(gè)輔助函數(shù)`FairCycle`。
`BuildMatchesTable`函數(shù)接收一個(gè)正數(shù)n作為參數(shù),返回一個(gè)二維數(shù)組,表示比賽表。
`FairCycle`函數(shù)是一個(gè)模擬時(shí)鐘循環(huán)迭代器,用于生成球隊(duì)比賽對(duì)陣表。
讓我們以`BuildMatchesTable(4)`為例來(lái)詳細(xì)介紹`yield return`的運(yùn)行過(guò)程和直接使用`return`的運(yùn)行過(guò)程:
首先,我們調(diào)用`BuildMatchesTable(4)`函數(shù),它將生成一個(gè)4個(gè)參與者的比賽表。
執(zhí)行`var teams = FairCycle(n).GetEnumerator();`時(shí)會(huì)調(diào)用`FairCycle(n)`方法,并開(kāi)始執(zhí)行其中的代碼。在`FairCycle(n)`方法中,第一次調(diào)用`yield return`語(yǔ)句會(huì)返回一個(gè)`IEnumerator`對(duì)象,該對(duì)象用于迭代生成比賽對(duì)陣。
這意味著在執(zhí)行`var teams = FairCycle(n).GetEnumerator();`之后,`teams`變量將持有一個(gè)可以用于迭代生成比賽對(duì)陣的迭代器對(duì)象。你可以使用`teams.MoveNext()`方法來(lái)逐個(gè)獲取比賽對(duì)陣,每次調(diào)用`MoveNext()`方法時(shí),都會(huì)執(zhí)行`FairCycle(n)`方法中的代碼,直到遇到下一個(gè)`yield return`語(yǔ)句。
使用`yield return`的運(yùn)行過(guò)程如下:
1. 初始化時(shí)鐘位置p為1。2. 進(jìn)入無(wú)限循環(huán)。3. 第一次調(diào)用`yield return`語(yǔ)句,返回當(dāng)前時(shí)鐘位置p和數(shù)字n的配對(duì)`(p, n)`,即`(1, 4)`。此時(shí),比賽表中的第一場(chǎng)比賽是1號(hào)選手對(duì)陣4號(hào)選手。4. 繼續(xù)循環(huán),進(jìn)入第二次調(diào)用`yield return`語(yǔ)句。- 計(jì)算左側(cè)數(shù)字l,它等于p加上i,即2。- 計(jì)算右側(cè)數(shù)字r,它等于p加上(n-1)-i,即3。- 使用`yield return`語(yǔ)句返回左側(cè)數(shù)字l和右側(cè)數(shù)字r的配對(duì)`(l, r)`,即`(2, 3)`。此時(shí),比賽表中的第二場(chǎng)比賽是2號(hào)選手對(duì)陣3號(hào)選手。5. 更新時(shí)鐘位置p,使其加上n/2,即2。此時(shí),時(shí)鐘位置p指向下一輪的起始位置。6. 回到步驟3,開(kāi)始下一輪的配對(duì)。- 第三次調(diào)用`yield return`語(yǔ)句,返回當(dāng)前時(shí)鐘位置p和數(shù)字n的配對(duì)`(p, n)`,即`(2, 4)`。此時(shí),比賽表中的第三場(chǎng)比賽是2號(hào)選手對(duì)陣4號(hào)選手。- 繼續(xù)循環(huán),進(jìn)入第四次調(diào)用`yield return`語(yǔ)句。- 計(jì)算左側(cè)數(shù)字l,它等于p加上i,即3。- 計(jì)算右側(cè)數(shù)字r,它等于p加上(n-1)-i,即2。- 使用`yield return`語(yǔ)句返回左側(cè)數(shù)字l和右側(cè)數(shù)字r的配對(duì)`(l, r)`,即`(3, 2)`。此時(shí),比賽表中的第四場(chǎng)比賽是3號(hào)選手對(duì)陣2號(hào)選手。- 更新時(shí)鐘位置p,使其加上n/2,即4。此時(shí),時(shí)鐘位置p指向下一輪的起始位置。- 回到步驟3,開(kāi)始下一輪的配對(duì)。由于此時(shí)時(shí)鐘位置p超過(guò)了(n-1),即4,所以將其減去(n-1),即得到1。此時(shí),時(shí)鐘位置p重新指向下一輪的起始位置。- 第五次調(diào)用`yield return`語(yǔ)句,返回當(dāng)前時(shí)鐘位置p和數(shù)字n的配對(duì)`(p, n)`,即`(1, 4)`。此時(shí),比賽表中的第五場(chǎng)比賽是1號(hào)選手對(duì)陣4號(hào)選手。- 繼續(xù)循環(huán),進(jìn)入第六次調(diào)用`yield return`語(yǔ)句。- 計(jì)算左側(cè)數(shù)字l,它等于p加上i,即2。- 計(jì)算右側(cè)數(shù)字r,它等于p加上(n-1)-i,即3。- 使用`yield return`語(yǔ)句返回左側(cè)數(shù)字l和右側(cè)數(shù)字r的配對(duì)`(l, r)`,即`(2, 3)`。此時(shí),比賽表中的第六場(chǎng)比賽是2號(hào)選手對(duì)陣3號(hào)選手。- 更新時(shí)鐘位置p,使其加上n/2,即2。此時(shí),時(shí)鐘位置p指向下一輪的起始位置。- 回到步驟3,開(kāi)始下一輪的配對(duì)。由于此時(shí)時(shí)鐘位置p超過(guò)了(n-1),即4,所以將其減去(n-1),即得到1。此時(shí),時(shí)鐘位置p重新指向下一輪的起始位置。- ...
這樣,`yield return`語(yǔ)句會(huì)按需生成比賽對(duì)陣,每次調(diào)用時(shí)返回一個(gè)比賽對(duì)陣,并在下一次調(diào)用時(shí)從迭代器Enumerator上一次離開(kāi)的地方繼續(xù)執(zhí)行。
現(xiàn)在,讓我們來(lái)看看直接使用`return`的運(yùn)行過(guò)程:
1. 初始化時(shí)鐘位置p為1。2. 直接使用`return`語(yǔ)句返回當(dāng)前時(shí)鐘位置p和數(shù)字n的配對(duì)`(p, n)`,即`(1, 4)`。此時(shí),方法立即終止執(zhí)行,并將配對(duì)`(1, 4)`作為方法的結(jié)果返回。這意味著只會(huì)生成一個(gè)比賽對(duì)陣,即1號(hào)選手對(duì)陣4號(hào)選手。
使用`return`語(yǔ)句會(huì)立即終止方法的執(zhí)行,并將指定的值作為方法的結(jié)果返回。這意味著方法只會(huì)生成一個(gè)比賽對(duì)陣,并且無(wú)法再繼續(xù)執(zhí)行其他的代碼。
注:FairCycle方法設(shè)計(jì)為時(shí)鐘的模式是為了確保每個(gè)參賽球隊(duì)都能在錦標(biāo)賽中與其他球隊(duì)公平地對(duì)抗一次。該方法的好處包括:
公平性:通過(guò)時(shí)鐘的模式(所有球隊(duì)對(duì)半匹配一次),每支球隊(duì)都有機(jī)會(huì)與其他球隊(duì)進(jìn)行比賽,確保了比賽的公平性。沒(méi)有球隊(duì)會(huì)被遺漏或被偏好,每個(gè)球隊(duì)都有相同的機(jī)會(huì)競(jìng)爭(zhēng)。
競(jìng)爭(zhēng)性:FairCycle方法確保了每支球隊(duì)都能面對(duì)各種對(duì)手,包括實(shí)力強(qiáng)大的球隊(duì)和實(shí)力較弱的球隊(duì)。這種多樣性的對(duì)手使得比賽更具競(jìng)爭(zhēng)性,增加了球隊(duì)之間的激烈程度。
簡(jiǎn)潔性:時(shí)鐘的模式使得比賽安排更加簡(jiǎn)潔明了。每支球隊(duì)在每輪比賽中都有一個(gè)確定的對(duì)手,沒(méi)有重復(fù)或遺漏的情況,減少了混亂和錯(cuò)誤的可能性。
可預(yù)測(cè)性:由于時(shí)鐘的模式,每支球隊(duì)都可以提前知道在每一輪比賽中將要面對(duì)的對(duì)手。這樣,球隊(duì)可以提前制定戰(zhàn)術(shù)和策略,更好地準(zhǔn)備比賽。
假設(shè)有8支球隊(duì)參加一個(gè)錦標(biāo)賽,并且FairCycle方法被用來(lái)安排比賽。FairCycle方法的時(shí)鐘模式將確保每支球隊(duì)都能與其他球隊(duì)公平地對(duì)抗一次。
首先,我們將8支球隊(duì)編號(hào)為A、B、C、D、E、F、G、H。根據(jù)FairCycle方法,比賽的安排如下:
第一輪: A vs B C vs D E vs F G vs H
第二輪: A vs C B vs D E vs G F vs H
第三輪: A vs D B vs C E vs H F vs G
第四輪: A vs E B vs F C vs G D vs H
第五輪: A vs F B vs E C vs H D vs G
第六輪: A vs G B vs H C vs E D vs F
第七輪: A vs H B vs G C vs F D vs E
通過(guò)這個(gè)例子,我們可以看到每支球隊(duì)都與其他球隊(duì)公平地對(duì)抗了一次。FairCycle方法的時(shí)鐘模式確保了每支球隊(duì)都有相同的機(jī)會(huì)競(jìng)爭(zhēng),沒(méi)有球隊(duì)被遺漏或被偏好。這種安排方式確保了比賽的公平性,并且每支球隊(duì)都有機(jī)會(huì)面對(duì)各種對(duì)手,增加了比賽的競(jìng)爭(zhēng)性。
測(cè)試用例:
using NUnit.Framework; using System; using System.Linq; using System.Collections.Generic; namespace Solution { [TestFixture] public class SolutionTest { [Test] public void Test2Teams() { var expected = new []{ new []{(1, 2)} }; var actual = Tournament.BuildMatchesTable(2); Assert.That(actual, Has.Length.EqualTo(1), "Should have 1 round"); Assert.That(actual[0], Has.Length.EqualTo(1), "The round should have 1 match"); if(actual[0][0].Item1>actual[0][0].Item2) (actual[0][0].Item1, actual[0][0].Item2) = (actual[0][0].Item2, actual[0][0].Item1); Assert.AreEqual(expected, actual, "The match should be team 1 vs team 2"); } [Test] public void Test4Teams() => TestTeams(4); [Test] public void Test20Teams() => TestTeams(20); [Test] public void TestRandom() { Random rand = new Random(); TestTeams(2*(3+rand.Next(3))); TestTeams(2*(6+rand.Next(4))); } public void TestTeams(int numberOfTeams) { List<int> teamsExpected = Enumerable.Range(1, numberOfTeams).ToList(); HashSet<(int, int)> matchesExpected = new HashSet<(int, int)>(); foreach(var round in TournamentSolution.BuildMatchesTable(numberOfTeams)) { foreach(var game in round) matchesExpected.Add(game.Item1>game.Item2?(game.Item2,game.Item1):(game.Item1, game.Item2)); } var actual = Tournament.BuildMatchesTable(numberOfTeams); Assert.That(actual, Has.Length.EqualTo(numberOfTeams-1), $"Should have {numberOfTeams-1} rounds"); foreach(var round in actual) { List<int> teamsByRound = new List<int>(); Assert.That(round, Has.Length.EqualTo(numberOfTeams/2), $"Each round should have {numberOfTeams/2} matches"); foreach(var game in round) { Assert.That(game, Is.InstanceOf(typeof((int, int))), "Each match is a tupple of 2 teams"); teamsByRound.Add(game.Item1); teamsByRound.Add(game.Item2); Assert.True(matchesExpected.Remove(game.Item1>game.Item2?(game.Item2,game.Item1):(game.Item1, game.Item2)), $"{game} is a duplicate or doesn't exist"); } teamsByRound.Sort(); Assert.AreEqual(teamsExpected, teamsByRound, "Each round should have matches with every team"); } Assert.IsEmpty(matchesExpected, "At least one match isn't scheduled"); } } } public class TournamentSolution { public static (int, int)[][] BuildMatchesTable(int numberOfTeams) { List<int> teams = Enumerable.Range(1, numberOfTeams).ToList(); int roundsNbr = numberOfTeams-1, gamesNbr = numberOfTeams /2, rotatorID = roundsNbr-1, buffer = 0; (int, int)[][] result = new (int, int)[roundsNbr][]; for (int i=0; i<roundsNbr; i++) { result[i] = new (int, int)[gamesNbr]; for (int j = 0; j < gamesNbr; j++) result[i][j] = (teams[0 + j], teams[roundsNbr - j]); buffer = teams[rotatorID]; teams.RemoveAt(rotatorID); teams.Insert(0, buffer); } return result; } }
到此這篇關(guān)于c#中的yield return用法的文章就介紹到這了,更多相關(guān)c# yield return內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
c# WPF實(shí)現(xiàn)Windows資源管理器(附源碼)
這篇文章主要介紹了c# WPF實(shí)現(xiàn)Windows資源管理器的示例(附源碼),幫助大家更好的理解和學(xué)習(xí)使用c#,感興趣的朋友可以了解下2021-03-03C# 總結(jié)QueueUserWorkItem傳參幾種方式案例詳解
這篇文章主要介紹了C# 總結(jié)QueueUserWorkItem傳參幾種方式案例詳解,本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-09-09WPF實(shí)現(xiàn)基礎(chǔ)控件之托盤的示例代碼
這篇文章主要為大家詳細(xì)介紹了如何利用WPF實(shí)現(xiàn)托盤這一基礎(chǔ)控件,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)或工作有一定幫助,感興趣的小伙伴可以了解一下2022-10-10C#設(shè)計(jì)模式之Strategy策略模式解決007大破密碼危機(jī)問(wèn)題示例
這篇文章主要介紹了C#設(shè)計(jì)模式之Strategy策略模式解決007大破密碼危機(jī)問(wèn)題,簡(jiǎn)單描述了策略模式的定義并結(jié)合加密解密算法實(shí)例分析了C#策略模式的具體使用方法,需要的朋友可以參考下2017-09-09C#?CM框架實(shí)現(xiàn)多頁(yè)面管理的實(shí)例代碼
這篇文章主要介紹了C#?CM框架下一行代碼實(shí)現(xiàn)多頁(yè)面管理,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-03-03c#實(shí)現(xiàn)把異常寫入日志示例(異常日志)
這篇文章主要介紹了c#實(shí)現(xiàn)把異常寫入日志示例(異常日志),需要的朋友可以參考下2014-04-04深入分析緩存依賴中cachedependency對(duì)象及周邊小講
本篇文章是對(duì)緩存依賴中cachedependency對(duì)象進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-06-06c#調(diào)用c++的DLL的實(shí)現(xiàn)方法
本文主要介紹了c#調(diào)用c++的DLL的實(shí)現(xiàn)方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-05-05