MFP编程语言对数、字符串和数组的操作:
截止当前版本,MFP还是一个面向过程,基于函数调用的编程语言,MFP对于各种各样复杂的数,字符串和数组一方面有内建的支持,另一方面也提供了一系列函数用于操作这些数据类型。
第1节 MFP对数的操作函数
1. MFP对整数的操作
MFP对整数的操作函数函数包括,如何对一个小数(也就是浮点数)取整,以及如何求整数的模。
对一个浮点数取整,MFP提供了三个函数:round,ceil和floor。round函数是对一个浮点数四舍五入,比如1.6取整为2,-1.6取整为 -2,1.4取整为1,-1.4取整为-1;ceil函数是返回不小于该浮点数的最小的整数,比如ceil(1.6)返回2,ceil(-1.6)返回-1,ceil(-5.0)返回-5;floor函数是返回不大于该浮点数的最大的整数,比如floor(1.6)返回1,floor(-1.4)返回-2,floor(-5.0)返回-5;
round,ceil和floor还能支持在任意小数位截断取值,这时候,这3个函数都需要两个参数,第一个参数是要被截断取值的浮点数,第二个参数是在哪一个小数位截断,比如round(-1.82347, 4)就是在小数点后面第四位截断并四舍五入取值,得到-1.8235;同理,ceil(-1.82347, 4) 就是在小数点后面第四位截断,并返回小数点后面最多跟随四位数字,并且不小于-1.82347的最小的数,也就是-1.8234,而floor(-1.82347, 1) 就是在小数点后面第1位截断,并返回小数点后面最多跟随1位数字,并且不大于-1.82347的最大数,也就是-1.9。
MFP求整数的模的函数名字叫mod。Mod(x,y) 返回x除以正整数y的余数(余数必须为正数),如果x或者y不是整数,将被首先转换为整数,转换的办法是如果被转换数大于0,则取比不比被转换数大的最大整数,如果被转换数小于0,则取比不比被转换数小的最小整数。比如Mod(-17.8214, 4.665)相当于求Mod(-17, 4)其结果为3。而Mod(17.8214, 4.665)相当于求Mod(17, 4)其结果为1。
以下是上面几个函数的例子程序。本例子可以在本手册自带的示例代码所在目录中的numbers, strings and arrays子目录中的examples.mfps文件中找到):
Help
@language:
test round, ceil, floor and mod functions
@end
@language:simplified_chinese
测试round,ceil,floor和mod等几个函数
@end
endh
function testRoundsMod()
print("\n round(1.6) == " + round(1.6))
print("\n round(1.4) == " + round(1.4))
print("\n round(-1.6) == " + round(-1.6))
print("\n round(-1.4) == " + round(-1.4))
print("\n ceil(1.6) == " + ceil(1.6))
print("\n ceil(-1.6) == " + ceil(-1.6))
print("\n ceil(-5.0) == " + ceil(-5.0))
print("\n floor(1.6) == " + floor(1.6))
print("\n floor(-1.4) == " + floor(-1.4))
print("\n floor(-5.0) == " + floor(-5.0))
print("\n round(-1.82347, 4) == " + round(-1.82347, 4))
print("\n ceil(-1.82347, 4) == " + ceil(-1.82347, 4))
print("\n floor(-1.82347, 1) == " + floor(-1.82347, 1))
print("\n mod(-17.8214, 4.665) == " + Mod(-17.8214, 4.665))
print("\n mod(17.8214, 4.665) == " + Mod(17.8214, 4.665))
endf
上述程序的运行结果如下:
round(1.6) == 2
round(1.4) == 1
round(-1.6) == -2
round(-1.4) == -1
ceil(1.6) == 2
ceil(-1.6) == -1
ceil(-5.0) == -5
floor(1.6) == 1
floor(-1.4) == -2
floor(-5.0) == -5
round(-1.82347, 4) == -1.8235
ceil(-1.82347, 4) == -1.8234
floor(-1.82347, 1) == -1.9
mod(-17.8214, 4.665) == 3
mod(17.8214, 4.665) == 1
2. MFP进制转换函数
MFP支持用二进制,8进制,10进制和16进制来表示一个整数或者小数。二进制数格式为0b打头,比如0b0011100,八进制数格式为0打头,比如0371.242或者00.362,16进制数格式为0x打头,比如0xAF46BC.0DD3E。用户输入这些数字,MFP在计算的时候将任何非10进制数自动转换为10进制数然后进行计算。比如用户可以把一个变量赋值为0b1101然后进行计算:
Variable a = 0b1101 //相当于Variable a = 13
Print("a+1 = " + (a+1))
,最后计算出来的结果为
a+1 = 14
。之所以得到10进制结果,是因为MFP的计算结果数值永远是用十进制表示的,用户需要调用以下函数将一种进制的数或者该进制数表达字符串转换为另一种进制的表达字符串(如果是要转化为10进制,则转换结果不是一个字符串而是10进制的数值):
conv_bin_to_dec(x)将一个二进制的非负数或代表该数的字符串x(x可以为浮点数,也可以为整数)转换为一个十进制的数值。
conv_bin_to_hex(x)将一个二进制的非负数或代表该数的字符串x(x可以为浮点数,也可以为整数)转换为一个代表16进制数的字符串。
conv_bin_to_oct(x)将一个二进制的非负数或代表该数的字符串x(x可以为浮点数,也可以为整数)转换为一个代表八进制数的字符串。
conv_dec_to_bin(x)将一个十进制的非负数或代表该数的字符串x(x可以为浮点数,也可以为整数)转换为一个代表二进制数的字符串。
conv_dec_to_hex(x)将一个十进制的非负数或代表该数的字符串x(x可以为浮点数,也可以为整数)转换为一个代表16进制数的字符串。
conv_dec_to_oct(x)将一个十进制的非负数或代表该数的字符串x(x可以为浮点数,也可以为整数)转换为一个代表八进制数的字符串。
conv_hex_to_bin(x)将一个16进制的非负数或代表该数的字符串x(x可以为浮点数,也可以为整数)转换为一个代表二进制数的字符串。
conv_hex_to_dec(x)将一个16进制的非负数或代表该数的字符串x(x可以为浮点数,也可以为整数)转换为一个十进制的数值。
conv_hex_to_oct(x)将一个16进制的非负数或代表该数的字符串x(x可以为浮点数,也可以为整数)转换为一个代表八进制数的字符串。
conv_oct_to_bin(x)将一个八进制的非负数或代表该数的字符串x(x可以为浮点数,也可以为整数)转换为一个代表二进制数的字符串。
conv_oct_to_dec(x)将一个八进制的非负数或代表该数的字符串x(x可以为浮点数,也可以为整数)转换为一个十进制的数值。
conv_oct_to_hex(x)将一个八进制的非负数或代表该数的字符串x(x可以为浮点数,也可以为整数)转换为一个代表16进制数的字符串。
还要注意,这里的代表某个进制数的字符串不包括进制的起头,比如二进制数0b1101(相当于十进制数13)的表达字符串是"1101 ",而不是" 0b1101"。
以下是上面几个函数的例子程序。本例子可以在本手册自带的示例代码所在目录中的numbers, strings and arrays子目录中的examples.mfps文件中找到):
Help
@language:
test conversion functions between bin, oct, dec and hex
@end
@language:simplified_chinese
测试进制转换函数
@end
endh
function testBinOctDecHex()
print("\n\nconv_bin_to_dec(\"00111001.000110\") = " _
+ conv_bin_to_dec("00111001.000110"))
print("\n\nconv_bin_to_hex(\".1000110001\") = " _
+ conv_bin_to_hex(".1000110001"))
print("\n\nconv_bin_to_oct(\"1000110001\") = " _
+ conv_bin_to_oct("1000110001"))
print("\n\nconv_dec_to_bin(\".487960\") = " _
+ conv_dec_to_bin(".487960"))
print("\n\nconv_dec_to_bin(.487960) = " _
+ conv_dec_to_bin(.487960))
print("\n\nconv_dec_to_bin(0.48700) = " _
+ conv_dec_to_bin(0.48700))
print("\n\nconv_dec_to_hex(\"153439.000\") = " _
+ conv_dec_to_hex("153439.000"))
print("\n\nconv_dec_to_hex(153439.000) = " _
+ conv_dec_to_hex(153439.000))
print("\n\nconv_dec_to_hex(153) = " _
+ conv_dec_to_hex(153))
print("\n\nconv_dec_to_oct(\"1356.2341\") = " _
+ conv_dec_to_oct("1356.2341"))
print("\n\nconv_dec_to_oct(1356.2341) = " _
+ conv_dec_to_oct(1356.2341))
print("\n\nconv_dec_to_oct(1356) = " _
+ conv_dec_to_oct(1356))
print("\n\nconv_hex_to_bin(\"0AB0039BA.FFE01BBC64\") = " _
+ conv_hex_to_bin("0AB0039BA.FFE01BBC64"))
print("\n\nconv_hex_to_dec(\"0AB0039BA.FFE01BBC64\") = " _
+ conv_hex_to_dec("0AB0039BA.FFE01BBC64"))
print("\n\nconv_hex_to_oct(\"0AB0039BA\") = " + conv_hex_to_oct("0AB0039BA"))
print("\n\nconv_oct_to_bin(\"027400330.017764\") = " + conv_oct_to_bin("027400330.017764"))
print("\n\nconv_oct_to_dec(\"027400330.017764\") = " + conv_oct_to_dec("027400330.017764"))
print("\n\nconv_oct_to_hex(\"027400330\") = " + conv_oct_to_hex("027400330"))
endf
函数打印输出的结果为:
conv_bin_to_dec("00111001.000110") = 57.09375
conv_bin_to_hex(".1000110001") = 0.8c4
conv_bin_to_oct("1000110001") = 1061
conv_dec_to_bin(".487960") = 0.011111001110101011110010010100011100000110010011101100111010011010001011000110011010010000010101111101000101111000001011010011100001000111011011110010101001
conv_dec_to_bin(.487960) = 0.011111001110101011110010010100011100000110010011101100111010011010001011000110011010010000010101111101000101111000001011010011100001000111011011110010101001
conv_dec_to_bin(0.48700) = 0.0111110010101100000010000011000100100110111010010111100011010100111111011111001110110110010001011010000111001010110000001000001100010010011011101001011110001
conv_dec_to_hex("153439.000") = 2575f
conv_dec_to_hex(153439.000) = 2575f
conv_dec_to_hex(153) = 99
conv_dec_to_oct("1356.2341") = 2514.1676677220777134443505161674646552054171173545773053
conv_dec_to_oct(1356.2341) = 2514.1676677220777134443505161674646552054171173545773053
conv_dec_to_oct(1356) = 2514
conv_hex_to_bin("0AB0039BA.FFE01BBC64") = 10101011000000000011100110111010.11111111111000000001101110111100011001
conv_hex_to_dec("0AB0039BA.FFE01BBC64") = 2868918714.99951337193851941265165805816650390625
conv_hex_to_oct("0AB0039BA") = 25300034672
conv_oct_to_bin("027400330.017764") = 10111100000000011011000.0000011111111101
conv_oct_to_dec("027400330.017764") = 6160600.0312042236328125
conv_oct_to_hex("027400330") = 5e00d8
3. MFP逻辑操作函数
逻辑操作函数是对布尔值(TRUE或者FALSE)进行操作,常常用于条件判断语句if或者elseif,以及条件判断函数iff(这里也将一并说明)。
MFP提供了三个逻辑判断函数:and、or和xor。
and函数接受不少于1个的任意个数的参数,返回这些参数的逻辑与值。如果某一个参数不是布尔类型,将会被自动转换为布尔类型(如果能够自动转换的话,如果不能自动转换,将会抛出异常)。
比如,and(True, 3>2, 1-1)我们得到False,原因在于,True,3>2的布尔值都是True,但是1-1得0,它的布尔值是False,三个参数中,有一个是False,返回就是False。而如果把第三个参数改为1-2得-1,它的布尔值是True,返回就是True。
or函数接受不少于1个的任意个数的参数,返回这些参数的逻辑或值。如果某一个参数不是布尔类型,将会被自动转换为布尔类型(如果能够自动转换的话,如果不能自动转换,将会抛出异常)。
比如,or(True, 3>2, 1-1)我们得到True,原因在于,True,3>2和1-1的布尔值中有True,or函数的参数,只要有一个是True,返回就是True。而如果把第一个参数改为False,第二个参数改为3<2,它们的布尔值都变为了False,返回就是False。
xor函数是逻辑异或函数,它有两个参数,如果这两个参数的值不等,则返回True,否则返回False。
用户需要搞清楚上述三个函数和位于&,位或|以及位异或^这三个操作符的区别。首先,位操作符的操作数都是正整数,如果遇到不是正整数的操作数,位操作符将尝试强制转换为正整数。而逻辑函数,除了xor,的参数都是布尔值,如果不是布尔值,则强制转换为布尔值。
其次,位操作符只接受一前一后两个操作数,而逻辑函数and和or可以接受任意不少于1个参数。
最后,位操作符是对操作数的每一个比特位进行逻辑比较,比如7&8,7的所有比特位为111,8的所有比特位为1000,7&8得到0000也就是0,而逻辑操作函数则是把7和8转换为布尔值后进行逻辑比较,7和8转换为布尔值后都是True,所以and(7,8)得到True。
最后介绍条件判断函数iff。函数iff是if语句的函数表达形式,它需要最少三个参数,其语法为:iff(条件1, 如果条件1为True的结果, 条件2, 如果条件2为True的结果, ..., 如果所有条件都不是True的结果)。参数条件1,条件2,...为代表条件的布尔值,iff函数的返回值由条件值决定。比 如,iff(true, 3, 2)返回3,iff(3 < 2, 3, 2)返回2(这是因为3 < 2是False),iff(3 < 2, 3, 5 > 4, 5, 6 == 9, 6, 9)返回5,以及iff(3 < 2, 3, 5 < 4, 5, 6 == 9, 6, 9)返回9。
以下是上面几个函数的例子程序。本例子可以在本手册自带的示例代码所在目录中的numbers, strings and arrays子目录中的examples.mfps文件中找到):
Help
@language:
test logic operation functions
@end
@language:simplified_chinese
测试逻辑函数
@end
Endh
function testLogic()
print("\n and(True, 3>2, 1-1) = " + and(True, 3>2, 1-1))
print("\n and(True, 3>2, 1-2) = " + and(True, 3>2, 1-2))
print("\n or(True, 3>2, 1-1) = " + or(True, 3>2, 1-1))
print("\n or(False, 3<2, 1-1) = " + or(False, 3<2, 1-1))
print("\n 7&8 = " + (7&8)) // result is 0 (结果为0)
print("\n and(7, 8) = " + and(7, 8)) // result is true (结果为true)
print("\n iff(true, 3, 2) = " + iff(true, 3, 2))
print("\n iff(3 < 2, 3, 2) = " + iff(3 < 2, 3, 2))
print("\n iff(3 < 2, 3, 5 > 4, 5, 6 == 9, 6, 9) = " _
+ iff(3 < 2, 3, 5 > 4, 5, 6 == 9, 6, 9))
print("\n iff(3 < 2, 3, 5 < 4, 5, 6 == 9, 6, 9) = " _
+ iff(3 < 2, 3, 5 < 4, 5, 6 == 9, 6, 9))
endf
函数打印输出的结果为:
and(True, 3>2, 1-1) = FALSE
and(True, 3>2, 1-2) = TRUE
or(True, 3>2, 1-1) = TRUE
or(False, 3<2, 1-1) = FALSE
7&8 = 0
and(7, 8) = TRUE
iff(true, 3, 2) = 3
iff(3 < 2, 3, 2) = 2
iff(3 < 2, 3, 5 > 4, 5, 6 == 9, 6, 9) = 5
iff(3 < 2, 3, 5 < 4, 5, 6 == 9, 6, 9) = 9
4. MFP对复数的操作函数
众所周知,复数包括实部和虚部,也可以表示为幅值和幅角。MFP提供了4个函数:real,image,abs和angle用于分别返回一个复数的实部、虚部、幅值和幅角。注意abs也可以返回一个实数的绝对值。
比如real(-3+2i)返回-3,image(-3+2*i)返回2(注意返回的是虚部的实数值,如果要返回虚数值,需要增加一个参数true,比如image(-3+2*I, true)返回2i),abs(-3+2*i)返回3.60555128,angle(-3+2*i)返回2.55359005(基于弧度)。
以下是上面几个函数的例子程序。本例子可以在本手册自带的示例代码所在目录中的numbers, strings and arrays子目录中的examples.mfps文件中找到):
Help
@language:
test complex functions
@end
@language:simplified_chinese
测试复数操作函数
@end
Endh
function testComplexFuncs()
print("\nreal(-3+2*i) = " + real(-3+2i))
print("\nimage(-3+2*i) = " + image(-3+2*i))
print("\nabs(-3+2*i) = " + abs(-3+2 * i))
print("\nangle(-3+2*i) = " + angle(-3+2i))
endf
函数打印输出的结果为:
real(-3+2*i) = -3
image(-3+2*i) = 2
abs(-3+2*i) = 3.6055512754639893469033040673821233212947845458984375
angle(-3+2*i) = 2.5535900500422257345460612287910790449857054711524495709749445923
第2节 MFP对字符串的操作函数
对字符串的操作包括定位字符串中的一个或多个字符,将几个字符串连接起来,或者将一个字符串分解,等等。
首先,用户可能会想要知道一个字符串中间包含多少个字符。这时可以调用strlen函数,比如strlen("abcdefg!")会返回8,表示里面有8个字符。
然后,用户可能会想知道其中第3个和第四个字符是什么,这时,就要用到strsub函数。strsub(str, start, end)返回字符串str的子字符串。该子字符串从字符start开始到字符end-1。注意字符串的第一个字符是0号字符。这时,调用strsub("abcdefg!", 2, 4)得到一个新的字符串"cd",它包含两个字符,第一个是原字符串的第3个字符(索引号为2),第二个是原字符串的第4个字符(索引号为3)。
注意,MFP没有字符数据类型,只有字符串类型,所以,哪怕用户只返回一个字符,返回的数据仍然是一个字符串,比如strsub("abcdefg!", 2, 3)返回字符串"c"。
如果用户想返回从原字符串的某个字符开始,到原字符串的尾部的所有字符,则strsub的第三个参数可以省略,比如strsub("abcdefg!", 2)得到"cdefg!"。
如果strsub的第二个参数或者第三个参数超出了字符串的范围,则会报错,比如运行strsub("abcdefg!", 2, 10)会得到出错提示:Invalid parameter range!。
如果用户想把若干个(不少于2个)字符串顺次连接起来,则可以调用strcat函数,比如strcat("abc","hello", " 1,3,4")将三个参数字符串连接起来返回字符串"abchello 1,3,4"。也可以直接使用加号,也就是"abc"+"hello"+ " 1,3,4",也会得到同样的结果。
如果用户想把字符串切割为若干个子字符串,则应该调用split函数。split(string_input, string_regex)将字符串string_input按照正则表达式string_regex分割为若干个子字符串并返回包含所有子字符串的数 组。正则表达式可以非常复杂,用户需要阅读JAVA语言的Pattern类和String.split函数的帮助文档获得更多关于正则表达式使用方法的信息。在这里给出几个例子分别包括将一个字符串用空格来分割,用冒号来分割,用字母来分割和用逗号来分割。
比如,split(" ab kkk\t6\nd", "\\s+")会返回一个字符串数组["", "ab", "kkk", "6", "d"]。这是因为,"\\s+"是基于空格分割的正则表达式,而原字符串中,包括普通的空格,\t也就是缩进符,\n也就是换行符,它们都被看作空格的一种,所以,都作为切割标志。另外还要注意,在这个例子中ab和kkk之间的空格不止一个,但是,由于分割的正则表达式里面有个+号,相连的空格会被当作单一的切割标志。
再比如,split("boo:and:foo", ":")返回["boo", "and", "foo"]以及split("boo:and:foo", "o")返回["b", "", ":and:f"],在这里,由于分割的正则表达式里面没有+号,所以,相连的o不会被当作单一的切割标志,所以,boo:会被切割为"b"、""、":…"。
还比如,split(",Hello,world,", ",")则使用逗号作为切割符,返回["","Hello", "world"],第一个逗号前面没有内容,切割返回空字符串,与此对比最后一个逗号后面没有内容,却被忽略了,这是用户需要注意的。
除了上述基本的几个字符串函数,还有一些其他的函数用于实现对字符串复杂的操作,比如, trim_left,trim_right和trim函数分别去除参数字符串左边,右边和两边的空白字符(包括换行符和缩进符)并返回新字符串,例如Trim("\t \tabc def \n ")返回字符串"abc def"。
用户还可以对字符串进行一些转换操作,比如to_lowercase_string将参数字符串中所有的大写字母转化为小写字母,to_uppercase_string将参数字符串中所有的小写字母转化为大写字母,而to_string返回参数(可以是任何数据类型)的打印值(也是一个字符串),比如to_lowercase_string("abEfg")返回"abefg",而to_string(123)返回字符串"123"。
用户也可以对两个字符串的部分或全部内容进行比较。比如,strcmp(src, dest, src_start, src_end, dest_start, dest_end)比较源字符串src(从src_start到src_end)和目标字符串dest(从dest_start到dest_end)。如 果src和dest相等返回0,如果src大于dest返回大于0的值,如果src小于dest返回小于0的值。注意字符串索引从0开始,src_end 和dest_end的索引位置为最后一个被选中字符的索引位置加一。另外,最后四个参数可以省略,如果被省略,src_start和dest_start 的缺省值为0,src_end和dest_end的缺省值为对应字符串的长度。
而stricmp(src, dest, src_start, src_end, dest_start, dest_end)在忽略字母大小写的前提下比较源字符串src(从src_start到src_end)和目标字符串dest(从dest_start 到dest_end)。如果src和dest相等返回0,如果src大于dest返回大于0的值,如果src小于dest返回小于0的值。注意字符串索引 从0开始,src_end和dest_end的索引位置为最后一个被选中字符的索引位置加一。另外,最后四个参数可以省略,如果被省 略,src_start和dest_start的缺省值为0,src_end和dest_end的缺省值为对应字符串的长度。
字符串比较函数的例子包括
stricmp("abc","ABc")得到0,原因是在忽略大小写的前提下,"abc"和"ABc"相同。
strcmp("abcdefgk", "defik", 5, 8, 2, 5)则比较"abcdefgk"的第6,7,8个字符和"defik"的第3,4,5个字符,也就是比较"fgk"和"fik",所以返回-2,也就是第一个字符串小于第二个字符串的意思。
最后在这里介绍两个函数,conv_ints_to_str和conv_str_to_ints,第一个函数用于将一组整数转换成一个Unicode字符串,每一个整数分别对应字符串中的一个字符。用户也可以仅仅输入一个单一的整数,则该函数将这个单一的整数转化为包含一个Unicode字符的字符串。第二个函数则用于将一个Unicode字符串转换成一个整数数组。通常,一个Unicode字符对应一个整数(但也有可能对应两个整数,如果该Unicode字符超出了UTF-16字符集的范围。但这种情况很少出现)。由于汉字和一些常用的特殊符号比如¥∑⑨都是Unicode符号,调用这两个函数,可以帮助用户在程序中输入输出这些符号,比如:
conv_str_to_ints("中文汉字¥∑⑨")会返回[20013, 25991, 27721, 23383, 165, 8721, 9320]
而利用conv_str_to_ints函数的返回值作为conv_ints_to_str的参数,我们得到
conv_ints_to_str([20013, 25991, 27721, 23383, 165, 8721, 9320])返回"中文汉字¥∑⑨"。
以下是上面几个函数的例子程序。本例子可以在本手册自带的示例代码所在目录中的numbers, strings and arrays子目录中的examples.mfps文件中找到):
Help
@language:
test string functions
@end
@language:simplified_chinese
测试字符串操作函数
@end
Endh
function testString()
print("\nstrlen(\"abcdefg!\") = " + strlen("abcdefg!"))
print("\nstrsub(\"abcdefg!\", 2, 4) = " + strsub("abcdefg!", 2, 4))
print("\nstrsub(\"abcdefg!\", 2, 3) = " + strsub("abcdefg!", 2, 3))
print("\nstrsub(\"abcdefg!\", 2) = " + strsub("abcdefg!", 2))
print("\nstrcat(\"abc\",\"hello\", \" 1,3,4\") = " _
+ strcat("abc","hello", " 1,3,4"))
print("\nsplit(\" ab kkk\\t6\\nd\", \"\\\\s+\") = " _
+ split(" ab kkk\t6\nd", "\\s+"))
print("\nsplit(\"boo:and:foo\", \":\") = " _
+ split("boo:and:foo", ":"))
print("\nsplit(\"boo:and:foo\", \"o\") = " _
+ split("boo:and:foo", "o"))
print("\nsplit(\",Hello,world,\", \",\") = " _
+ split(",Hello,world,", ","))
print("\nTrim(\"\\t \\tabc def \\n \") = " _
+ Trim("\t \tabc def \n "))
print("\nto_lowercase_string(\"abEfg\") = " _
+ to_lowercase_string("abEfg"))
print("\nto_string(123) = " + to_string(123))
print("\nstricmp(\"abc\",\"ABc\") = " + stricmp("abc","ABc"))
print("\nstrcmp(\"abcdefgk\", \"defik\", 5, 8, 2, 5) = " _
+ strcmp("abcdefgk", "defik", 5, 8, 2, 5))
print("\nconv_str_to_ints(\"中文汉字¥∑⑨\") = " _
+ conv_str_to_ints("中文汉字¥∑⑨"))
print("\nconv_ints_to_str([20013, 25991, 27721, 23383, 165, 8721, 9320]) = " _
+ conv_ints_to_str([20013, 25991, 27721, 23383, 165, 8721, 9320]))
endf
测试程序输出结果为:
strlen("abcdefg!") = 8
strsub("abcdefg!", 2, 4) = cd
strsub("abcdefg!", 2, 3) = c
strsub("abcdefg!", 2) = cdefg!
strcat("abc","hello", " 1,3,4") = abchello 1,3,4
split(" ab kkk\t6\nd", "\\s+") = ["", "ab", "kkk", "6", "d"]
split("boo:and:foo", ":") = ["boo", "and", "foo"]
split("boo:and:foo", "o") = ["b", "", ":and:f"]
split(",Hello,world,", ",") = ["", "Hello", "world"]
Trim("\t \tabc def \n ") = abc def
to_lowercase_string("abEfg") = abefg
to_string(123) = 123
stricmp("abc","ABc") = 0
strcmp("abcdefgk", "defik", 5, 8, 2, 5) = -2
conv_str_to_ints("中文汉字¥∑⑨") = [20013, 25991, 27721, 23383, 165, 8721, 9320]
conv_ints_to_str([20013, 25991, 27721, 23383, 165, 8721, 9320]) = 中文汉字¥∑⑨
第3节 MFP对数组和矩阵的操作函数
数组和矩阵是MFP数据类型的一个重要组成部分。数组中包括若干个元素,每个元素可以是数,字符串或者数组。数组支持基本的MFP操作符加减乘除转置以及整数次方,此外,软件还提供了一系列的函数用于操作数组和矩阵。
1. MFP创建数组的函数
首先需要了解的是如何创建一个数组。MFP提供了一些函数。第一个是alloc_array函数。Alloc_array函数接受一个或多个参数,如果参数多于一个,每个参数都必须为正整数,表示生成数组的在每一个对应维度上的尺寸。如果参数只有一个,并且是一个数组,那么数组中的每一个元素都必须为正整数,表示生成数组的在每一个对应维度上的尺寸。生成的数组,每一个元素都被初始化为0。比如alloc_array(3)返回[0, 0, 0],而alloc_array(2,3,4)和alloc_array([2,3,4])都返回[[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]]。
如果用户希望alloc_array返回的数组,每个元素不是被初始化为0,而是其他的某个值,则需要使用alloc_array的另外一个用法:alloc_array(x,y),其中x是一个数组,x中的每一个元素都必须为正整数,表示生成数组的在每一个对应维度上的尺寸,y表示所有元素的初始值。比如调用alloc_array([2,1],"hello")得到[["hello"], ["hello"]]。
第二是eye,ones和zeros函数。eye(x)返回正整数x乘x的2维方阵I。注意表达式eye(0)返回常数1。
ones函数返回一个所有元素都是1的矩阵,和alloc_array相似,本函数的参数用于决定矩阵的尺寸,要么为一批正整数,要么为一个正整数数列。
zeros函数返回一个所有元素都是0的矩阵,和alloc_array相似,本函数的参数用于决定矩阵的尺寸,要么为一批正整数,要么为一个正整数数列。
以下是上面几个函数的例子程序。本例子可以在本手册自带的示例代码所在目录中的numbers, strings and arrays子目录中的examples.mfps文件中找到):
Help
@language:
test array construction functions
@end
@language:simplified_chinese
测试创建数组的函数
@end
Endh
function createArray()
print("\nalloc_array(3) = " + alloc_array(3))
print("\nalloc_array(2,3,4) = " + alloc_array(2,3,4))
print("\nalloc_array([2,3,4]) = " + alloc_array([2,3,4]))
print("\nalloc_array([2,1],\"hello\") = " + alloc_array([2,1],"hello"))
print("\neye(3) = " + eye(3))
print("\nones(2,3) = " + ones(2,3))
print("\nzeros([2,3]) = " + zeros([2,3]))
endf
上述函数的运行结果如下:
alloc_array(3) = [0, 0, 0]
alloc_array(2,3,4) = [[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]]
alloc_array([2,3,4]) = [[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]], [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]]
alloc_array([2,1],"hello") = [["hello"], ["hello"]]
eye(3) = [[1, 0, 0], [0, 1, 0], [0, 0, 1]]
ones(2,3) = [[1, 1, 1], [1, 1, 1]]
zeros([2,3]) = [[0, 0, 0], [0, 0, 0]]
2. 获取数组的尺寸和判断数组的特征
对数组进行操作的第二步是获取一个已有数组的尺寸,这需要调用size函数。size函数有两种使用方法,第一种,只接受一个参数,也就是所操作的数组矩阵:size(x)返回矩阵x的尺寸向量。注意如果x不是一个矩阵,则总是返回[]。注意,这里的尺寸向量是指每个维度的最大的尺寸向量,比如[1, 2+3i, [5, "hello", [9, 10], 6], 11, 12]包括5个元素,分别为1,2+3i,[5, "hello", [9, 10], 6],11和12,所以,第一维的尺寸为5,第二维,元素1,2+3i,11和12的 尺寸都是[](这里,任何复数都被视作单一元素),唯有[5, "hello", [9, 10], 6]是一个数组包含4个元素,所以第二维的尺寸为4,第三维,元素5,"hello"和6都是单个的元素(这里,任何字符串都被视为单一元素),尺寸都是[],唯有[9, 10]是一个包含两个元素的数组,所以第三维尺寸为2。所以,最终我们得到size([1, 2+3i, [5, "hello", [9, 10], 6], 11, 12])==[5, 4, 2]。
size函数的第二种是用办法接受两个参数:size(x,y)返回矩阵x前y维的尺寸向量,如果x少于y维,返回完整的尺寸向量,注意y必须为正整数。此外如果x不是一个矩阵,则总是返回[]。类似上面的例子,如果调用
size([1, 2+3i, [5, "hello", [9, 10], 6], 11, 12], 2)
获取数组([1, 2+3i, [5, "hello", [9, 10], 6], 11, 12]尺寸的前两维会得到返回值为[5,4]。
对数组或矩阵进行操作的第三步是判断数组矩阵的特性。用户可使用is_eye(x)函数判断参数x是否是一个I矩阵(单位矩阵);
用户可以使用is_zeros(x)函数判断参数x是否是一个元素全为0的矩阵;
用户还可以使用includes_inf(x),includes_nan(x),includes_null(x),includes_nan_or_inf(x)和includes_nan_or_inf_or_null(x)来分别判断参数x是否为一个包含值为inf或-inf的元素的数组,是否为一个包含值为nan的元素的数组,是否为一个包含值为null的元素的数组,是否为一个包含值为nan或者inf或-inf的元素的数组,以及是否为一个包含值为nan或者inf或-inf或者null的元素的数组。
以下是上面几个函数的例子程序。本例子可以在本手册自带的示例代码所在目录中的numbers, strings and arrays子目录中的examples.mfps文件中找到):
Help
@language:
acquire array's properties functions
@end
@language:simplified_chinese
获取数组特性的函数
@end
Endh
function getArrayProperty()
print("\nsize([1, 2+3i, [5, \"hello\", [9, 10], 6], 11, 12]) = " _
+ size([1, 2+3i, [5, "hello", [9, 10], 6], 11, 12]))
print("\nsize([1, 2+3i, [5, \"hello\", [9, 10], 6], 11, 12], 2) = " _
+ size([1, 2+3i, [5, "hello", [9, 10], 6], 11, 12], 2))
print("\nis_eye([[1,1],[0,1]]) = " + is_eye([[1,1],[0,1]]))
print("\nis_zeros([[0,0],0]) = " + is_zeros([[0,0],0]))
print("\nincludes_nan_or_inf([5, [3, -inf], \"hello\"]) = " _
+ includes_nan_or_inf([5, [3, -inf], "hello"]))
print("\nincludes_null([5, [3, -inf], \"hello\"]) = " _
+ includes_null([5, [3, -inf], "hello"]))
Endf
上述函数的运行结果如下:
size([1, 2+3i, [5, "hello", [9, 10], 6], 11, 12]) = [5, 4, 2]
size([1, 2+3i, [5, "hello", [9, 10], 6], 11, 12], 2) = [5, 4]
is_eye([[1,1],[0,1]]) = FALSE
is_zeros([[0,0],0]) = TRUE
includes_nan_or_inf([5, [3, -inf], "hello"]) = TRUE
includes_null([5, [3, -inf], "hello"]) = FALSE
3. 对数组赋值
对数组进行操作的第四步是对数组赋值,显然,用户可以用对变量复制的常规办法把一个数组赋值给一个变量,比如:
Variable a = [1,2,3]
a[1] = [7,9]
。但是,很多时候,用户需要在程序中间动态地给数组赋值,并且被赋予的值位置超过了数组的范围,比如上面,在将a[1]赋值为[7,9]之后,用户还想将a[4]赋值为3+6i,但是数组a不存在a[4]这个元素,这个时候,就需要调用set_array_elem函数。set_array_elem(x,y,z)将x[y]赋值为z,并且返回新的x。注意x不是必须为矩阵,y必须为正整数向量。y的值可以超出x的尺寸和 维度。比如,如果x=3,y=[1,2],z=2+3i,那么set_array_elem(x,y,z)等于[3, [0, 0, 2+3i]]。还要注意,调用了set_array_elem函数之后,x的值可能会自动变为新值,也可能不会。所以,必须将 set_array_elem的返回值赋予x,以保证x的值得到更新。所以,set_array_elem的正确的调用方法是
x = set_array_elem(x, y, z)
。回到上面的例子,如果想将a[4]赋值为3+6i,办法是
a = set_array_elem(a, 4, 3+6i)
,运行完此语句之后,得到a的值为[1, [7, 9], 3, 0, 3 + 6 * i]。a[3]原本也不存在,但由于需要生成a[4],所以a[3]的值也被自动生成并赋值为0
用户在建立自己的函数的时候,有时需要用数组作为参数,在函数体内,又调用了set_array_elem或者赋值语句给数组的一部分赋值,这个时候需要注意,数组在子程序中发生了变化,主程序中也会看到。比如,有个子程序的定义为
function subfunc1(array_value)
…
Variable my_array = Array_value
my_array[2] = 7
…
Endf
现在用户在主程序中调用::mfpexample::subfunc1,代码如下
…
variable array_val = [1,2,3]
::mfpexample::subfunc1(array_val)
print(array_val)
…
在运行完print(array_val)后,用户会发现,array_val变成了[1, 2, 7]了。
之所以会这样,是因为,MFP在传递数组参数时,不是把数组整个拷贝到子程序的栈中,而是数组的引用拷贝到子程序的栈中。这样一来,子程序和主程序实际上操作的是同一个数组。在上述代码中,子程序又将参数数组赋值给一个新的变量,但是,数组的赋值也仅仅只是引用的拷贝,最终,子程序中数组的改变也造成了主程序中相应的改变。
那么,如果用户需要在子程序中对数组的值加以改变,但又不希望主程序受影响,该怎么办呢?这时可以调用clone函数。这个函数接受一个参数,该参数可以为任何数据类型,包括数,数组和字符串,该函数将参数的值拷贝并返回,返回值和参数值虽然在数值上相同,但在内存中保存在不同的地方,不会相互影响,比如上面的例子,我们将子程序改为
function subfunc2(array_value)
…
Variable my_array = clone(Array_value)
my_array[2] = 7
…
Endf
现在用户在主程序中调用::mfpexample::subfunc2,代码如下
…
variable array_val = [1,2,3]
::mfpexample::subfunc1(array_val)
print(array_val)
…
在运行完print(array_val)后,用户会发现,array_val还是[1, 2, 3]。
以下是对数组赋值和通过参数传递数组的例子程序。本例子可以在本手册自带的示例代码所在目录中的numbers, strings and arrays子目录中的examples.mfps文件中找到):
function subfunc1(array_value)
Variable my_array = Array_value
my_array[2] = 7
Endf
function subfunc2(array_value)
Variable my_array = clone(Array_value)
my_array[2] = 7
Endf
function assignValue2Array()
variable array_val = [1,2,3]
print("\narray_val's initial value is " + array_val)
::mfpexample::subfunc2(array_val)
// clone function called in ::mfpexample::subfunc2, any change inside
// will not affect main function.
// 由于clone函数被调用,子函数改变数组参数的值不会对主函数有影响。
print("\nWith clone, after calling sub function array_val is " + _
array_val)
::mfpexample::subfunc1(array_val)
// clone function not called in ::mfpexample::subfunc2, value changes
// of array_val inside will affect main function.
// 由于clone函数没有被调用,子函数改变数组参数的值会对主函数有影响。
print("\nWithout clone, after calling sub function array_val is " _
+ array_val)
array_val = set_array_elem(array_val, [4], -5.44-6.78i)
// array_val now has 5 elements after calling set_array_elem
// 在调用set_array_elem函数后,array_val有5个元素了。
print("\nAfter set_array_elem array_val is " + array_val)
endf
用户在命令提示符下运行::mfpexample::assignValue2Array(),或者先输入using citingspace ::mfpexample,然后再执行assignValue2Array(),结果如下:
array_val's initial value is [1, 2, 3]
With clone, after calling sub function array_val is [1, 2, 3]
Without clone, after calling sub function array_val is [1, 2, 7]
After set_array_elem array_val is [1, 2, 7, 0, -5.44 - 6.78i]
小结
本章详细说明了MFP编程语言对实数,复数,数组以及字符串的操作办法,MFP编程语言对复数,数组和字符串有内建的支持,但是,为了实现各种复杂的功能,仍然需要调用函数。MFP编程语言提供了一整套的对浮点数四舍五入的函数,一整套进制转换函数,一组存取复数实部和虚部的函数,一整套获取字符串信息(如长度,子字符串等)的函数以及一组操作数组的函数(包括创建,获取尺寸和赋值)。
需要注意的是,对MFP编程语言来说,数组和矩阵是两个相关但并不完全一样的概念。MFP的矩阵多半是指二维方阵,而数组的维度是任意的,每一组的长度也不一定要一样。矩阵是一种特殊的数组。本手册在后面详细介绍使用矩阵的数学计算函数。