国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 學(xué)院 > 開(kāi)發(fā)設(shè)計(jì) > 正文

面向組合子設(shè)計(jì)Coder

2019-11-17 02:30:16
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

面向組合子設(shè)計(jì)Coder

面向組合子

面向組合子(Combanitor-Oriented),是最近幫我打開(kāi)新世界大門(mén)的一種pattern。緣起haskell,又見(jiàn)monad與ParseC,終于ajoo前輩的幾篇文章。自去年9月起正式回歸C#以來(lái),我又逐漸接受了不少新的paradigm(雖然主要原因還是在學(xué)校用C#的方法太山寨),其中對(duì)我影響比較深刻的就是codegen。此codegen非compiler中的codegen,可能更像是meta-PRogramming中的codegen。抽象來(lái)說(shuō),就是作為一個(gè)嵌入于構(gòu)建流程中的某一步驟,拿到一些元描述信息,來(lái)生成代碼。我目前所接觸到的codegen的具體應(yīng)用情景,有這樣幾種:1.RPC相關(guān)的,數(shù)據(jù)打解包邏輯、Stub/Skeleton、組播等2.配表轉(zhuǎn)代碼3.策劃配出來(lái)的可視化行為樹(shù)轉(zhuǎn)代碼從這些情景可以看出這種需求的典型特征:性能好、便于上層調(diào)用。

具體來(lái)說(shuō),我們還是拿這種形式跟一些比較傳統(tǒng)的形式做下對(duì)比:RPC打解包邏輯直接自動(dòng)走函數(shù) V.S. protobufcodegen成C#代碼的行為樹(shù) V.S. 硬解腳本C#結(jié)構(gòu)描述的配置 V.S. 一坨meta二進(jìn)制+一坨data二進(jìn)制又是一堆廢話,現(xiàn)在直接進(jìn)入主題。

正文

首先定義一個(gè)概念,Coder,當(dāng)然這跟平時(shí)一些低端討論串上經(jīng)常引起的Coder還是Programmer中的Coder沒(méi)關(guān)系,這里我們把它理解為一個(gè)函數(shù),接收一個(gè)T描述結(jié)構(gòu)作為參數(shù),輸出一個(gè)字符串。為了更C#一點(diǎn),我們這樣定義Coder:

    public interface ICoder<in T>    {        string Code(T meta);    }

這是所有Coder的基本表現(xiàn)形式,與之對(duì)應(yīng)的,任何復(fù)雜的代碼生成程序,其實(shí)本質(zhì)都是通過(guò)一個(gè)抽象數(shù)據(jù)結(jié)構(gòu)生成一個(gè)字符串。基于ICoder,我們先從最簡(jiǎn)單的組合子開(kāi)始構(gòu)造,也就是"0"和"1":

    internal class UnitCoder<T> : ICoder<T>    {        readonly string output;        public UnitCoder(string output)        {            this.output = output;        }        public override string Code(T meta)        {            return output;        }    }        internal class ZeroCoder<T> : ICoder<T>    {        private static ZeroCoder<T> instance;        public static ZeroCoder<T> Instance        {            get { return instance ?? (instance = new ZeroCoder<T>()); }        }        public override string Code(T meta)        {            return "";        }    }

UnitCoder:不論給什么作為輸入,都只返回一個(gè)固定的字符串ZeroCoder:不論給什么作為輸入,都返回空字符串只有這兩個(gè)的話,似乎還是什么都不能做,我們需要一個(gè)最基本的可以讓我們定制的Coder:

    internal class BasicCoder<T> : ICoder<T>    {        private readonly Func<T, string> func;        public BasicCoder(Func<T, string> func)        {            this.func = func;        }        public override string Code(T meta)        {            return func(meta);        }    }

假設(shè)現(xiàn)在有一個(gè)結(jié)構(gòu)定義:

        class Meta1        {            public string Type;            public string Name;            public string Value;        }

如此構(gòu)造一個(gè)BasicCoder:

var basicCoder = Generator.GenBasic((Meta1 m) => string.Format(@"{0} {1} = {2}", m.Type, m.Name, m.Value));

這樣,通過(guò)給basicCoder傳不同的、具體的Meta1實(shí)例,這個(gè)Coder就跟真的Coder一樣coding出了不一樣的代碼。僅有這三個(gè)還不夠,我們還需要想一種辦法將兩個(gè)Coder組合起來(lái)。說(shuō)實(shí)話,這一塊代碼我寫(xiě)得非常丑,整理成博客的原因也是希望有哪位前輩看到能指點(diǎn)一下。好了,直接上有很明顯bad smell的代碼。首先需要對(duì)最基本的ICoder結(jié)構(gòu)進(jìn)行改造:

    public interface ICoder    {        string Code(object meta);    }        public interface ICoder<in T> : ICoder      {        string Code(T meta);    }

這樣ICoder來(lái)提供通用的Coder接口,方便后面的SequenceCoder。所有的Coder都復(fù)用一下這樣的邏輯:

    internal abstract class CoderBase<T> : ICoder<T>    {        private readonly T instance;        public abstract string Code(T meta);        public string Code(object meta)        {            if (meta is T)            {                return Code((T)meta);            }            throw new Exception("...");        }    }

然后我們著手實(shí)現(xiàn)SequenceCoder:

    internal class SequenceCoder<T> : CoderBase<T>    {        readonly ICoder[] coderArr;        readonly Func<T, ICoder[], string> coderJoiner;        public SequenceCoder(ICoder[] coderArr, Func<T, ICoder[], string> coderJoiner)        {            this.coderArr = coderArr;            this.coderJoiner = coderJoiner;        }        public override string Code(T meta)        {            return coderJoiner(meta, coderArr);        }    }

