C#用鏈式辦法表達輪回嵌套。本站提示廣大學習愛好者:(C#用鏈式辦法表達輪回嵌套)文章只能為提供參考,不一定能成為您想要的結果。以下是C#用鏈式辦法表達輪回嵌套正文
一.起緣
故事緣於一名同伙的一道題:
同伙四人玩LOL游戲。第一局,分離選擇地位:中單,上單,ADC,幫助;第二局新參加的同伴要選上單,四人可選地位變成:中單,打野,ADC,幫助;請求,第二局四人每人不得選擇和第一局雷同的地位,請問兩局綜合斟酌有若干種地位選擇方法?
關於像我這邊不懂游戲的人來說,看不懂。因而有了這個版本:
有4小我,4只椅子,第一局每人坐一只椅子,第二局去失落第2只椅子,增長第5只椅子,每人坐一只椅子,並且每一個人不克不及與第一局坐雷同的椅子。問兩局綜合斟酌,共有若干種能夠的情形?
我一開端的設法主意是如許的,4小我就叫ABCD:第1局能夠數是4*3*2*1=24,假如A第1局選了第2張椅,則A有4種能夠,不然A有3種能夠。對B來說,假如A選了B第一局的椅,則B有3種能夠,不然B有2種能夠(列隊本身第一局和A第二局已選)……想到這裡我就暈了,情形越分越多。
二.原始的for嵌套
原來是一道數學題,應當由常識算出來有若干種,但我忽然有個設法主意,不如用盤算機窮舉出出來。一來可認為各類猜想供給一個准確的謎底,二來也許可以從謎底反推出(數學上的)盤算辦法。然後就寫了第1版:
static Seat data = new Seat(); public static void Run() { for (int a = 0; a < 4; a++) { if (data.IsSelected(0, a)) //第1局編號0。假如曾經被人坐了。 continue; data.Selected(0, a, "A"); //第1局編號0。A坐a椅。 for (int b = 0; b < 4; b++) { if (data.IsSelected(0, b)) continue; data.Selected(0, b, "B"); for (int c = 0; c < 4; c++) { if (data.IsSelected(0, c)) continue; data.Selected(0, c, "C"); for (int d = 0; d < 4; d++) { if (data.IsSelected(0, d)) continue; data.Selected(0, d, "D"); for (int a2 = 0; a2 < 5; a2++) { if (a2 == 1) continue; if (data.IsSelected(1, a2)) //第2局編號1 continue; if (data.IsSelected(0, a2, "A")) //假如第1局A坐了a2椅 continue; data.Selected(1, a2, "A"); for (int b2 = 0; b2 < 5; b2++) { if (b2 == 1) continue; if (data.IsSelected(1, b2)) continue; if (data.IsSelected(0, b2, "B")) continue; data.Selected(1, b2, "B"); for (int c2 = 0; c2 < 5; c2++) { if (c2 == 1) continue; if (data.IsSelected(1, c2)) continue; if (data.IsSelected(0, c2, "C")) continue; data.Selected(1, c2, "C"); for (int d2 = 0; d2 < 5; d2++) { if (d2 == 1) continue; if (data.IsSelected(1, d2)) continue; if (data.IsSelected(0, d2, "D")) continue; data.Selected(1, d2, "D"); data.Count++; //能夠的情形數加1 Console.WriteLine("{0,5} {1}", data.Count, data.Current); data.UnSelected(1, d2); } data.UnSelected(1, c2); } data.UnSelected(1, b2); } data.UnSelected(1, a2); } data.UnSelected(0, d); } data.UnSelected(0, c); } data.UnSelected(0, b); } data.UnSelected(0, a); //A起身(釋放坐椅) } }
部門運轉成果:
解釋:
1.ABCD是人名
2.“.”代表沒有人
3.地位是是坐位
4.-右邊是第1局,左邊是第2局
5.數字是序號
1 A B C D .-B . A C D
2 A B C D .-C . A B D
3 A B C D .-D . A B C
4 A B C D .-D . A C B
5 A B C D .-B . D A C
6 A B C D .-C . B A D
7 A B C D .-D . B A C
8 A B C D .-C . D A B
9 A B C D .-B . D C A
10 A B C D .-D . B C A
11 A B C D .-C . D B A
12 A B D C .-B . A D C
...
262 D C B A .-B . C D A
263 D C B A .-B . D C A
264 D C B A .-C . D B A
算出來是264種。從謎底下去看是每11種是一組,一組中第1局的坐法是雷同的,也就是說關於第一局的每種情形,第2局都是有11種分歧的能夠。而第一局的能夠性是24,所以謎底是24*11=264。而第2局為何是11種能夠,前面再說。
三.想要鏈式寫法
主題來了,像適才的第1版的寫法太逝世板太費事了。
假如能像如許寫代碼就爽了:
obj.Try("A").Try("B").Try("C").Try("D").Try2("A").Try2("B").Try2("C").Try2("D").Write();
而如許的代碼平日的邏輯是履行Try("A")辦法,然後履行Try("A")它return的對象的Try("B")辦法……,等於Try("B")辦法只被履行1次,而我願望的是Try("B")辦法被Try("A")外部輪回挪用n次,Try("C")辦法又被Try("B")辦法挪用m次。想一想第1版的for套for不難解白為何要尋求如許的後果。假如Try("A")履行完了,再去履行Try("B"),那末Try("B")確定不會被挪用屢次,所以得延遲Try("A")的履行,同理也延遲一切Try和Try2的履行。因為lambda表達生成有延遲盤算的特征,因而很快寫出了第2版:
public static void Run2() { Try("A", () => Try("B", () => Try("C", () => Try("D", () => Try2("A", () => Try2("B", () => Try2("C", () => Try2("D", null ) ) ) ) ) ) ) ); } public static void Try(string name, Action action) //第1局 { for (int i = 0; i < 4; i++) { if (data.IsSelected(0, i)) continue; data.Selected(0, i, name); if (action == null) { Console.WriteLine(data.Current); } else { action(); } data.UnSelected(0, i); } } public static void Try2(string name, Action action) //第2局 { for (int i = 0; i < 5; i++) { if (i == 1) continue; if (data.IsSelected(1, i)) continue; if (data.IsSelected(0, i, name)) continue; data.Selected(1, i, name); if (action == null) { data.Count++; Console.WriteLine("{0,5} {1}", data.Count, data.Current); } else { action(); } data.UnSelected(1, i); } }
構造更公道,邏輯更清楚,然則一堆lambda嵌套,太丑了,也不是我要的後果,我要的是相似如許的:
obj.Try("A").Try("B").Try("C").Try("D").Try2("A").Try2("B").Try2("C").Try2("D").Write();
四.持續向目的切近親近。
因為要延遲,所以必需先把要被挪用的辦法的援用“告知”上一級,當上一級履行for的時刻,就可以挪用下一級的辦法。因而我想到了一個“回調鏈”
所以,履行鏈式辦法是在結構回調鏈,最初的辦法再經由過程挪用鏈頭(Head)的某個辦法啟動真正要履行的全部邏輯。
延遲盤算是從Linq自創和進修來的,結構Linq的進程並沒有履行,比及了履行ToList, First等辦法時才真正去履行。
我想結構回調鏈每步都是一個固定的辦法,這裡隨意升引了T這個極短稱號,而每步前期盤算時要履行的辦法可靈巧指定。因而有了第3版:
static Seat data = new Seat(); //借用Seat保留數據 public Seat2(string name, Seat2 parent, Action<Seat2> method) { this.Name = name; this.Parent = parent; if (parent != null) parent.Child = this; this.Method = method; } public static void Run() { new Seat2("A", null, me => me.Try()) .T("B", me => me.Try()) .T("C", me => me.Try()) .T("D", me => me.Try()) .T("A", me => me.Try2()) .T("B", me => me.Try2()) .T("C", me => me.Try2()) .T("D", me => me.Try2()) .P().Start(); } public Seat2 T(string name, Action<Seat2> method) { return new Seat2(name, this, method); } public void Try() { for (int i = 0; i < 4; i++) { if (data.IsSelected(0, i)) continue; data.Selected(0, i, this.Name); if (this.Child != null) { this.Child.Method(this.Child); } data.UnSelected(0, i); } } public void Try2() { for (int i = 0; i < 5; i++) { if (i == 1) continue; if (data.IsSelected(1, i)) continue; if (data.IsSelected(0, i, this.Name)) continue; data.Selected(1, i, this.Name); if (this.Child != null) { this.Child.Method(this.Child); } data.UnSelected(1, i); } }
五.解耦
這類挪用方法,是滿足了。然則運算框架與詳細的算法耦合在一路,假如能把運算框架提掏出來,今後寫詳細的算法也便利很多。因而經由苦逼的提取,測試,踩坑,終究湧現了第4版:
//運算框架 class ComputeLink<T> where T : ISeat { ComputeLink<T> Parent { get; set; } //父節點,即上一級節點 ComputeLink<T> Child { get; set; } //子節點,即下一級節點 T Obj { get; set; } //以後節點對應的算法對象,可以看做營業對象 public ComputeLink(T obj, ComputeLink<T> parent, Action<T> method) { if (obj == null) throw new ArgumentNullException("obj"); this.Obj = obj; this.Obj.Method = x => method((T)x); if (parent != null) { this.Parent = parent; parent.Child = this; parent.Obj.Child = this.Obj; } } public static ComputeLink<T> New(T obj, Action<T> method) { return new ComputeLink<T>(obj, null, method); } public ComputeLink<T> Do(T obj, Action<T> method) { return new ComputeLink<T>(obj, this, method); } public ComputeLink<T> Head //鏈表的頭 { get { if (null != this.Parent) return this.Parent.Head; return this; } } public void Action() //啟動(延遲的)全部盤算 { var head = this.Head; head.Obj.Method(head.Obj); } } interface ISeat { ISeat Child { get; set; } Action<ISeat> Method { get; set; } }
p.s.為何第4版是ISeat3而不是ISeat4呢,由於我本不把第1版看成1個版本,由於太原始了,湧現第2版後,我就把第1版給刪除。為了寫這篇文章才從新去寫第1版。因而本來我看成第3版的ISeat3天然地排到了第4版。
詳細的"算法"就很簡略了:
class Seat3 : ISeat3 { static Seat data = new Seat(); string Name { get; set; } public Seat3(string name) { this.Name = name; } /// <summary> /// 解耦的版本 /// </summary> public static void Run() { var sql = ComputeLink<Seat3> .New(new Seat3("A"), m => m.Try()) .Do(new Seat3("B"), m => m.Try()) .Do(new Seat3("C"), m => m.Try()) .Do(new Seat3("D"), m => m.Try()) .Do(new Seat3("A"), m => m.Try2()) .Do(new Seat3("B"), m => m.Try2()) .Do(new Seat3("C"), m => m.Try2()) .Do(new Seat3("D"), m => m.Try2()) .Do(new Seat3(""), m => m.Print()); sql.Action(); } public Action<ISeat3> Method { get; set; } public ISeat3 Child { get; set; } public void Try() { for (int i = 0; i < 4; i++) { if (data.IsSelected(0, i)) continue; data.Selected(0, i, this.Name); if (this.Child != null) { this.Child.Method(this.Child); } data.UnSelected(0, i); } } public void Try2() { for (int i = 0; i < 5; i++) { if (i == 1) continue; if (data.IsSelected(1, i)) continue; if (data.IsSelected(0, i, this.Name)) continue; data.Selected(1, i, this.Name); if (this.Child != null) { this.Child.Method(this.Child); } data.UnSelected(1, i); } } public void Print() { data.Count++; Console.WriteLine("{0,5} {1}", data.Count, data.Current); } }
Seat3寫起來簡略,(Run辦法外部)看起來舒暢。經由過程鏈式寫法到達嵌套輪回的後果。對,這就是我要的!
它很像linq,所以我直接給變量定名為sql。
•關於Try和Try2來說,要挪用的辦法最好從參數傳來,然則如許就會增長Run辦法中New和Do的參數龐雜性,損壞了美感,所以經由衡量,Child和Method經由過程屬性傳入。這個我也不肯定如許做好欠好,請列位年夜俠指導。
•還有一個細節,就是ComputeLink結構辦法中的(行號12的)代碼 this.Obj.Method = x => method((T)x); 。我本來是如許寫的 this.Obj.Method = method; 編譯欠亨過,緣由是不克不及把 Action<ISeat3> 轉化為 Action<T> ,固然T必定完成了ISeat3,強迫轉化也不可,想起之前看過的一篇文章外面提到願望C#今後的版天性具有的一特征叫“協變”,極可能指的就是這個。既然這個 Action<ISeat3> 不克不及轉化為 Action<T> 然則ISeat3是可以強迫轉化為T的,所以我包了一層薄薄的殼,成了 this.Obj.Method = x => method((T)x); ,假如有更好的方法請告知我。
六.第2局為何是11種能夠
回過火來處理為何關於一個肯定的第1局,第2局有11種能夠。
無妨假定第1局的選擇是A選1號椅,B選2號椅,C選3號椅,D選4號椅。
第2局分為兩年夜類情形:
假如B選了第5號椅
則只要2種能夠:
A B C D .-D . A C B
A B C D .-C . D A B
假如B選了不是第5號椅,
則ACD都有能夠選第5號椅,有3種能夠。B有3種選的能夠(1,3,4號椅),B一旦肯定,A和C也只要一種能夠
所以11 = 2 + 3 * 3
七.結論
由一道數學題牽引出多層輪回嵌套,終究經由過程封裝到達了我要的鏈式挪用的後果,我是很滿足的。這也是我第一次設計延遲盤算,感到激烈。假如新的場景須要用到延遲盤算我想有了此次經歷寫起來會隨手很多。假如是須要多層for的算法題都可以比擬便利的完成了。
你都看到這裡了,為我點個贊吧,能說一下意見就更好了。
完全代碼:
using System; using System.Linq; using System.Diagnostics; namespace ConsoleApplication { class Seat { static Seat data = new Seat(); public static void Run() { //Seat.Run(); //return; for (int a = ; a < ; a++) { if (data.IsSelected(, a)) //第局編號。假如曾經被人坐了。 continue; data.Selected(, a, "A"); //第局編號。A坐a椅。 for (int b = ; b < ; b++) { if (data.IsSelected(, b)) continue; data.Selected(, b, "B"); for (int c = ; c < ; c++) { if (data.IsSelected(, c)) continue; data.Selected(, c, "C"); for (int d = ; d < ; d++) { if (data.IsSelected(, d)) continue; data.Selected(, d, "D"); for (int a = ; a < ; a++) { if (a == ) continue; if (data.IsSelected(, a)) //第局編號 continue; if (data.IsSelected(, a, "A")) //假如第局A坐了a椅 continue; data.Selected(, a, "A"); for (int b = ; b < ; b++) { if (b == ) continue; if (data.IsSelected(, b)) continue; if (data.IsSelected(, b, "B")) continue; data.Selected(, b, "B"); for (int c = ; c < ; c++) { if (c == ) continue; if (data.IsSelected(, c)) continue; if (data.IsSelected(, c, "C")) continue; data.Selected(, c, "C"); for (int d = ; d < ; d++) { if (d == ) continue; if (data.IsSelected(, d)) continue; if (data.IsSelected(, d, "D")) continue; data.Selected(, d, "D"); data.Count++; //能夠的情形數加 Console.WriteLine("{,} {}", data.Count, data.Current); data.UnSelected(, d); } data.UnSelected(, c); } data.UnSelected(, b); } data.UnSelected(, a); } data.UnSelected(, d); } data.UnSelected(, c); } data.UnSelected(, b); } data.UnSelected(, a); //A起身(釋放坐椅) } } public static void Run() { Try("A", () => Try("B", () => Try("C", () => Try("D", () => Try("A", () => Try("B", () => Try("C", () => Try("D", null ) ) ) ) ) ) ) ); } public static void Try(string name, Action action) { for (int i = ; i < ; i++) { if (data.IsSelected(, i)) continue; data.Selected(, i, name); if (action == null) { Console.WriteLine(data.Current); } else { action(); } data.UnSelected(, i); } } public static void Try(string name, Action action) { for (int i = ; i < ; i++) { if (i == ) continue; if (data.IsSelected(, i)) continue; if (data.IsSelected(, i, name)) continue; data.Selected(, i, name); if (action == null) { data.Count++; Console.WriteLine("{,} {}", data.Count, data.Current); } else { action(); } data.UnSelected(, i); } } public Seat() { seats[, ] = "."; seats[, ] = "."; } private string[,] seats = new string[, ]; public void UnSelected(int game, int i) { Debug.Assert(game == && i != || game == && i != ); Debug.Assert(seats[game, i] != null); seats[game, i] = null; } public void Selected(int game, int i, string name) { Debug.Assert(game == && i != || game == && i != ); Debug.Assert(seats[game, i] == null); seats[game, i] = name; } public bool IsSelected(int game, int a) { return seats[game, a] != null && seats[game, a] != "."; } public bool IsSelected(int game, int a, string name) { return seats[game, a] == name; } public string Current { get { return string.Format("{} {} {} {} {}-{} {} {} {} {}", seats[, ], seats[, ], seats[, ], seats[, ], seats[, ], seats[, ], seats[, ], seats[, ], seats[, ], seats[, ]); } } public int Count { get; set; } } class Seat { static Seat data = new Seat(); //借用Seat保留法的數據 Seat Parent { get; set; } Seat Child { get; set; } string Name { get; set; } Action<Seat> Method { get; set; } public Seat(string name, Seat parent, Action<Seat> method) { this.Name = name; this.Parent = parent; if (parent != null) parent.Child = this; this.Method = method; } /// <summary> /// 耦合的版本 /// </summary> public static void Run() { new Seat("A", null, me => me.Try()) .T("B", me => me.Try()) .T("C", me => me.Try()) .T("D", me => me.Try()) .T("A", me => me.Try()) .T("B", me => me.Try()) .T("C", me => me.Try()) .T("D", me => me.Try()) .P().Start(); } public Seat T(string name, Action<Seat> method) { return new Seat(name, this, method); } public Seat P() { return new Seat("Print", this, me => me.Print()); } public void Start() { var head = this.Head; head.Method(head); } public Seat Head { get { if (null != this.Parent) return this.Parent.Head; return this; } } public void Try() { for (int i = ; i < ; i++) { if (data.IsSelected(, i)) continue; data.Selected(, i, this.Name); if (this.Child != null) { this.Child.Method(this.Child); } data.UnSelected(, i); } } public void Try() { for (int i = ; i < ; i++) { if (i == ) continue; if (data.IsSelected(, i)) continue; if (data.IsSelected(, i, this.Name)) continue; data.Selected(, i, this.Name); if (this.Child != null) { this.Child.Method(this.Child); } data.UnSelected(, i); } } public void Print() { data.Count++; Console.WriteLine("{,} {}", data.Count, data.Current); } public override string ToString() { return this.Name.ToString(); } } class ComputeLink<T> where T : ISeat { ComputeLink<T> Parent { get; set; } //父節點,即上一級節點 ComputeLink<T> Child { get; set; } //子節點,即下一級節點 T Obj { get; set; } //以後節點對應的算法對象,可以看做營業對象 public ComputeLink(T obj, ComputeLink<T> parent, Action<T> method) { if (obj == null) throw new ArgumentNullException("obj"); this.Obj = obj; this.Obj.Method = x => method((T)x); if (parent != null) { this.Parent = parent; parent.Child = this; parent.Obj.Child = this.Obj; } } public static ComputeLink<T> New(T obj, Action<T> method) { return new ComputeLink<T>(obj, null, method); } public ComputeLink<T> Do(T obj, Action<T> method) { return new ComputeLink<T>(obj, this, method); } public ComputeLink<T> Head //鏈表的頭 { get { if (null != this.Parent) return this.Parent.Head; return this; } } public void Action() //啟動(延遲的)全部盤算 { var head = this.Head; head.Obj.Method(head.Obj); } } interface ISeat { ISeat Child { get; set; } Action<ISeat> Method { get; set; } } class Seat : ISeat { static Seat data = new Seat(); string Name { get; set; } public Seat(string name) { this.Name = name; } /// <summary> /// 解耦的版本 /// </summary> public static void Run() { var sql = ComputeLink<Seat> .New(new Seat("A"), m => m.Try()) .Do(new Seat("B"), m => m.Try()) .Do(new Seat("C"), m => m.Try()) .Do(new Seat("D"), m => m.Try()) .Do(new Seat("A"), m => m.Try()) .Do(new Seat("B"), m => m.Try()) .Do(new Seat("C"), m => m.Try()) .Do(new Seat("D"), m => m.Try()) .Do(new Seat(""), m => m.Print()); sql.Action(); } public Action<ISeat> Method { get; set; } public ISeat Child { get; set; } public void Try() { for (int i = ; i < ; i++) { if (data.IsSelected(, i)) continue; data.Selected(, i, this.Name); if (this.Child != null) { this.Child.Method(this.Child); } data.UnSelected(, i); } } public void Try() { for (int i = ; i < ; i++) { if (i == ) continue; if (data.IsSelected(, i)) continue; if (data.IsSelected(, i, this.Name)) continue; data.Selected(, i, this.Name); if (this.Child != null) { this.Child.Method(this.Child); } data.UnSelected(, i); } } public void Print() { data.Count++; Console.WriteLine("{,} {}", data.Count, data.Current); } public override string ToString() { return this.Name.ToString(); } } }
以上內容是小編給年夜家引見的C#用鏈式辦法表達輪回嵌套的相干常識,願望對年夜家有所贊助!