[English Version]

MFP语言和可编程科学计算器

MFP语言简介

operators

function

variable

if

while do for

break continue

select

try catch

class

call

citingspace

help

@compulsory_link

@execution_entry

@build_asset

MFP函数

部署用户自定义函数

在您的应用中调用MFP

创建安卓安装包

小游戏的开发

绘制图形

使用MFP进行数学分析

使用MFP处理文件

数,字符串和数组

日期时间和系统相关

可编程科学计算器介绍

MFP编程语言class和endclass语句:

* 类的声明

classendclass语句定义了MFP语言的类。class语句是类定义的开始。如果class语句中没有父类声明,class就是直接派生于MFP语言的最基本的object类型。这样的class语句的例子如下:

class Class_Name

反之,如果类是从一个或者多个父类中直接派生而来,class语句则如下面的例子所示:

class Class_Name: Super_Class1, Super_Class2, ..., Super_ClassN

这里,直接派生是指该类是父类的子类,而不是孙类,曾孙类甚至更后辈。

还需要注意,在class语句中,父类的名字是可以包含完整或者部分的引用空间路径的,比如:

class Class_Name: aaa::bbb::Super_Class1, ::ccc::Super_Class2, ..., Super_ClassN

上述语句是完全合法的。MFP将根据当前引用的所有引用空间以及它们的优先次序来找到每一个父类。

* 嵌套的类和类的成员

类可以定义在另外一个类的里面。这种类被称为嵌套的类。在这种情况下,被嵌入的类仅仅只相当于嵌套类的引用空间。比如,类A定义在引用空间::AAA::bbb中,而一个嵌套的类B定义在A中,则类A的完整引用空间路径为::AAA::bbb::A而类B的完整引用空间路径为::AAA::bbb::A::B。除了它们引用空间路径上的相似,嵌套类和被嵌入的类是相互独立的。此外,嵌套类总是对外部可见的。

类的成员则包括函数和变量。它们分为两大类,私有(使用private关键字,只有类成员函数可访问,外部不可见)和公有(使用public关键字,外部可见),比如:

public variable self memberA = 7, memberB = "Hello", memberC
private function memberFunc(a, b, c)
...
endf

需要注意的是,如果声明类成员时既没有使用private也没有使用public关键字,那么该成员被视为公有,也就是public

类成员变量的声明和普通变量的声明略有不同。首先,如上面指出的,类成员变量的声明语句前面可以加上private或者public关键字;其次,在variable关键字之后,必须加入一个self关键字。这个self关键字意思是该语句声明的变量不是静态变量。需要注意的是,在现阶段,MFP不支持静态变量,所以,如果类成员变量的声明语句没有self关键字将会被忽略;最后,和函数内的变量一样,类成员变量可以在声明语句中被初始化。但是,MFP只允许用存粹的值而不是函数来初始化类成员变量。比如:

variable self varA = [[1,2]]

是对的而

variable self varA = func(3,4)

则会出错。更多的类成员变量声明语句示例如下:

variable self varA, varB = "Hello", varC = [[1,2],[3,4]]
private variable self varD

和成员变量一样,类的成员函数声明语句前面可以加入public或者private关键字。此外,如果函数的第一个参数是self,这个函数就不是静态的,否则,这个函数就是静态的。在函数的内部,使用self关键字再加一个点可以访问类的成员,比如:

public function memberFunc(self, a, b, c)
	self.MemberA = a
	self.MemberB = b
	return self.MemberA * self.MemberB * self.memberFunc(c)
endf

如果类的成员函数是静态的,它显然就不能访问类的非静态成员。一个静态成员函数的例子如下:

public function memberStaticFunc(a, b, c)
	return a+b+c
endf

虽然self关键字可以用于访问类的成员和类的父类(包括父类的父类和更上辈的类)的公共(public)成员,但如果类和它的某些父类拥有同样名字的成员变量或者相同声明的成员函数,self关键字则只能够访问本类中的成员而无法访问父类中的成员。比如:

class SuperClassA
	public function memberFunc(self, a)
		return a
	endf
endclass
class SuperClassB
	public function memberFunc(self, a)
		return 2*a
	endf