我對(duì)SequenceCoder的定位是,Coder組合子系統(tǒng)內(nèi)部的一個(gè)結(jié)合不同Coder的基礎(chǔ)組件。有了SequenceCoder,我們就可以多出來(lái)很多有意義的東西了。之前我們構(gòu)造的basicCoder,是沒(méi)打出來(lái)語(yǔ)句末尾的";"的,我們來(lái)構(gòu)造一下。先是前后綴的一些公共邏輯:

        internal static ICoder<T> WithPostfix<T>(this ICoder<T> coder, string postfix)        {            var coderPostfix = new UnitCoder<T>(postfix);            return new SequenceCoder<T>(new ICoder[] { coder, coderPostfix }, (meta, arr) => string.Join("", coder.Code(meta), coderPostfix.Code(meta)));        }        internal static ICoder<T> WithPrefix<T>(this ICoder<T> coder, string prefix) where        {            var coderPrefix = new UnitCoder<T>(prefix);            return new SequenceCoder<T>(new ICoder[] { coderPrefix, coder }, (meta, arr) => string.Join("", coderPrefix.Code(meta), coder.Code(meta)));        }

然后是statementCoder:

var statementCoder = basicCoder.WithPostfix(";");

還可以被大括號(hào)包裹:

        public static ICoder<T> Brace<T>(this ICoder<T> coder)        {            return coder.WithPostfix("}").WithPrefix("{");        }

var braceStatementCoder = statementCoder.Brace();

可以實(shí)現(xiàn)重復(fù),也就是將一個(gè)ICoder<T>轉(zhuǎn)為一個(gè)ICoder<IEnumerable<T>>:

    internal class RepeatedCoder<T> : CoderBase<IEnumerable<T>>    {        private readonly ICoder coder;        private readonly string seperator;        private readonly Func<T, bool> predicate;        public RepeatedCoder(ICoder<T> coder, string seperator, Func<T, bool> predicate)        {            this.coder = coder;            this.seperator = seperator;            this.predicate = predicate;        }        public override string Code(IEnumerable<T> meta)        {            bool first = true;            return meta.Where(m=>predicate(m)).Select(m => coder.Code(m)).Aggregate("", (val, cur) =>            {                if (first)                {                    first = false;                    return val + cur;                }                return val + seperator + cur;            });        }    }

為了自己寫(xiě)代碼方便,直接把seperator和predicate邏輯硬塞進(jìn)去了,各位看官見(jiàn)諒。構(gòu)造一個(gè)重復(fù)Coder:

        public static ICoder<IEnumerable<T>> Many<T>(this ICoder<T> coder, string seperator) where T : class        {            return Generator.GenRepeated(coder, seperator);        }

var repeatedCoder = basicCoder.WithPostfix(";").Many("/n");

這樣,給repeatedCoder一個(gè)Meta1的數(shù)組,他就會(huì)像一只coder一樣自動(dòng)把每個(gè)元素轉(zhuǎn)成一行代碼。有了這些還不夠,我們還是回歸需求本身。假設(shè)有這樣一個(gè)Coder :: ICoder<A>,這個(gè)Coder需要根據(jù)A的某個(gè)字段比如name寫(xiě)出來(lái)一個(gè) class name,需要根據(jù)另外一個(gè)比如IEnumerable<B>類型的字段寫(xiě)出一系列field的定義。我們期望生成的代碼形式:

class XXX{        public t1 aaa = v1;    public t2 bbb = v2;}

假設(shè)A的結(jié)構(gòu)定義是這樣的:

class A{    public string Name;    public IEnumerable<Meta1> Fields;}

其實(shí)這種需求也是我做出之前那種壞味代碼的原因,還是那句話,求高人指點(diǎn)!繼續(xù)上代碼,CombineCoder:

        public static ICoder<T> GenCombine<T, T1>(ICoder<T> tCoder, ICoder<T1> t1Coder, Func<T, T1> selector)        {            return new SequenceCoder<T>(new ICoder[] { tCoder, t1Coder },                (meta, arr) =>                    string.Format("{0}{1}", tCoder.Code(meta), t1Coder.Code(selector(meta))));        }

復(fù)用我們之前構(gòu)造的repeatedCoder

var coder1 = Generator.GenBasic((A a) => string.Format("class {0}", a.Name)).WithPostfix("/n");var coder2 = repeatedCoder.Brace();

現(xiàn)在我們希望一個(gè)A->string的coder1與一個(gè)IEnumerable<Meta1>->string的coder2 combine起來(lái),組合成一個(gè)A->string的classCoder,這樣做:

var classCoder = Generator.GenCombine(coder1, coder2, a => a.Fields);

好了大功告成,給classCoder一個(gè)A類型的元數(shù)據(jù)實(shí)例,就能輸出我們期望的字符串。

寫(xiě)在最后

這篇博文的主體內(nèi)容其實(shí)也差不多告一段落了。誠(chéng)然,以上貼出的代碼不論是性能還是擴(kuò)展性都存在很大的問(wèn)題,但是前者對(duì)于一個(gè)codegen程序來(lái)說(shuō)并不是關(guān)鍵考慮因素;而后者,正如之前所說(shuō),代碼的壞味還是存在不

發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 佛坪县| 嵩明县| 西贡区| 涿鹿县| 桃江县| 金山区| 武义县| 阳春市| 永济市| 枣庄市| 茶陵县| 金湖县| 孟津县| 兴和县| 财经| 时尚| 社旗县| 广平县| 宣威市| 勃利县| 榆林市| 阿勒泰市| 深水埗区| 武川县| 荥经县| 河源市| 库尔勒市| 胶南市| 惠州市| 祁门县| 乐陵市| 象州县| 荣昌县| 古田县| 浦东新区| 营山县| 大城县| 铜鼓县| 惠水县| 曲麻莱县| 河南省|