使用序列

阅读(6663) 标签: 序列,

一些数据构成的有序集合即称为序列,构成序列的数据称为其成员。序列相当于高级语言中的数组,但其成员的数据类型不要求一致。下面通过生成、访问、运算符、函数等几个方面讲解序列的基本运算。

生成

常数构造

在显示序列和序表时,在集算器中会显示Index列以便查看,这一列并不属于序列或序表的结构,在后面的展示中,并不一定会显示这一列的内容。

将常数直接用"[]"括起来可表示序列常数,也可以在表达式中用"[]"将成员括起来得到序列,如:

 

A

B

C

1

1

red

2013-06-04

2

2

blue

27.49

3

3

yellow

Tom

4

[15.2,b,1]

=[A1:C3]

=[3,A4,B4]

5

[1,2,3,3]

[]

[[]]

网格中,A4,A5,B5,C5中,都是序列常数,B4C4中是用表达式计算的序列。

其中,A4序列中的成员包含了浮点数、字符串、整数等不同的类型,B4中的成员由单元格区域得到,C4中序列的成员还包含序列,A5中序列中存在重复的成员。A4,B4,C4A5中的数据依次如下:

 

 

下面来比较一下B5C5中的值:

 

可以看到,B5中是空序列,而C5中是一个非空序列,其成员只有一个空序列。

说明:序列的成员可以是任意数据类型,包括基本类型、其他序列、记录等等,成员全是整数的序列被称为数列

函数构造

 

A

1

=to(2,6)

2

="1,a,b,c".split@c()

3

=periods@yx("2014-08-10",date(2018,2,1))

4

=file("sales.txt").import@t()

其中A1表示从26的连续整数构成的序列,如果从1开始的数列可以简写为to(6)A2中将字符串拆分为序列。A3中生成两个日期之间的日期序列,@y选项表示以年为间隔,@x选项表示不包括后端点。A1,A2A3中的结果如下:

   

A4从结构化的文本文件中读取记录形成序列,其值为:

 

以记录为成员的序列又称作序表,常用来进行结构化数据的计算,序表不是本文重点,想进一步了解请参考使用与排列

计算生成

下面网格中的代码,将文本文件sales.txt读入序表A1,取其中的STATE列,生成序列A2,将记录按照STATE分组,生成序列A3

 

A

1

=file("sales.txt").import@t()

2

=A1.(STATE)

3

=A1.group(STATE)

计算后,A2中的序列如下:

A3中的序列如下:

可以看到,序列A3的成员是多个序列,这些序列的成员都是记录。

访问

按序号访问成员

 

A

B

1

[a,b,c,d,e,f,g]

 

2

=A1(2)

=A1.m(2)

3

=A1([2,3,4])

=A1(to(2,4))

4

=A1.m(-1)

 

A2B2中的表达式是等价的,都是从序列中取出第2个成员,结果如下:

A3中从序列中取出第24个成员,值为序列。注意,[2,3,4]也是个序列(数列),因此B3中中表达式的结果和A3中是相同的:

A4中取出序列中的倒数第1个成员。注意,倒取数时必须用A.m()函数,不能简写为A1(-1)。结果如下:

特别的,用A.m()函数可以访问多个成员,其中还可以指定某个区间,如:

 

A

B

1

[a,b,c,d,e,f,g]

 

2

=A1.m(2,-2)

=A1.m(2:4,3:5)

3

=A1.m(-1,4:-2,5)

=A1.m(-2:2)

A2中获取序列中的第2个和倒数第2个成员,B2中获取序列中两段区间中的成员,A3中在获取序列成员时,同时使用了指定位置和区间的方式。A2,B2A3中的结果依次如下:

  

B3中指定的区间是从倒数第2个到第2个,顺序是颠倒的,获得的结果中,成员的排列也是逆序的:

如果只需访问序列中的一段区间内的成员,可以使用A.to()函数,如:

 