endclass
class ChildClass : SuperClassA, SuperClassB
	public function memberFunc(self, a)
		return 3*a
	endf
	public function memberFunc1(self)
		return self.memberFunc(3)	// 调用ChildClass的memberFunc函数,而不是SuperClassA或者SuperClassB的对应函数。
	endf
endclass

为了访问父类成员,则必须使用super成员变量。这个成员变量是一个数组,第一个元素是第一个父类的对象,第二个元素是第二个父类的对象,...,以此类推。注意这里的对象都是切片的对象,也就是说通过super返回的父类的对象是本类对象的一部分。这样一来,如果在上面的示例中开发者想调用父类的memberFunc函数,代码应该这样写:

class SuperClassA
	public function memberFunc(self, a)
		return a
	endf
endclass
class SuperClassB
	public function memberFunc(self, a)
		return 2*a
	endf
endclass
class ChildClass : SuperClassA, SuperClassB
	public function memberFunc(self, a)
		return 3*a
	endf
	public function memberFunc1(self)
		variable x = self.super[0].memberFunc(3)	// 调用SuperClassA的memberFunc函数
		variable y = self.super[1].memberFunc(4)	// 调用SuperClassB的memberFunc函数
		return x + y
	endf
endclass

注意,如果一个类的声明语句没有包括任何父类,那么该类有一个唯一的父类,就是MFP语言的object类型。在这种情况下,self.super[0]返回被切片的object对象。

在MFP语言中,成员函数和成员变量均可以被覆写(override)。如果MFP通过self关键字访问一个成员(函数或者变量),而该成员已经被继承树上的多个类覆写,那么它总是访问的最“下层”的成员。比如,如果类A是从类B派生而来,A和B均有一个成员变量叫C,并且我们已经定义了如下函数:

function func(objOfClass)
	print(objOfClass.C)
endf

那么,如果一个A的对象作为参数被传入这个函数,那么A的成员变量C的值将会被打印出来。而如果一个B的对象作为参数被传入这个函数,那么B的成员变量C的值将会被打印出来。

但是,上面的规定有时候会给开发者带来困惑。比如,在上面的例子中类B有一个公共成员函数,它读取成员变量C的值。类B的开发者并不知道别的程序员会从类B派生出类A,所以他假定成员变量C一定是类B的成员变量C。但如果第三个开发者创建了一个类A的对象,并且调用了该对象从类B那里继承的读取成员变量C的值的成员函数,那么这时该成员函数实际上读取的是类A而不是类B的成员变量C的值。

class B
	variable self C = 1
	function printC(self)
		print("self.C = " + self.C + "\n")
	endf
endclass

class A : B
	variable self C = 2
endclass

function printABC()
	variable bObj = B(), aObj = A()
	bObj.printC()	// self.C = 1
	aObj.printC()	// self.C = 2
endf

如果类B的开发者想要确保在类B的成员函数中读取的是类B的成员变量C,那么就必须通过类的this成员变量访问成员变量C。所有的类都有一个this成员变量,该变量返回当前函数所在的类的一个(被切片的)的对象。一个例子如下:

class B
	variable self C = 1
	function printC(self)
		print("self.this.C = " + self.this.C + "\n")
	endf
endclass

class A : B
	variable self C = 2
endclass

function printABC()
	variable bObj = B(), aObj = A()
	bObj.printC()	// self.this.C = 1
	aObj.printC()	// self.this.C = 1
endf

和公共的super成员变量不同的是,this是私有的。

* 构造函数和魔术函数

如果开发者想要以一个MFP类作为模板创造一个对象,必须调用构造函数。和其他的编程语言不同,MFP类的构造函数是内置的,不能够开发者自定义,不能被重载,也不能够被覆写。构造函数没有参数。它所做的一切工作就是就是根据成员变量的声明初始化成员变量。如果一个成员变量在声明中没有给出初始值,那么它就会被初始化为NULL。构造函数返回值是该类的一个对象。构造函数的一个例子如下。在该例子中,类Abcd定义在引用空间::AAA::bbb中,这个类的构造函数就是Abcd(),包括引用空间路径的完整名称为::AAA::bbb::Abcd()。

