ShoneSharp语言(S#)的设计和使用介绍系列(11)—“类”披炫服靓妆化成“表”

时间:2023-03-08 16:43:51

ShoneSharp语言(S#)的设计和使用介绍

系列(11)—“类”披炫服靓妆化成“表”

作者:Shone

声明:原创文章欢迎转载,但请注明出处,https://www.cnblogs.com/ShoneSharp。

摘要: 匿名类指类定义体(即代码块)本身,使得类也成为所谓的“一等公民”,类也可以像变量一样进行赋值定义、传递和使用。S#语言的数据类就是程序代码,数据表是该代码运行时对应的数据值,为此本文介绍了数据表、类定义和类实例化的各种使用方法,让你体验S#强大的数据和信息表达能力。

软件: S#语言编辑解析运行器13.6.31,运行环境.NET 4.0,绿色软件,单个EXE直接运行,无副作用。网盘下载链接为https://pan.baidu.com/s/1nv1hmJn

有了前述S#公式、函数以及语句的铺垫,终于进入到本系列的高潮部分介绍———S#类,为配合本文例子,请使用最新软件13.6.31版运行本文示例。

一、引言

世界很复杂,世界也很简单。站在不同角度,收获视图也不一样。前面第四章第二节已经清晰地展示S#符号解析运行视图,提到了:

S#语言开始处理的是数据,最终获得的也是数据,从而形成周而复始的闭环,最终用于实现“数据 → 信息 → 智慧”的完美进化。

S#语言的表达其实也是数据加工成信息的过程,即从没有意义的数据,变成有指定意义的数据(信息)。那么怎么表达呢?

S#第一定律:万物皆对象,对象皆引用。

S#把所有数据值都看成是一个.NET Object对象,可以表达任意数据类型的数据值,而且所有数据都是.NET引用,默认情况下后面的操作不应修改引用数据的内容,而会产生新的应用,保证数据表达或加工过程不产生副作用。

S#第二定律:程序就是树,代码即节点。

S#把程序代码本身也看成是一棵语法数据树,每个树节点对应不同的语法结构代码,可以包含运行过程生成的数据值。因此可以把树节点看成是其数据值的包装,而且树节点本身也是一种数据值,这样就会统一了程序与数据之间的关系,达到“程序即数据,数据即程序”的无缝融合。

S#第三定律:程序以类聚,数据以表分。

第二定律指出程序也是数据,而程序与数据的关系是动态映射的。在程序运行过程中程序的一个数据节点通常保持不变,但其包含的数据值却是根据上下文动态变化的,即不同实例的数据值。因此相同功能的程序代码通常写成一个数据类(简称类),对一个类进行实例化后得到的是一个数据表(简称表)。再次强调与其他语言不同,S#类是程序代码,S#表是该代码运行时对应的数据值,这就是所谓的“类”披炫服靓妆化成“表”。

S#第四定律:无名成列表,有名数据表。

用道德经的名言“无名天地之始,有名万物之母”来描述S#对数据的理解再贴合不过了。上帝创世时的世间万物还都是无名,但取名这事,还是归功于人类的聪明才智,为此人类创造了语言。同样S#语言表达一系列无名的数据集合时采用列表,而表达一系列有名的数据集合时采用的是数据表。可见命名是S#语言表达数据的关键方式,也是把数据加工成信息的必经之路。

上面就是S#语言的圣经,如果您对编程语言领域(PL,Programming Language,网上引起很多争议的王垠就是这方面的专家)有比较深入的研究,本人上述的宏观论断或许会引发您的深思或顿悟。不过不懂没关系,懵懂也很好,下面我就会逐步讲解。

二、数据表

一个无名的数据值是这样子(数值):

10

一系列无名的同类数据值是这样子(数组):

[10, 20, 30]

一系列无名的各类数据值是这样子(列表):

{'box', 10, 20, 30, true}

一系列有名的各类数据值是这样子(数据表):

(1)标准写法

{Name='box',  L=,  W=,  H=,  Visible=true}

这种表达让我们看到了盒子及其长宽高等更多的数据含义,促使原始无意义的数据加工成了有意义的信息。这就是数据表的标准写法,可用于表达包含一系列键值数据对的集合,注意与其他语言不同,数据表带有局部变量堆栈,其键就是堆栈中的变量名称,而值就是变量的数据值。

如果每个键值都是数组,那么是不是与数据库的列对应,数据表也就相当于一张数据库中的表。关系数据库的理论已经证明表的关系演算与Lambada演算的表达能力是等价的,可见数据表有多么重要了。

{

Name=['box1', 'box2', 'box3'] ,

L=[, , ] ,

W=[,  ] ,

H=[, , ] ,

Visible=[true, false, true]

}

正因为数据表是如此地重要,S#语言支持很多种数据表的扩展写法:

(2)JSON写法

熟悉Javascript的同学有福了,把数据表标准写法的键名写成字符串,中间符号换成冒号,就成为JSON写法。例如:

{"Name":'box',  "L":,  "W":,  "H":,  "Visible":true}

现在JSON数据在网络上大行其道,而S#语言可以说就是JSON的超集,可以直接读取JSON,处理JSON数据简直太简单、方便和残暴了。

(3)脚本写法

熟悉脚本文件的同学有福了,把数据表标准写法去掉逗号,花括号换成括号即可简化成脚本写法。例如:

(Name='box'  L=  W=  H=  Visible=true)

这种写法没有,分隔符,由于S#把换行也看成空白,因此可以很容易直接导入外部常见的各种脚本或配置文本文件,就是加上()直接解析运行即可。这种脚本数据应用也很广泛,S#语言处理起来也非常简单、方便和残暴。

(4)YAML写法

熟悉YAML和Python的同学有福了,把数据表标准写法去掉逗号和花括号,中间符号换成冒号,加上===可扩展成YAML写法,例如:

===

Name: 'box'

Size:

L: 

W: 

H: 

Visible: true

===

//等价于标准写法

{

    Name = 'box'  ,

   Size = { L =  , W =  , H =  }  ,

   Visible = True

}

这种写法没有,分隔符,换行缩进自动处理成不同层级的嵌套,可以省略掉好多逗号和花括号,因此表达层级数据非常整齐和简洁,这点与Python处理数据有点异曲同工之妙。S#语言处理YAML也比较简单、方便。

除此之外数据表还有很多从类实例化的写法,本文后面也会专门介绍。简单的数据表也可以看作数据库中表的一条记录,如果有很多条类似这样的记录如下:

{Name='box1',  L=10,  W=20,  H=30,  Visible=true}

{Name='box2',  L=11,  W=21,  H=31,  Visible=false}

那么说明他们都是同一类型的数据,也就是有共同的模板。这时我们可以使用数据类对他们所属的分类或模板进行抽象表达——数据类(简称类),而实际的数据记录则可以按照该类进行实例化修改成对应的数据表。无名的数据类就是匿名类,有名的数据类通常通过赋值定义或显示定义。

三、匿名类

匿名类(也叫表模板、元表或自定义数据类)指数据类定义体(即代码块)本身,可以当作变量进行传递和使用,使得类成为所谓的“一等公民”。实现方式上也是通过指向类入口的指针、地址或引用传递,在程序后面调用匿名类时则把当前程序跳转到匿名类体上执行,当然每次执行还要处理同名称变量数据值的传递和堆栈进出。

(1)匿名类公式

class 缺省数据表

数据表前加class关键字就可以定义一个匿名类,可用于变量赋值。例如:

class {Name='box',  L=,  W=,  H=,  Visible=true}

(2)继承匿名类公式

class : 基类名称 缺省数据表

如果类是从指定的基类派生而来,那么class关键字后面指定一个基类名称即可,实例化的时候是先实例化基类数据再实例化当前类的数据。例如:

class : A {Name='box',  L=,  W=,  H=,  Visible=true}

S#类通常是有名字的,上述匿名类的表达方式比较简单便捷,通常用于公式中使用,复杂的请使用下面的类定义。

四、类定义

由于类是一等公民了,因此类通常也可以像变量一样进行赋值定义。

S#有两种类定义形式,但是其类实例化方法是一样的。

(1)隐式类定义

变量名称=匿名类

这种写法最为灵活多变,可使用在任何公式或语句中。因为匿名类被看作普通数据,可以通过命名变量进行传递和使用,无论是传给其他函数作为实参调用,或是作为其他函数的返回值,都没有任何违和感觉。例如:

{

f = class {a=, b=}  ,

g = f{a=}  ,                 //直接调用,g结果{a=15,b=20}

h = { a = f , b = a{b=} }    //变量传递后间接调用,b结果{a=10 b=30}

}

隐式类定义可以使用在任何变量赋值公式中,而且类变量的作用域还可以用专门语法进行修改。

(2)显式类定义语句(注:只能使用在语句中)

class 类名称 缺省数据表;

class关键字显示定义一个类名,进行类变量赋值。例如:

class Test {Name='box',  L=,  W=,  H=,  Visible=true}

class 类名称 语句块

与上面类似,只不过类定义体是语句块,支持更复杂代码逻辑,这时和C#的类定义代码很像。例如:

class Test

{

var L=;

var W=;

func GetArea()

{

Return L*W;

}

}

class 类名称 : 基类名称 缺省数据表;

class关键字显示定义一个类名,进行派生类变量赋值。例如:

class Test : A {Name='box',  L=,  W=,  H=,  Visible=true}

class 类名称 : 基类名称 语句块

同样类定义体也改成语句块,可支持更复杂代码逻辑,这时和C#的派生类定义代码很像。例如:

class Test : A

{

var L=;

var W=;

func GetArea()

{

Return L*W;

}

}

五、类实例化

一个类其实就是一段代码,只要进行实例化才能转化成为数据表进行使用,这与函数必须进行调用后才能转化为数据值进行使用相类似。

假设前面上下文已定义了类型A如下:

class A { L=10,  W=20,  Area=L*W }

那么可以使用以下四种S#的类实例化方式,可以用于任何公式和语句中。

(1)无参数的类实例化公式

类名称 {}

直接获得类代码的运行结果即数据表,注意类实例化后时还会把类名添加到数据表中。例如:

{

    Rect = class {L = , W = , Area = L * W}   ,

    b = Rect{}     // b结果为 { Class = 'Rect', L = 10 , W = 20 , Area = 200 }

}

(2)带参数的类实例化公式

类名称 { 参数赋值系列 }

传入参数数据值并赋值给类代码中的同名变量,再运行类代码获得的结果数据表。注意如果传入参数没有找到同名变量,那么也会添加到结果数据表中,注意类实例化后时还会把类名添加到数据表中。例如:

{

    Rect = class {L = , W = , Area = L * W}   ,

    b = Rect{L=, X=}      // b结果为 { Class = 'Rect', L = 4 , W = 20 , Area = 80, X=50 }

}

(3)带子节点的类实例化公式

类名称 { 类实例化公式系列 }

运行类代码并把子节点作为Children变量附加到的结果数据表,注意类实例化后时还会把类名添加到数据表中。例如:

{

    Shape = class {Name = ""}   ,        //定义基类

    Line = class : Shape {L = }   ,    //定义线类

    Rect = class : Shape {L = , W = , Area = L * W}   , //定义矩形类

    b = Shape         //实例化Shape类(带子节点)

    {

        Line{ Name = "l", L =  }

        Rect{ Name = "r", L =  }

    }

}

//b的计算结果为:

{

    Class = 'Shape',

    Name = ''  ,

    Children = {

        { Class = 'Line', Name = 'l' , L =  }  ,

        {

            Class = 'Rect',

            Name = 'r'  ,

            L =   ,

            W =   ,

            Area = 

        }

    }

}

(4)带参数及子节点的类实例化公式

类名称 { 参数赋值系列 } { 类实例化公式系列 }

传入参数数据值给类代码中的同名变量,再运行类代码并把子节点作为Children变量附加到的结果数据表。注意如果传入参数没有找到同名变量,那么也会添加到结果数据表中,另外类实例化后时还会把类名添加到数据表中。例如:

{

    Shape = class {Name = ""}   ,        //定义基类

    Line = class : Shape {L = }   ,    //定义线类

    Rect = class : Shape {L = , W = , Area = L * W}   , //定义矩形类

    b = Shape{ Name="l" Extend="扩展数据"}      //实例化Shape类(带参数和子节点)

    {

        Line{ Name = "l", L =  }

        Rect{ Name = "r", L =  }

    }

}

//b的计算结果为:

{

    Class = 'Shape'  ,

    Name = 'l'  ,

    Extend = '扩展数据'  ,

    Children = {

        {

            Class = 'Line'  ,

            Name = 'l'  ,

            L = 

        }  ,

        {

            Class = 'Rect'  ,

            Name = 'r'  ,

            L =   ,

            W =   ,

            Area = 

        }

    }

}

六、XML类实例化

S#还支持类似于XML的类实例化方式,与标准方式一一对应且功能等价,也可以用于任何公式和语句中。

(1)无参数的XML类实例化公式

<类名称/>

与XML相同,例如:

<Rect/>

(2)带参数的XML类实例化公式

<类名称 参数赋值系列/>

与XML相同,赋值序列中间用空白而不是逗号分隔,例如:

<Rect L=4  X=50/>

(3)带子节点的XML类实例化公式

与XML相似,但子节点的写法不同,这样便于扩展,例如:

<类名称/> { 类实例化公式系列 }

<Shape/>

{

   <Line Name = "l"  L = 5 />

   <Rect Name = "r"  L = 5 />

}

(4)带参数及子节点的XML类实例化公式

<类名称 参数赋值系列 /> { 类实例化公式系列 }

与XML相似,但子节点的写法不同,这样便于扩展,例如:

<Shape Name="l" Extend="扩展数据"/>

{

   <Line Name = "l"  L = 5 />

   <Rect Name = "r"  L = 5 />

}

七、类定义及实例化综合示例

下面给出一个有继承关系的类的语句级别,其实与C#类似,只不过S#更加关注于数据表达层面,不用进行序列化和反序列就能把数据全部计算并表达出来,用于动态扩展各类数据和信息是非常方便的。

{

    class Drawing {

        var Layer = '';

        var Color = 'ByLayer';

        var LineType = 'ByLayer';

        var LineWidth = 'ByLayer';

        var X = ;

        var Y = ;

}  

    class Block:Drawing {

        var Rotation = ;

        var Scale = ;

        var Children = [];

} 

    class Line:Drawing {

        var Length = ;

        var Angle = ;

} 

    class Circle:Drawing {

        var Radius = ;

}

    class Rectangle:Drawing {

        var Width = ;

        var Height = ;

}

    return Drawing { Name = "图形", Author = "hjx" }

    {

        Line { Length =  }

        Circle { Radius =  }

        Block { Rotation =  }

        {

            Line { Angle =  }

            Circle { Radius =  }

        }

    };

}

计算结果如下:

{

    Class = 'Drawing'  ,

    Layer = ''  ,

    Color = 'ByLayer'  ,

    LineType = 'ByLayer'  ,

    LineWidth = 'ByLayer'  ,

    X =   ,

    Y =   ,

    Name = '图形'  ,

    Author = 'hjx'  ,

    Children = {

        {

            Class = 'Line'  ,

            Layer = ''  ,

            Color = 'ByLayer'  ,

            LineType = 'ByLayer'  ,

            LineWidth = 'ByLayer'  ,

            X =   ,

            Y =   ,

            Length =   ,

            Angle = 

        }  ,

        {

            Class = 'Circle'  ,

            Layer = ''  ,

            Color = 'ByLayer'  ,

            LineType = 'ByLayer'  ,

            LineWidth = 'ByLayer'  ,

            X =   ,

            Y =   ,

            Radius = 

        }  ,

        {

            Class = 'Block'  ,

            Layer = ''  ,

            Color = 'ByLayer'  ,

            LineType = 'ByLayer'  ,

            LineWidth = 'ByLayer'  ,

            X =   ,

            Y =   ,

            Rotation =   ,

            Scale =   ,

            Children = {

                {

                    Class = 'Line'  ,

                    Layer = ''  ,

                    Color = 'ByLayer'  ,

                    LineType = 'ByLayer'  ,

                    LineWidth = 'ByLayer'  ,

                    X =   ,

                    Y =   ,

                    Length =   ,

                    Angle = 

                }  ,

                {

                    Class = 'Circle'  ,

                    Layer = ''  ,

                    Color = 'ByLayer'  ,

                    LineType = 'ByLayer'  ,

                    LineWidth = 'ByLayer'  ,

                    X =   ,

                    Y =   ,

                    Radius = 

                }

            }

        }

    }

}

八、S#语言小结

至今为止,本系列博文把S#的基本语言特性大都介绍过了,还有一些高级特性后续博文继续讲解。不知您对S#语言有何看法?如有请把您理解的优势利弊发表在下面评论中。

总而言之,S#语言是一种基于.NET平台的面向表达的动态语言,他尽量兼收并蓄了其他语言常用的语法特性,把数据值、数组、列表、数据表、函数以及数据类全部都看做是一等公民,并通过对公式和语句两种语法基础结构的对全面支持和扩展,使得该语言化繁为简,表达能力相当出色。

声明:原创文章欢迎转载,但请注明出处,https://www.cnblogs.com/ShoneSharp。

软件: S#语言编辑解析运行器13.6.31,运行环境.NET 4.0,绿色软件,单个EXE直接运行,无副作用。网盘下载链接为https://pan.baidu.com/s/1nv1hmJn