A

B

1

[a,b,c,d,e,f,g]

 

2

=A1.to(2,4)

=A1.to(4,2)

3

=A1.to(3)

=A1.to(-3)

A2,B2,A3,B3中的结果如下:

 

 

B2的结果中可以看到,与A.m()不同,A.to()函数在指定区间时,可以由后至前,此时获得序列中成员的顺序也是逆序的。A3中表示获得序列的前3个成员,B3中表示获得序列的最后3个成员。

赋值和修改

 

A

1

[a,b,c,d,e,f,g]

2

>A1(2)="r"

3

>A1([3,4])=["r","s"]

4

>A1.modify(4,[ "r","s"])

为了明确每个单元格中的语句引起的变化,在这里点击工具栏中的按钮,分步执行代码。

A2A1中的第2个成员修改为rA3继续修改A1中的第3、第4个成员。A4从第4个成员起继续依次修改,该表达式等价于>A1([4,5])=["r","s"],逐步运行时A1中序列的变化如下:

新增成员

 

A

1

[a,b,c,d,e,f,g]

2

>A1.insert(0,"r")

3

>A1.insert(2,["r","s","t"])

A2在序列结尾新增成员,A3继续在第2个成员之前连续插入3个新成员。仍然使用分步执行,A1中序列的变化如下:

 

除了insert之外,还可以用A.pad(x,n)函数,在序列A中连续添加x构成新序列,直到其中的成员个数达到n,如:

 

A

1

[a,b,c,d,e,f,g]

2

=A1.pad("A",10)

计算后,A2中的结果如下:

A1中的原序列不会受到pad函数的影响。

删除成员

 

A

1

[a,b,c,d,e,f,g]

2

> A1.delete(2)

3

> A1.delete([2,4])

A2中删除第2个成员,A3中继续删除第2、第4个成员,分布执行时,A1中变化如下:

运算符

集合运算

集合运算包括^(交集)、&(并集)、\(差集)、|(合集)等,比如:

 

A

B

1

[a,b,1,2,3,4]

[d,b,10,12,3,4]

2

=A1^B1

=A1\B1

3

=A1&B1

=A1|B1

A1B1中的序列如下:

 

A2,B2,A3,B3中,分别计算这两个序列的交集,差集,并集和合集。计算后,A2,B2,A3,B3中的结果分别如下:

 

 

说明:并集与合集都是把两个序列的成员按顺序合并起来组成新序列,但并集中共同的成员不重复出现,合集中会重复出现。

在上面的集合运算中,序列中包含了不同种类的数据,既有字符,又有整数。双目运算只需比较是否相等,因此上面的代码能够正常运行,但需要注意的是,字符与整数是不能比较大小的。如果需要比较大小,如计算排序等,必须保证序列内成员数据能够比较。如:

 

A

B

C

1

[3.456,5L,,2,-3,4]

=["d","b","Ace",,"Tom","3"]

[a,b,1,2,3,4]

2

=A1.sort@z()

=B1.sort()

=C1.sort()

A1中序列的成员包括各种实数及空值,B1中的成员包括字符串或空值,A1B1中的成员均可以比较。A2B2中排序后的结果如下:

 

其中,空值null都会被认为是最小值。

C1中的序列中,既包括字符串,又包括整数,两种类型的数据是不能比较的,因此C2在计算时会报错:

对位四则运算

长度相同的两个序列可按成员进行对位计算,返回序列,包括:++(加)、--(减)、**(乘)、//(除)、(求余)%%,比如:

 

A

B

1

[1,2,3,4]

[10,12,3,4]

2

=A1++B1

=A1--B1

3

=A1**B1

=A1//B1

A1B1中的序列如下:

 

A2,B2,A3,B3中,分别用这两个序列计算对位加法,对位减法,对位乘法和对位除法。计算后,A2,B2,A3,B3中的结果分别如下:

 

 

