前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C#用链式方法表达循环嵌套1

C#用链式方法表达循环嵌套1

作者头像
逸鹏
发布2018-04-10 14:16:21
9750
发布2018-04-10 14:16:21
举报
文章被收录于专栏:逸鹏说道逸鹏说道

情节故事得有情节,不喜欢情节的朋友可看第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版:

代码语言:java
复制
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版的写法太死板太麻烦了。 如果能像这样写代码就爽了:

代码语言:javascript
复制
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版:

代码语言:java
复制
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嵌套,太丑了,也不是我要的效果,我要的是类似这样的:

代码语言:javascript
复制
obj.Try("A").Try("B").Try("C").Try("D").Try2("A").Try2("B").Try2("C").Try2("D").Write();

四.继续向目标逼近。 由于要延迟,所以必须先把要被调用的方法的引用“告诉”上一级,当上一级执行for的时候,就能调用下一级的方法。于是我想到了一个“回调链”

所以,执行链式方法是在构造回调链,最后的方法再通过调用链头(Head)的某个方法启动真正要执行的整个逻辑。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2016-06-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 我为Net狂 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档