citingspace ::AAA::bbb
class Abcd
	variable self a = 1, b = "Hello", c
	public function printMembers(self)
		print("self.a = " + self.a + " self.b = " + self.b + " self.c = " + self.c)
	endf
endclass

endcs

function printABC()
	variable obj = ::AAA::bbb::abcd()
	obj.printMembers()	// self.a = 1 self.b = Hello self.c = NULL
endf

由于开发者无法自定义构造函数,所以自定义的初始化步骤应该放在公共的成员函数中。这种成员函数的名称,返回类型和参数都可以由开发者来决定,但是MFP语言推荐使用__init__作为函数名,并且函数返回值为该对象自身。注意__init__只是一个普通的成员函数,而并非一个魔术函数,它可以被重载(overload),也可以在任何时候被多次调用。一个例子如下:

citingspace ::AAA::bbb
class Abcd
	variable self a = 1, b = "Hello", c
	public function printMembers(self)
		print("self.a = " + self.a + " self.b = " + self.b + " self.c = " + self.c)
	endf
	public function __init__(self)
		self.a = 7
		self.c = (3-i) * self.a
		return self
	endf
	public function __init__(self, a, b, c)	// __init__函数和其他用户自定义的普通成员函数一样,它可以被重载
		self.a = a
		self.b = b
		self.c = c
		return self
	endf
endclass

endcs

function printABC()
	using citingspace ::AAA::bbb
	variable obj = abcd().__init__()
	obj.__init__(3, 2, 1)
	obj.__init__([5,4],[2,3],"WWW").printMembers()	// self.a = [5, 4] self.b = [2, 3] self.c = WWW
endf

MFP的类还提供了一些内置的魔术函数。这些魔术函数可以被用户自定义相同声明的函数所覆写。函数__to_string__是最常用的魔术函数。这个函数吧一个对象转换为字符串。当把一个对象和字符串相加,或者以这个对象为参数调用MFP内置的to_string函数时,这个函数将会被调用。

函数__deep_copy__返回该对象的深度拷贝。当以这个对象为参数调用MFP内置的clone函数时该魔术函数将会被调用。

函数__equals__判断该对象是否和另外一个变量的值相等。当使用==操作符并且操作符的左操作数为该对象时该魔术函数将会被调用。

函数__hash__返回该对象的哈希值。当以这个对象为参数调用MFP内置的hash_code函数时该魔术函数将会被调用。

函数__copy__返回该对象的浅拷贝。它的默认行为是创建一个新的对象,但是该对象成员变量均引用旧的对象的对应值。

函数__is_same__判断该对象是否和另外一个变量的值相同(注意不是相等)。这个函数仅仅判断两个对象的引用是否一样。所以,实际上它也和未重载的操作符==的功能是一致的。这个函数在开发者覆写__equals__函数,需要直接比较引用时很有用。MFP不推荐覆写这个函数。

以下例子给出了上述函数的使用方法:

class SampleClass
	variable self a = 1, b = 2
	public function __equals__(self, o)
		print("User defined __equals__\n")
		if self.__is_same__(o) // 判断self和o是否指向同一个对象
			return true
		elseif null == o
			return false
		elseif get_type_fullname(self) != get_type_fullname(o)
			return false
		elseif or(self.a != o.a, self.b != o.b)
			return false
		else
			return true
		endif
	endf
	public function __to_string__(self)
		return "Class SampleClass, a = " + a + " b = " + b
	endf
	public function __hash__(self)
		print("User defined __hash__\n")
		return a + b * 19
	endf
	public function __copy__(self)
		print("User defined __copy__\n")
		return self
	endf
	public function __deep_copy__(self)
		print("User defined __deep_copy__\n")
		variable o = SampleClass()
		o.a = self.a
		o.b = self.b
		return o
	endf
endclass

function testOverriddenMagicFunctions()
	variable obj1 = SampleClass()
	print(obj1)	// 将会输出Class SampleClass, a = 1 b = 2
	variable obj2 = clone(obj1)	// 将会输出User defined __deep_copy__
	print("obj1 == obj2 is " + (obj1 == obj2))	// 将会先输出User defined __equals__,然后再输出obj1 == obj2 is true
	print(hash_code(obj2)) // 将会先输出User defined __hash__,然后再输出39
endf