布尔运算

在集算器中,用函数cmp(A,B)可以比较两个序列AB的大小。

Ø  cmp(A,B)

比较序列大小时,对位比较每个成员的值,遇到第一个不等成员时根据大小分别返回1-1AB全等则返回0。特别的,cmp(A)或者cmp(A,0),表示A和与之等长且成员均为0的数列比较,即cmp(A,[0,0,…,0])

 

A

1

=cmp(["a","b","c"],["a","b","c"])

2

=cmp([1,3,5,7],[1,3,7,5])

3

=cmp([7,6,5,4],[7,6,4,10,11])

A1,A2A3中结果如下:

   

两个序列的比较可以简写成A==B, A>B这样的形式。

通过使用这种写法,两个序列可以对位比较其大小,结果为布尔型。如:

 

A

1

=[1,2,3]==[1,2,3]

2

=[1,"B",3]<=[1,"b",4]

3

=[1,2,3]<[1,3,4]

A1中比较结果为true,两个序列相等。A2中比较结果为true,因为Bb小。A3中结果为true,因为两个序列的第2个成员比较时,23要小:

   

需要注意的是,集算器中的序列,是有序的集合,因此在判断两个序列AB是否大小相等时,是跟顺序有关的。如果需要判断的是两个序列是否有着相同的成员,需要用A.eq(B)来判断:

 

A

1

[Tom,Jerry,Tuffe,Tyke]

2

[Jerry,Tuffe,Tom,Tyke]

3

=A1==A2

4

=A1.eq(A2)

A1A2中成员的顺序不同,因此A3中的结果表明两个序列不相等:

A4中的结果为true,说明两个序列的成员相同:

 

函数

序列与字串

通过s.split()A.concat()两个函数,序列和字串可以很方便地相互转化。函数s.split(d)用来将字串s以分隔符d拆分成序列,@p选项自动识别数据类型;d缺省为按字符拆分。函数A.concat(d)用来将序列A用分隔符d连接,拼接成字串,自动处理数据类型;d缺省为无分隔符。在这两个函数中可以添加@c选项,表示以逗号为分隔符。

如:

 

A

1

a,1,c,2011-8-11,false

2

=A1.split@c()

3

=A2.concat@c()

A2A3中结果如下:

 

可以看到,A2中拆分子串后,结果序列中的成员均为字符串,如果需要自动解析数据类型,可以添加@p选项。

 

正则表达式函数s.regex(rs)用正则表达式rs匹配字串s,返回匹配结果的序列;如果无法匹配,则返回nullregex函数中,可以添加选项@c表示大小写字母不敏感,添加选项@u表示使用unicode匹配。

regex函数,最简单的用法就是判定某个字符串与指定正则表达式的匹配结果,下面先来看一下与数字字符串的匹配用法:

 

A

B

C

1

="a12b".regex("(a[0-9])")

="a12b".regex("(a[0-9]*)")

="a12b".regex("^[0-9]b")

2

="a12b".regex("\\S*([0-9][a-z])")

'\S*([0-9][a-z])

="a12b".regex(B2)

先来看A1,B1C1中的结果:

  

A1中使用的正则表达式是"(a[0-9])",其中a就是对应字母a本身,而[0-9]表示09之间的1个字符,即1位数字,两边的小括号()表示把字符串中能匹配“字母a开头后接1位数字”的串取出返回,结果就是单一成员a1构成的序列。B1[0-9]后面的*表示连续匹配前面的字符任意次,这里表示连续的任意位数字,因此B1中返回的结果是a12C1中,^表示字符串的开头,整个正则表达式需要返回数字开头后面接着字母b的字符串,但是a12b并非是“以数字开头”的,因此无法匹配,C1中的结果是null

