🌓
搜索
 找回密码
 立即注册

【SU Ruby教程】了解基础结构前:数据类型

admin 2022-9-12 19:42:23 43665

这篇文章是属于SketchUp Ruby教程的第三篇,也是第二部分的第一篇。上一部分教程目的在于通过一个简短的例子大概地窥探SketchUp Ruby的用法,因此很多涉及到的细枝末节被一笔带过甚至隐藏。但从这一部分开始,教程将尽可能地展开内容。


这一部分的核心是介绍SketchUp的基本结构,为了更好的理解这些结构,需要一些例子来展示它们的功能。而理解这些例子需要一些Ruby的语法知识,因此在展开介绍基本结构之前需要对Ruby的数据类型和基础语法有所了解。于是这一部分将分为三篇文章,分别介绍Ruby的数据类型基础语法SketchUp Ruby的基本结构


第一篇:Ruby的数据类型


前期工作:打开SketchUp,在“窗口”菜单中选择“Ruby控制台”。


(1)基础数据类型


在控制台中分别依次输入以下几行代码,每一次完成一行后回车确认,便可以看到控制台执行这行代码的结果(“#>>”之后展示返回值):


1.class#>>Fixnum1.1.class#>>Float[1,2,3,4,5].class#>>Array"str".class#>>String(1==2).class#>>FalseClass(3>2).class#>>TrueClass


以上的代码展示几种基本的数据类型,分别是整型数(FixNum)浮点数(Float)数组(Array)、字符串(String)布尔类型(TrueClass / FalseClass)。“.class”方法可以查看这些数据的类型。整型数和浮点数用于表示数的概念,而数组常常用于表示多维向量、复数和集合这些数学概念。字符串用于表示名义上的概念,如文件名、组件名称、文字标注等概念。最后两项的TrueClass和FalseClass是布尔类型,用于表示条件是否成立;这是它们的类型名称,而它们的值分别表示为“true”和“false”。false和FalseClass的关系类似于0和FixNum的关系,前者是数值,后者是类型;但是不同于整型数多对一的关系,两个布尔数值分别对应唯一的一个类型。以下代码可以查看布尔变量的值而非类型:


(1==2)#>>false(3>2)#>>true


每一种类型都有对应的赋值运算比较的方法。在ruby中,变量没有强制的类型要求,一个变量存进整型数它就是整型变量,存进字符串它就是字符串变量。尝试一下赋值语句,将不同类型的数值赋值给不同的变量:


a=128b=2.7182c=[98,4,30]d="Apiglio"e=truef=false
puts "#{a},#{b},#{c},#{d},#{e},#{f}"#>>128,2.7182,[98,4,30],Apiglio,true,false


注:以上代码中第8行表示依次输出a到f并以逗号隔开,这是Ruby的格式化输出方法(之一)。


赋值是将数值赋予变量,而运算是通过一个或多个数值计算得出一个某种类型的结果。就像向量的加减结果是向量,但点乘的结果是数,运算的结果可以不是参与计算的变量的类型,因此前文所说的“运算”和“比较”都是运算的概念,只不过后者返回的是布尔变量而已。以下直接用代码附上结果和说明来简单介绍不同数据类型的一些常用运算方式。


①数的算术运算

4+2#>>6    加法,浮点数同理,两个值有一个是浮点数时结果为浮点数4-3#>>1    减法,浮点数同理,两个值有一个是浮点数时结果为浮点数4*3#>>12   乘法,浮点数同理,两个值有一个是浮点数时结果为浮点数12/3#>>4    除法,这里整型与浮点型不同13/3#>>4    除数和被除数都是整型时为整除,结果为整型13.0/3#>>4.333333333333333#除数和被除数中有一个为浮点型是为一般的除法,结果为浮点型1.0/0#>>Infinity   被除数为0并不会报错,而是返回无穷量#Infinity能带符号,其反正切函数结果(Math.atan 1.0/0.0).radians=9013%3#>>1    求余,两个值有一个是浮点数时结果为浮点数2**10#>>1024 幂运算,两个值有一个是浮点数时结果为浮点数


②字符串和数组的运算

"Ap"+"iglio"#>>Apiglio               字符串连接"ASD-"*3#>>ASD-ASD-ASD-          字符串复读[1,2,3,4]+[3,4,5,6]#>>[1,2,3,4,3,4,5,6]     数组连接[1,2,3,4,5,6]-[2,5]#>>[1,3,4,6]             差集运算[1,2,3,4,5]|[3,4,5,6,7]#>>[1,2,3,4,5,6,7]       并集运算[1,2,3,4,5]&[3,4,5,6,7]#>>[3,4,5]               交集运算


③比较运算

1>2#>>false    是否大于1==1#>>true     是否等于1<=1#>>true     是否小于等于(不大于)"Apemiro"!="Apiglio"#>>true     是否不等于1<=>2#>>-1       返回两个值的大小关系,小于返回-1,等于返回0,大于返回1#其他所有比较运算实际上都是调用这个运算,#但由于直观性不强,更多地是在排序中使用


④逻辑运算

true and truetrue && true#>> true   与运算,两种表达完全等价 true or falsetrue || false#>> true   或运算,两种表达完全等价 not true! true#>> false  非运算,两种表达完全等价 false ^ false# true     异或运算,不能使用xor 


以上出现的这些用来代表赋值、运算和比较的符号都被称之为运算符,它们可以分为赋值运算符算术运算符比较运算符逻辑运算符。除此之外还有位运算符和条件(三元)运算符,赋值运算符也不止“=”一种。其他的运算符有一些仅仅是类似方言的存在,其功能可以被完全代替,在此处暂时不去理会,在有需要的时候会再介绍。


这些类型之间可以相互转换的,只需要在其后使用“.to_x”的方法:


⑤类型转换

1.to_f#>>1.0       转换为浮点数1.0.to_i#>>1         转换为整数(截取整数部分)12358.to_s#>>"12358"   转换为字符串"1+4i".to_c#>>i+4i      转换为复数[1,2,3,4].to_a#>>[1,2,3,4] 转换为新的数组,这么做的原因之后会提到


通常情况下,包括SketchUp的数据结构在内的其他数据类型也会有类似的转化方法。类似的也有其他类似的类型转换方法,也都是形如“.to_xxx”的方法。


关于字符串和数组还可以在多说几句,是有关它们整体与局部之间的关系。字符串是一连串字母元素有序组成的整体,数组则更为广泛的可以表示任何类型数据元素的排列。在使用过程中,有的时候需要对其中的某一些元素进行操作,这需要了解它们的下标。以下是一些例子:


⑥字符串和数组的下标与范围

"string"[0,10,20,30,40,50]#>>string#>>[0,10,20,30,40,50]#数组和字符串中个别元素的访问方法十分相似"string"[3][0,10,20,30,40,50][3]#>>i#>>30#返回第3个元素,此处第0个为习惯上的“第一个”,一次第3个即习惯上的“第四个”"string"[1,4][0,10,20,30,40,50][1,4]#>>trin#>>[10,20,30,40]#从第1个开始截取四个元素"string"[1..3][0,10,20,30,40,50][1..3]#>>tri#>>[10,20,30]#从第1个元素截取到第3个元素(两个点)"string"[1...3][0,10,20,30,40,50][1...3]#>>tr#>>[10,20]#从第1个元素截取到第3个元素之前(三个点)"string"[-3..-1][0,10,20,30,40,50][-3..-1]#>>ing#>>[30,40,50]#从倒数第3个元素截取到倒数第1个元素"string"[-3,2][0,10,20,30,40,50][-3,2]#>>in#>>[30,40]#从倒数第3个元素开始截取2个元素


⑦数组元素的增加与去除

arr=[1,2,3,4]      #创建一个数组puts arr.to_s      #返回[1, 2, 3, 4]arr << 5           #将元素5添加到数组末尾puts arr.to_s      #返回[1, 2, 3, 4, 5]arr.push 6         #将元素6添加到数组末尾,等价于“<<”的方法puts arr.to_s      #返回[1, 2, 3, 4, 5, 6]puts arr.pop       #取出最末尾的元素并通过puts函数返回6puts arr.to_s      #返回[1, 2, 3, 4, 5]puts arr.shift     #取出最开端的元素并通过puts函数返回1puts arr.to_s      #返回[2, 3, 4, 5]arr.unshift(1)     #在数组的最开端插入一个元素1puts arr.to_s      #返回[1, 2, 3, 4, 5]arr.insert(2,2.5)  #在第2个元素之前插入一个元素2.5puts arr.to_s      #返回[1, 2, 2.5, 3, 4, 5]
arr=[1,2,2,3,3,3]  #重新赋值arrarr.delete(2)      #删除其中所有等于2的元素puts arr.to_s #返回[1, 3, 3, 3]


⑧数组的排序、去重复和扁平化

[1,4,2,8,5,7].sort#>>[1,2,4,5,7,8]     升序排序[1,4,2,8,5,7].sort.reverse#>>[8,7,5,4,2,1]     升序排序后倒置实现的降序排序[1,4,2,8,5,7].sort{|a,b|-(a<=>b)}#>>[8,7,5,4,2,1]     直接地降序排序[1,4,2,8,5,7].sort{|a,b|a<=>b}#>>[1,2,4,5,7,8]     与第一行完全等价
[1,2,2,3,3,3].uniq#>>[1,2,3]           返回一个取出重复元素的结果[1,2,3,[1,2],[2,3]].flatten#>>[1,2,3,1,2,2,3]   返回一个扁平化的结果
arr=[1,3,3,2,4]puts arr.sort.to_s   #返回[1,2,3,3,4]puts arr.uniq.to_s #返回[1,3,2,4]puts arr.to_s #返回[1,3,3,2,4]#这是因为上述方法只返回结果不改变原有数组puts arr.sort!.to_s  #返回[1,2,3,3,4]puts arr.uniq!.to_s  #返回[1,2,3,4]puts arr.to_s        #返回[1,2,3,4]#带有感叹号的方法意味着对自身数据的修改# flatten也有对应的flatten!方法 


以上直接用代码展示了基本数据类型的一些常见用法,不过实际上基本数据类型并不存在与Ruby之中,包括这些类型也都是对象的概念,包括之前的运算符和之后的一些方法都是以对象的公有方法的身份来运行的。


123*342123.*(342)"Pineapple"+"Apple""Pineapple".+("Apple")


以上代码中的第一行和第三行是通常理解意义上的运算方式,但Ruby是分别将它们解析成第二行和第四行的样子,并调用字符串类的“.*”或者“.+”方法实现的,而每一个整型数、浮点数、字符串、数组和布尔类型其实都是“类”的概念,每一个数字,每一个字符串都是这些类的实例。为了更好地理解这个概念,将需要粗略的理解一下类与对象的概念,这也引出了下一个部分。


(2)类与对象


对象是一种抽象的方式,任何事物都可以被抽象为“对象”,而这个对象又一定属于某一些范畴,我们对具体一个事物的认识是通过将这个“对象”归于某个范畴,因而以这个范畴的特征来认识它。就像三段论一样,钠是碱金属,碱金属具有强还原性,所以钠具有强还原性。同样地,还可以描述成这样:object属于Class,所有Class都能够method,所以object也能够method。


所以object、Class和method的关系就很清楚了。其中object是对象,Class是类,method是类的方法。如果object属于Class,那么object就是Class的一个实例。这样一来“对象”、“类”、“方法”和“实例”这些概念就基本清晰了。


可能有强迫症患者会有因为上述例子中只有Class首字母大写而另两个没有表示不适,但是这是ruby的语法决定的。第一节课就有说过,ruby是大小写敏感的,“class”和“Class”是两个不一样的标识符。大写字母表示常量、类和模块名称,而变量、对象和方法以小写字母和下划线开头。


回到之前碱金属钠的例子,现在使用ruby构造一块属于碱金属(AlkaliMetal)的钠(sodium),并将它投入水(water)中就可以用以下的代码来实现:

class AlkaliMetal  def drop_into(targ)    #此处是具体实现方法  endend
sodium=AlkaliMetal.newsodium.drop_into(water)


这里首先定义了一个“AlkaliMetal”类,并且定义有“.drop_into”的方法,表示扔进某种其他物质里头。“targ”是这个方法的参数,在第8行中即“water”,也是一个对象(在别处也有它自己的定义),它能够帮助“.drop_into”方法明确具体以何种方式执行。而执行过程中“sodium”对象会熔化并逐渐消失在水中,同时“water”对象也会有相应的数据修改,比如说内含的离子浓度和温度这样的属性。但如果“.drop_into”方法的参数不是“water”而是煤油,那么参数的修改会使得同样的方法会有不同的处理结果。再比如参数是实心木块,在这种情况下是没有办法完成“投入”这个动作的,所以“.drop_into”方法不会执行,并且会在报告参数错误的同时终止执行之后的代码。


上文中提到水作为对象的属性,而属性是对象的构成要素之一,因为同一个类对象之间也会存在差异,这些差异需要靠属性来区别。例如对象“water”有温度的区别,那么每一个对象都会有一个@temperature的属性。以下代码定义了温度属性以及温度属性对应的读写方法:

class Water  @temperature=0  #以上定义了一个名叫temperature的属性  def temperature=(x)    @temperature=x  end  #以上定义了一个名叫"temperature="的赋值属性的方法  def temperature    return @temperature  end  #以上定义了一个名叫"temperature"的返回属性的方法end
water1=Water.newwater2=Water.newwater1.temperature=100water2.temperature=0puts "water1's temperature is now #{water1.temperature} centigrade"puts "water2's temperature is now #{water2.temperature} centigrade"#>>water1's temperature is now 100 centigrade#>>water1's temperature is now 0 centigrade


另外很有意思的是,“类”和“方法”本身也是对象,这就是说任何一个“类”也有所属的类(即类的类::Class),任何方法也有他的方法(即方法的方法::Method.methods)。这似乎很绕,但是请尝试以下代码:


ents = Sketchup.active_model.entites
ents.classents.class.class
mtd = ents.method :add_linemtd.classmtd.namemtd.parametersmtd.methods


以上代码中:第三行返回ents的类型(这里会返回Sketchup::Entities类),第四行返回Sketchup::Entities的类型(::Class类)。第六行的“ents.method”(注意,这里没有字母s)返回ents中的“:add_line”方法。之后可以查看这个方法所属的类型(::Method)、方法名、参数要求等等内容。


与类相似的还有一个模块的概念,像Sketchup、Geom这些就是模块,模块可以理解为不能实例化的类,它在Ruby中可以用作命名空间,一些类会定义在模块中,例如“Sketchup::Entity”(实体类)、“Sketchup::Edge”(边线类)和“UI::Toolbar”(工具栏类)。值得注意的是上述三个例子中Entity类和Edge类之间存在继承关系,或者更准确地说Entity是Edge的父类,因为抽象过程中所有的边线也都属于图元实体。这种继承关系表现为子类(Edge类)的实例可以使用父类(Entity类)的方法,同样的Edge的实例也可以出现在Entity类的容器Sketchup::Entities中。


#新建一个模型,删除自带的人物模型,用直线工具画一笔ents = Sketchup.active_model.entites
ents[0].class#>>Sketchup::Edge#返回人唯一一个图元的类型,即“边线类型”
ents[0].instance_of? Sketchup::Edgeents[0].kind_of? Sketchup::Edgeents[0].instance_of? Sketchup::Entityents[0].kind_of? Sketchup::Entity#以下是返回结果:#>>true#>>true#>>false#>>true


以上代码中“.instance_of?”和“.kind_of?”两个方法的作用是返回对象是否与某个类存在关系。当对象是给定类的实例时“.instance_of?”和“.kind_of?”都返回true(真);当对象是给定类的子类的实例时,“.instance_of?”返回false(假),而“.kind_of?”依然返回true(真);除此之外的其他情况两个方法均返回false(假)。


以上是有关类与对象的初步介绍,而核心内容在于理解类与对象的使用方法,知道类的继承关系,但是暂时并不要求去自己定义和构造类或者模块,因为更多时候,使用SU Ruby就像是搭积木,用提供的方法的组合来解决具体的需求。当然随着教程内容的深入,后续在合适的时机这些内容还是会出现的。


(3)关于类与对象的自学


因为Ruby中数据皆可对象,因此标题中的“数据类型”现在已经可以将它理解成是“类与对象”了。这最后一小节简单地介绍一种认识类与对象的方法。这种方法在第一篇中就已经提到过了,到现在依然值得重复介绍。那就是利用几个非常实用的方法来了解一个未知的对象。


以下方法可以确定一个object是哪一个类的实例:

object.class

以下方法可以打印出object可以使用的方法:

object.methods

以下方法可以打印出object中名为method的方法所需要的参数列表:

(object.method :method).parameters

以下方法可以确定object的所属类的父类:

object.class.superclass

以下方法可以将object的所属类的完整继承关系打印出来:

object.class.ancestors

通过使用上面这些方法,可以很有效地了解一个未知的对象,而在面临问题是,能够找到更多关键词用于检索,以便于快速在网络中找到相关的实现案例。



最后还是按照惯例放一些相关的代码。


#当列表中的对象不能简单地使用.sort方法排序时就需要使用到块的概念#这一部分内容本属于下一篇的内容,但是无奈地出现在了这一篇中的排序部分#因此在这里展示一下.sort方法与块语句的结合有何作用arr=["123","146","223e-1","642e2","866","345e3","523","234e2"]#这里的"223e-1"是科学计数法,表示223*(10^-1),即22.3arr.sort#>>["123", "146", "223e-1", "234e2", "345e3", "523", "642e2", "866"]#这种排序是直接按照字符的前后顺序来排序的#它等同于arr.sort{|a,b|a<=>b}arr.sort{|a,b|a.to_f<=>b.to_f}#>>["223e-1", "123", "146", "523", "866", "234e2", "642e2", "345e3"]#以上这种情况则是将每一个字符串转换为浮点数再比较大小
#类似的例子还有:sel=Sketchup.active_model.selection.to_a#将选区中的图元存入数组sel中sel.sort!{|a,b|a.bounds.min.z<=>b.bounds.min.z}#按照图元的下界高度对sel进行重新排序




这是Apiglio SU Ruby教程的第三篇,编号为“SU-R03”,本系列的其他文章可以在公众号后台输入“SU-R”+两位数编号查看,或者直接点击菜单中的“SU Ruby”选项获得全部有关SketchUp文章的目录。



扫一扫

109305.jpg

0 回复

高级模式
游客
返回顶部