博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
C#用链式方法表达循环嵌套
阅读量:5095 次
发布时间:2019-06-13

本文共 25216 字,大约阅读时间需要 84 分钟。

情节故事得有情节,不喜欢情节的朋友可看第1版代码,然后直接跳至“三.想要链式写法”

一.起缘

故事缘于一位朋友的一道题:

朋友四人玩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
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
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版:

1 //运算框架 2 class ComputeLink
where T : ISeat3 3 { 4 ComputeLink
Parent { get; set; } //父节点,即上一级节点 5 ComputeLink
Child { get; set; } //子节点,即下一级节点 6 T Obj { get; set; } //当前节点对应的算法对象,可以看作业务对象 7 public ComputeLink(T obj, ComputeLink
parent, Action
method) 8 { 9 if (obj == null)10 throw new ArgumentNullException("obj");11 this.Obj = obj;12 this.Obj.Method = x => method((T)x);13 if (parent != null)14 {15 this.Parent = parent;16 parent.Child = this;17 parent.Obj.Child = this.Obj;18 }19 }20 public static ComputeLink
New(T obj, Action
method)21 {22 return new ComputeLink
(obj, null, method);23 }24 25 public ComputeLink
Do(T obj, Action
method)26 {27 return new ComputeLink
(obj, this, method);28 }29 public ComputeLink
Head //链表的头30 {31 get32 {33 if (null != this.Parent)34 return this.Parent.Head;35 return this;36 }37 }38 public void Action() //启动(延迟的)整个计算39 {40 var head = this.Head;41 head.Obj.Method(head.Obj);42 }43 }44 interface ISeat345 {46 ISeat3 Child { get; set; }47 Action
Method { get; set; }48 }

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;    }    ///     /// 解耦的版本    ///     public static void Run()    {        var sql = ComputeLink
.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
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的算法题都可以比较方便的实现了。

你都看到这里了,为我点个赞吧,能说一下看法就更好了。

完整代码:

1 using System;  2 using System.Linq;  3 using System.Diagnostics;  4   5 namespace ConsoleApplication1  6 {  7     class Seat  8     {  9         static Seat data = new Seat(); 10         public static void Run() 11         { 12             //Seat2.Run(); 13             //return; 14             for (int a = 0; a < 4; a++) 15             { 16                 if (data.IsSelected(0, a))  //第1局编号0。如果已经被人坐了。 17                     continue; 18                 data.Selected(0, a, "A");   //第1局编号0。A坐a椅。 19                 for (int b = 0; b < 4; b++) 20                 { 21                     if (data.IsSelected(0, b)) 22                         continue; 23                     data.Selected(0, b, "B"); 24                     for (int c = 0; c < 4; c++) 25                     { 26                         if (data.IsSelected(0, c)) 27                             continue; 28                         data.Selected(0, c, "C"); 29                         for (int d = 0; d < 4; d++) 30                         { 31                             if (data.IsSelected(0, d)) 32                                 continue; 33                             data.Selected(0, d, "D"); 34                             for (int a2 = 0; a2 < 5; a2++) 35                             { 36                                 if (a2 == 1) 37                                     continue; 38                                 if (data.IsSelected(1, a2))     //第2局编号1 39                                     continue; 40                                 if (data.IsSelected(0, a2, "A"))    //如果第1局A坐了a2椅 41                                     continue; 42                                 data.Selected(1, a2, "A"); 43                                 for (int b2 = 0; b2 < 5; b2++) 44                                 { 45                                     if (b2 == 1) 46                                         continue; 47                                     if (data.IsSelected(1, b2)) 48                                         continue; 49                                     if (data.IsSelected(0, b2, "B")) 50                                         continue; 51                                     data.Selected(1, b2, "B"); 52                                     for (int c2 = 0; c2 < 5; c2++) 53                                     { 54                                         if (c2 == 1) 55                                             continue; 56                                         if (data.IsSelected(1, c2)) 57                                             continue; 58                                         if (data.IsSelected(0, c2, "C")) 59                                             continue; 60                                         data.Selected(1, c2, "C"); 61                                         for (int d2 = 0; d2 < 5; d2++) 62                                         { 63                                             if (d2 == 1) 64                                                 continue; 65                                             if (data.IsSelected(1, d2)) 66                                                 continue; 67                                             if (data.IsSelected(0, d2, "D")) 68                                                 continue; 69                                             data.Selected(1, d2, "D"); 70  71                                             data.Count++;       //可能的情况数加1 72                                             Console.WriteLine("{0,5} {1}", data.Count, data.Current); 73  74                                             data.UnSelected(1, d2); 75                                         } 76                                         data.UnSelected(1, c2); 77                                     } 78                                     data.UnSelected(1, b2); 79                                 } 80                                 data.UnSelected(1, a2); 81                             } 82                             data.UnSelected(0, d); 83                         } 84                         data.UnSelected(0, c); 85                     } 86                     data.UnSelected(0, b); 87                 } 88                 data.UnSelected(0, a);  //A起身(释放坐椅) 89             } 90         } 91         public static void Run2() 92         { 93             Try("A", 94                 () => Try("B", 95                     () => Try("C", 96                         () => Try("D", 97                             () => Try2("A", 98                                 () => Try2("B", 99                                     () => Try2("C",100                                         () => Try2("D",101                                             null102                                         )103                                     )104                                 )105                             )106                         )107                     )108                 )109             );110         }111         public static void Try(string name, Action action)112         {113             for (int i = 0; i < 4; i++)114             {115                 if (data.IsSelected(0, i))116                     continue;117                 data.Selected(0, i, name);118                 if (action == null)119                 {120                     Console.WriteLine(data.Current);121                 }122                 else123                 {124                     action();125                 }126                 data.UnSelected(0, i);127             }128         }129 130         public static void Try2(string name, Action action)131         {132             for (int i = 0; i < 5; i++)133             {134                 if (i == 1)135                     continue;136                 if (data.IsSelected(1, i))137                     continue;138                 if (data.IsSelected(0, i, name))139                     continue;140                 data.Selected(1, i, name);141                 if (action == null)142                 {143                     data.Count++;144                     Console.WriteLine("{0,5} {1}", data.Count, data.Current);145                 }146                 else147                 {148                     action();149                 }150                 data.UnSelected(1, i);151             }152         }153         public Seat()154         {155             seats[0, 4] = ".";156             seats[1, 1] = ".";157         }158         private string[,] seats = new string[2, 5];159         public void UnSelected(int game, int i)160         {161             Debug.Assert(game == 0 && i != 4 || game == 1 && i != 1);162             Debug.Assert(seats[game, i] != null);163             seats[game, i] = null;164         }165         public void Selected(int game, int i, string name)166         {167             Debug.Assert(game == 0 && i != 4 || game == 1 && i != 1);168             Debug.Assert(seats[game, i] == null);169             seats[game, i] = name;170         }171         public bool IsSelected(int game, int a)172         {173             return seats[game, a] != null && seats[game, a] != ".";174         }175         public bool IsSelected(int game, int a, string name)176         {177             return seats[game, a] == name;178         }179         public string Current180         {181             get182             {183                 return string.Format("{0} {1} {2} {3} {4}-{5} {6} {7} {8} {9}",184                     seats[0, 0], seats[0, 1], seats[0, 2], seats[0, 3], seats[0, 4],185                     seats[1, 0], seats[1, 1], seats[1, 2], seats[1, 3], seats[1, 4]);186             }187         }188 189         public int Count { get; set; }190     }191 192     class Seat2193     {194         static Seat data = new Seat();      //借用Seat保存法的数据195         Seat2 Parent { get; set; }196         Seat2 Child { get; set; }197         string Name { get; set; }198         Action
Method { get; set; }199 public Seat2(string name, Seat2 parent, Action
method)200 {201 this.Name = name;202 this.Parent = parent;203 if (parent != null)204 parent.Child = this;205 this.Method = method;206 }207 ///
208 /// 耦合的版本209 /// 210 public static void Run()211 {212 new Seat2("A", null, me => me.Try())213 .T("B", me => me.Try())214 .T("C", me => me.Try())215 .T("D", me => me.Try())216 .T("A", me => me.Try2())217 .T("B", me => me.Try2())218 .T("C", me => me.Try2())219 .T("D", me => me.Try2())220 .P().Start();221 }222 public Seat2 T(string name, Action
method)223 {224 return new Seat2(name, this, method);225 }226 public Seat2 P()227 {228 return new Seat2("Print", this, me => me.Print());229 }230 public void Start()231 {232 var head = this.Head;233 head.Method(head);234 }235 public Seat2 Head236 {237 get238 {239 if (null != this.Parent)240 return this.Parent.Head;241 return this;242 }243 }244 public void Try()245 {246 for (int i = 0; i < 4; i++)247 {248 if (data.IsSelected(0, i))249 continue;250 data.Selected(0, i, this.Name);251 if (this.Child != null)252 {253 this.Child.Method(this.Child);254 }255 data.UnSelected(0, i);256 }257 }258 public void Try2()259 {260 for (int i = 0; i < 5; i++)261 {262 if (i == 1)263 continue;264 if (data.IsSelected(1, i))265 continue;266 if (data.IsSelected(0, i, this.Name))267 continue;268 data.Selected(1, i, this.Name);269 if (this.Child != null)270 {271 this.Child.Method(this.Child);272 }273 data.UnSelected(1, i);274 }275 }276 public void Print()277 {278 data.Count++;279 Console.WriteLine("{0,5} {1}", data.Count, data.Current);280 }281 282 public override string ToString()283 {284 return this.Name.ToString();285 }286 }287 288 class ComputeLink
where T : ISeat3289 {290 ComputeLink
Parent { get; set; } //父节点,即上一级节点291 ComputeLink
Child { get; set; } //子节点,即下一级节点292 T Obj { get; set; } //当前节点对应的算法对象,可以看作业务对象293 public ComputeLink(T obj, ComputeLink
parent, Action
method)294 {295 if (obj == null)296 throw new ArgumentNullException("obj");297 this.Obj = obj;298 this.Obj.Method = x => method((T)x);299 if (parent != null)300 {301 this.Parent = parent;302 parent.Child = this;303 parent.Obj.Child = this.Obj;304 }305 }306 public static ComputeLink
New(T obj, Action
method)307 {308 return new ComputeLink
(obj, null, method);309 }310 311 public ComputeLink
Do(T obj, Action
method)312 {313 return new ComputeLink
(obj, this, method);314 }315 public ComputeLink
Head //链表的头316 {317 get318 {319 if (null != this.Parent)320 return this.Parent.Head;321 return this;322 }323 }324 public void Action() //启动(延迟的)整个计算325 {326 var head = this.Head;327 head.Obj.Method(head.Obj);328 }329 }330 interface ISeat3331 {332 ISeat3 Child { get; set; }333 Action
Method { get; set; }334 }335 class Seat3 : ISeat3336 {337 static Seat data = new Seat();338 string Name { get; set; }339 public Seat3(string name)340 {341 this.Name = name;342 }343 ///
344 /// 解耦的版本345 /// 346 public static void Run()347 {348 var sql = ComputeLink
349 .New(new Seat3("A"), m => m.Try())350 .Do(new Seat3("B"), m => m.Try())351 .Do(new Seat3("C"), m => m.Try())352 .Do(new Seat3("D"), m => m.Try())353 .Do(new Seat3("A"), m => m.Try2())354 .Do(new Seat3("B"), m => m.Try2())355 .Do(new Seat3("C"), m => m.Try2())356 .Do(new Seat3("D"), m => m.Try2())357 .Do(new Seat3(""), m => m.Print());358 sql.Action();359 }360 public Action
Method { get; set; }361 public ISeat3 Child { get; set; }362 public void Try()363 {364 for (int i = 0; i < 4; i++)365 {366 if (data.IsSelected(0, i))367 continue;368 data.Selected(0, i, this.Name);369 if (this.Child != null)370 {371 this.Child.Method(this.Child);372 }373 data.UnSelected(0, i);374 }375 }376 public void Try2()377 {378 for (int i = 0; i < 5; i++)379 {380 if (i == 1)381 continue;382 if (data.IsSelected(1, i))383 continue;384 if (data.IsSelected(0, i, this.Name))385 continue;386 data.Selected(1, i, this.Name);387 if (this.Child != null)388 {389 this.Child.Method(this.Child);390 }391 data.UnSelected(1, i);392 }393 }394 public void Print()395 {396 data.Count++;397 Console.WriteLine("{0,5} {1}", data.Count, data.Current);398 }399 400 public override string ToString()401 {402 return this.Name.ToString();403 }404 }405 }
View Code

 

转载于:https://www.cnblogs.com/BillySir/p/5542104.html

你可能感兴趣的文章
串口接线
查看>>
python_paramiko模块
查看>>
C#一个FTP操作封装类FTPHelper
查看>>
Linux运维基础入门(二):网络基础知识梳理02
查看>>
你所不知道的 CSS 阴影技巧与细节
查看>>
MyBatis框架的使用及源码分析(三) 配置篇 Configuration
查看>>
20172319 实验三《查找与排序》实验报告
查看>>
构造函数的继承
查看>>
Nginx的虚拟主机配置
查看>>
overflow 属性
查看>>
Mychael原创题 洛谷T23923 Mychaelの水题 【题解】
查看>>
Objective-C 协议(protocol)
查看>>
Android自定义进度条
查看>>
java虚拟机深入理解Java虚拟机:JVM高级特性与最佳实践(第2版)
查看>>
struts返回对象json格式数据
查看>>
[[UIScreen mainScreen] bounds] 返回的屏幕尺寸不对
查看>>
python基础_格式化输出(%用法和format用法)
查看>>
前端面试题之手写事件模型及事件代理/委托
查看>>
python 自动回收机制
查看>>
11月17日站立会议内容
查看>>