A2中使用的正则表达式是"\\S*([0-9][a-z])",其中[a-z]表示字母az之间的字符,即所有小写字母。\S*表示连续的任意个可见字符,由于\是字符串中的转义字符,因此需要用\\S*表示。在这里,用小括号表示取出的部分是后面匹配[0-9][a-z]的结果,而前面匹配\S*的部分会被丢弃。在B2中,用字符串常数来表示A2中的正则表达式,此时不涉及字符串转义问题,所以C2中可以获得和A2中相同的结果。A2C2中的结果如下:

 

在集算器中,用正则表达式匹配字符串时,需要将返回的部分用 () 括起来,将返回由括号括起来的成员所构成的序列,否则在能够匹配时只会返回字符串本身。

 

在使用regex() 函数时,可以添加@c选项,表示在匹配正则表达式时,大小写字母不敏感,如:

 

A

B

C

1

="a12b".regex@c("(A[0-9])")

="a12b".regex@c("([A-Z][0-9])")

="a12b".regex("([A-Z][0-9])")

A1,B1C1中结果如下:

  

A1中正则表达式中要求匹配大写字母AB1中要求匹配任1个大写字母,由于使用了@c选项,因此用regex() 都能得到匹配结果a1,而C1中未使用@c选项,无法匹配正则表达式,返回null

用正则表达式rs匹配字串s,返回匹配结果的序列;如果无法匹配,则会返回null

当用集算器处理非英文字符时,有时需要用unicode来执行正则表达式,这是由于用unicode比较标准,不会被字符集设置所干扰。此时使用regex() 函数时,需要添加@u选项,如:

 

A

B

1

="Gerente de Fábrica".regex(".* (.*á.*)")

="Gerente de Fábrica".regex@u(".* (.*\\u00e1.*)")

A1的正则表达式.* (.*á.*)中,小数点 . 表示除回车符和换行符外的任一个单字字符,.*则表示任意个字符,A1中取出的是从最后一个包含字符á的单词之后的字符串。B1中的正则表达式是同样的意义,不过regex函数添加了@u选项,其中的字符áunicode的表示方法,表示为\u00e1regex@u() 通常用于解析外部字符串,通过unicode设置正则表达式可以避免不同字符集的干扰。A1B1中的结果是相同的:

 

 

聚合函数

序列的聚合函数包括求和A.sum()、平均值A.avg()、最大值A.max()、最小值A.min()、方差A.variance()等等。它们的使用方法都类似,如:

 

A

1

[2,4,6]

2

=A1.sum()

3

=A1.sum(~*~)

A2对序列求和,A3求序列中成员的平方和,A2A3中的结果如下:

 

如果序列中的成员全部是结果为布尔型的判断条件,可以用函数A.cand() A.cor() 来判断这些条件是否全部成立,或者是否至少有一个成立。如:

 

A

B

C

D

1

=[1,2,3,4].cand(24%~==0)

=[12,-3,0].cor(24%~==0)

[]

 

2

for 500

=A2%3==2

=A2%5==3

=A2%7==2

3

 

if [B2:D2].cand()

>C1=C1|A2

 

A1中,判断序列[1,2,3,4]中的成员,是否全部是24的约数,B1中则判断[12,-3,0]中的成员是否包含了24的约数。中国古代有一道著名算题:“今有物不知其数,三三数之剩二;五五数之剩三;七七数之剩二。问物几何?”在第23行,计算了这道题目在500以内的解,记录在了C1中。计算后,A1,B1C1中结果分别如下:

  

当序列中出现重复成员时,可以用不同的聚合函数A.count()A.icount()来统计。如:

 

A

1

[2,3,3,2,5,7,1]

2

=A1.count()

3

=A1.icount()

A2中计算序列中所有成员的个数,而A3中只计算不同成员的个数,A2A3中的结果如下:

 

如果序列中的成员都是整数,可以添加选项以提高计算效率,用A.icount@n() 来计算。如果序列中的成员都是整数或者长整数,则可以用A.icount@b() 来计算。

