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

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

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

延迟计算是从Linq借鉴和学习来的,构造Linq的过程并没有执行,等到了执行ToList, First等方法时才真正去执行。 我想构造回调链每一步都是一个固定的方法,这里随便起用了T这个极短名称,而每一步后期计算时要执行的方法可灵活指定。于是有了第3版:

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

代码语言:java
复制
//运算框架
class ComputeLink<T> where T : ISeat3
{
    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 ISeat3
{
    ISeat3 Child { get; set; }
    Action<ISeat3> Method { get; set; }
}

p.s.为什么第4版是ISeat3而不是ISeat4呢,因为我本不把第1版当作1个版本,因为太原始了,出现第2版后,我就把第1版给删除了。为了写这篇文章才重新去写第1版。于是原本我当作第3版的ISeat3自然地排到了第4版。

具体的"算法"就很简单了:

代码语言:java
复制
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的算法题都可以比较方便的实现了。

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

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

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

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

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