还有一些聚合函数是和排序相关的,如排序函数A.rank(y) 和中位数函数A.median(k:n)。如:

 

A

B

C

1

[6,8,1,3,7,2,4,9,5]

 

 

2

=A1.rank(8)

=A1.rank@z(8)

 

3

=A1.median()

=A1.median(1:4)

=A1.median(:3)

A2中计算8在序列中从小到大的排名,B2中则计算8在序列中从大到小的排名,A2B2中结果分别如下:

 

A3计算序列A1中的中位数,即序列中值的大小位于最中间的一个成员,如果序列中成员为偶数个,那么中位数将返回最中间的两个成员的平均值。如果在median函数中添加参数,则可以取出序列中成员按照从小到大的顺序,处于参数所指定分段点的成员,类似的,如果分段点正好处于两个成员中间,则会返回两个成员的平均值。如B3中即返回位于1/4点的数据,因此,A.median(k:n)的参数中,分段数n必须为不小于2的整数,而k必须小于。当参数中的k省略时,将返回序列n等分点位置的数据。A3,B3C3中的结果如下:

  

还有一些函数是用来在多个序列之间聚合运算的,如:

 

A

B

1

[[1,2,3],[3],[3,4],[6,5,3]]

 

2

=A1.conj()

=A1.union()

3

=A1.diff()

=A1.isect()

在使用A.conj(),A.union(),A.diff(),A.isect()这些函数时,应该是序列的序列。A2,B2,A3,B3分别计算A1中各个序列成员的和列、并列、差列和交列,结果分别如下:

 

 

循环函数

循环函数可以针对序列的每个成员进行计算,可以将结构复杂的循环语句用简单的函数来表达,包括循环计算、过滤、定位、查找、排名、排序等,如:

 

A

B

C

1

[2,4,-6]

=A1.(~+1)

 

2

=A1.select(~>1)

=A1.pselect@a(~>1)

=A1.pos([-6,2])

3

=A1.ranks@z()

=A1.sort()

=A1.sort(-~)

B1将序列中每个成员加1,结果如下:

A2过滤出大于1的成员,B2定位出大于1的所有成员的序号,C2查找成员-62在序列A1中的序号。A2,B2C2中的结果如下:

   

A3中,通过在ranks函数中添加@z选项,按降序求得序列各成员的排名,B3将序列中的成员升序排序,C3则将序列中的成员降序排序。A3,B3C3中的结果如下:

   

特别的,使用某些定位函数时,可以指定查找的起始位置,如:

 

A

B

1

[2,4,-6,null,4,3,]

 

2

=A1.pselect@a(~>1, 5)

=A1.pos(4,3)

A2中,从第5个成员开始查找大于1的成员位置,B2中从第3个成员开始查找4的位置,A1B2中结果如下:

  

在定位函数中,除了常用的@1@z选项,还经常使用@0选项,如:

 

A

B

C

1

[2,4,-6,null,4,3,]

 

 

2

=A1.pselect(~>10)

=A1.pselect@0(~>10)

=A1.pos(5)

3

=A1.pos@0(5)

=A1.pmin()

=A1.pmin@0()

A2,B2,C2,A3,B3,C3执行后的结果如下:

   

   

可见,添加了@0选项后,A.pselect()A.pos()函数在无法找到成员的情况下会返回0而不是空值,A.pmin@0()则会在序列中存在空值时返回首个空值所在的位置。@0选项还可以用于其它的一些定位或选出函数,如A.pfind(), A.ptop ()A.top()

除了@0选项,A.pselect()A.pos()函数还可以使用@n选项,在无法找到成员的情况下返回序列的总长度+1,如:

 

A

B

1

[2,4,-6,null,4,3,]

 

2

=A1.pselect@n(~>10)

=A1.pos@n(5)

A2B2中的结果是相同的:

  

需要注意的是,@0选项和@n选项是互斥的,不能同时使用。