可编程科学计算器1.8.0.79发布了,请到酷安网下载
mfp吧
全部回复
仅看楼主
level 1
woshiwppaa 楼主
在这个最新版本中,可编程科学计算器进一步完善了并行计算的功能,修正了一系列的bug。现在用户可以在任意一台支持MFP语言的设备上(该设备运行基于JAVA的可编程科学计算器,基于安卓的可编程科学计算器,或者可编程科学计算器编译生成的安卓应用),发送一段代码给另外一台支持MFP语言的设备执行。第二台设备同样行基于JAVA的可编程科学计算器,基于安卓的可编程科学计算器,或者可编程科学计算器编译生成的安卓应用,注意第一台和第二台设备完全可以运行基于不同的平台的可编程科学计算器,或者一台运行可编程科学计算器,另一台运行可编程科学计算器编译生成的安卓应用,或者两台均运行可编程科学计算器编译生成的安卓应用。但需要注意的是两台设备的可编程科学计算器版本均必须为1.8.0.79或以上,如果运行的是可编程科学计算器编译生成的安卓应用,该安卓应用必须由可编程科学计算器1.8.0.79或以上版本编译生成。另外,在当前,下层通信协议仅仅支持TCPIP。
MFP语言实现并行计算是通过call ... endcall语句。call和endcall语句定义了MFP语言call程序块的边界。call程序块是一段不在本进程中而是在别的进程中执行的指令。call语句是call程序块的开始。在call语句中,call关键字后面紧跟着连接对象,然后是on关键字,最后是一串call程序块的参数变量。call程序块的参数变量都是在call语句之前就已经声明的普通的变量。每一个参数变量都只能被一个call语句所使用。endcall语句标志着call程序块的中止。endcall语句有一个可选参数。该参数是call程序块在本地进程的返回变量。返回变量也是在call程序块之前声明的普通变量。返回变量只能被一个call程序块所使用而且不能同时作为call程序块的参数变量。
我们可以把一个call语句看作是一个函数的开始。与普通函数不同的是,call程序块并非是在本地进程中运行,而是在另外一个进程中运行。运行call程序块的进程可以是本机,也可以是另外一台设备。运行call程序块的进程通过call语句中的连接对象和本地进程连接。运行call程序块的进程可以看到call程序块参数变量的值的变化,也可以修改call程序块参数变量的值。call程序块对自己的参数变量的值的修改会反应到本地进程。但是需要注意的是,MFP语言并不保证本地进程和call程序块进程对程序块参数变量的值的修改会被实时同步到对方,也不保证按修改的顺序进行同步传递。MFP语言唯一保证的是在一个进程内对一个call程序块参数变量的修改是原子性的,也就是只有上一次修改完成了之后,对值的新的修改,不管是来自客户端还是服务器端,才能开始。需要注意的是这个原子性只是应用于一个进程。由于call程序块进程和本地进程都有一份程序块参数变量的拷贝,对于一个程序块参数变量的两份拷贝在不同的进程中同时进行修改不违背修改的原子性。当call程序块遇到endcall语句或者return语句时停止运行并返回。如果return语句返回一个值,位于本地进程的endcall语句将收到返回值并将返回值赋给call程序块在本地进程的返回变量(如果endcall语句声明了返回变量的话)。
需要注意的是,不同于call程序块的参数变量,call程序块的返回变量采用的是阻塞模式。换句话说,当call程序块被发送到远端执行后,任何在本地进程读取call程序块的返回变量的值的语句都将被阻塞,直到call程序块返回(不管有没有返回值)为止。
以下是call程序块的一个例子:
variable local_interface, remote_interface, ret
local_interface = ::mfp::paracomp::connect::generate_interface("TCPIP", "192.168.1.101")//客户端(本地进程)地址
ret = ::mfp::paracomp::connect::initialize_local(local_interface, false)
print("initialize_local ret = " + ret + "\n")
remote_interface = ::mfp::paracomp::connect::generate_interface("TCPIP", "192.168.1.107")//服务器端(运行call程序块的进程)地址
ret = ::mfp::paracomp::connect::connect(local_interface, remote_interface)//从客户端连接到服务器端
print("connect ret = " + ret + "\n")
//connect函数的返回值是一个基于数组的字典,"CONNECT"关键字所对应的就是连接对象的定义。如果connect函数失败,"CONNECT"关键字对应的值为NULL。
variable conn = ::mfp::data_struct::array_based::get_value_from_abdict(ret, "CONNECT")
variable a = "hekko, 48", b = 3+7i, c=["LCH"]
call conn on a, b
a = 88
b = "KIL"
print("Hello ....\n")
return 54
endcall c
//我们必须先取回c的值。c的值能够取回方才意味着call程序块已经返回。
print("c = " + c)
//当c的值取回之后,我们可以打印出a和b的值。可以看到这时a和b的值已经发生了更改。如果我们在print("c = " + c)语句之前打印a和b的值,
//我们可能无法观察到a和b的值发生了变化
print("a = " + a + " b = " + b)
close_out_connection(local_interface, remote_interface)//关闭连接
close_local(local_interface, false)//关闭本地通信协议界面
以上代码是由客户端进程所执行,在服务器端,我们需要运行以下代码接收连接请求并运行call程序块:
variable local_interface, ret
local_interface = ::mfp::paracomp::connect::generate_interface("TCPIP", "192.168.1.107")//服务器端(运行call程序块的进程)地址
ret = ::mfp::paracomp::connect::initialize_local(local_interface, true)
print("initialize_local ret = " + ret + "\n")
//监听连接请求。监听线程将在后台工作。
ret = ::mfp::paracomp::connect::listen(local_interface)
print("listen ret = " + ret + "\n")
//下面这条input语句将阻塞程序的运行。如果服务端代码是一个简单的MFPS脚本并且是在bash或者Windows命令提示符中运行,input语句可以阻止服务器程序的退出所以是必
//不可少的。但是如果是在安卓或者MFP语言的JAVA界面程序中运行,只要安卓应用或JAVA界面程序不退出input语句就是不必要的。因为这种情况下服务器端的进程并没有中止。
input("Press any key to exit\n", "S")
先运行以上服务器端代码,然后在不同的设备中运行客户端代码。在运行之前需要确保客户端和服务器端的地址是正确的。开发者可以看到在服务器端一个Hello ....消息被打印出来。在客户端变量c的新值是一个基于数组的字典,call程序块的返回值54位于该字典中。变量a和b的值也都被更新了。
2020年01月27日 04点01分 1
level 4
这个吧里面竟然还有人!
2020年01月27日 07点01分 2
level 1
woshiwppaa 楼主
如何通过可编程科学计算器在另一个设备中运行本机MFP语言编写的游戏
MFP语言的一个重要功能就是通过call ... endcall语句实现对并行计算的支持。MFP语言实现多线互不干扰并行计算的基本原理是采用沙盒机制。每一个MFP沙盒拥有完整一套所在设备的MFP系统库,但是自己用户代码库和用户资源库则是独立的,服务端会为一个从客户端传来的call程序块建立一个MFP沙盒。程序块所在的代码文件和程序块调用的用户代码所在文件会保存在沙盒的用户代码库目录下,所需要的资源文件会保存在沙盒的用户资源库目录下。各个MFP沙盒的程序栈也都是独立的。这样就保证了各个MFP沙盒之间不会相互影响,沙盒和MFP服务端本地程序也不会相互影响。
在帮助中已经有call ... endcall语句的详细例子,但是那是客户端设备发送一段算法去服务端设备。如果客户端想要发送一个游戏去服务端设备并在服务端设备运行,该如何实现呢?
游戏和普通算法的区别在于,游戏需要支持视频和音频,所以有很多资源文件(比如,图像文件,数据库文件,声音文件等等)。在其他方面,游戏和普通算法是一样的。所以关键就在于客户端的MFP语言如何将资源文件传到服务端,而服务端的MFP语言(它和客户端MFP语言运行的是同样的代码)如何保存并调用这些资源文件,同时不影响在服务端运行的其它客户端的call程序块或者服务端自己正在运行的程序。
以下代码给出了一个例子,在这段代码需要使用一个叫做food.png的图像文件。在可编程科学计算器上(不论是基于安卓的还是基于JAVA的),该图像文件和这段代码所在的MFP脚本文件位于同一个目录。
@build_asset copy_to_resource(get_upper_level_path(get_src_file_path()) + "food.png", "images/food.png")
if is_sandbox_session()
foodImage = load_image(get_sandbox_session_resource_path() + "images/food.png")
elseif is_mfp_app()
foodImage = load_image_from_zip(get_asset_file_path("resource"), "images/food.png", 1)
else
foodImage = load_image(get_upper_level_path(get_src_file_path()) + "food.png")
endif
如果我们将这段代码编译到一个APK安装包中,我们需要把food.png图片拷贝到APK的asset的resource.zip文件中。安装了APK之后,在运行MFP应用时,我们需要调用load_image_from_zip从APK的asset的resource.zip文件中读出图片。为了实现上述过程,我们加入了一个annotation语句:
@build_asset copy_to_resource(get_upper_level_path(get_src_file_path()) + "food.png", "images/food.png")
,告诉MFP编译器将位于代码所在目录的food.png文件拷贝到APK的asset的resource.zip文件中,在resource.zip文件中的具体位置是images/food.png。
如果我们不是编译安装包,而是需要把这段代码发送到另外一个设备上去执行,@build_asset同样会发挥作用。如果客户端是可编程科学计算器,@build_asset会告诉call语句把位于代码所在目录的food.png文件传送到服务端MFP沙盒的resource目录中,具体位置是resource目录中的image子目录的food.png文件。如果客户端是一个通过编译出的APK安装的MFP应用,由于MFP应用已经将food.png保存在asset的resource.zip文件中的images目录下,call语句会从安卓的asset中提取出food.png然后传送到服务端MFP沙盒的resource目录中,具体位置仍然是resource目录中的image子目录的food.png文件。
在运行上述代码时,我们需要根据MFP所在的环境执行不同的指令。如果MFP是在沙盒中运行,也就是服务端执行客户端的call程序块,那么资源文件是保存在本地存储器上的,读取图像文件时我们用load_image并指定路径即可。我们通过函数is_sandbox_session()返回true来断定MFP是在沙盒中运行,通过调用函数get_sandbox_session_resource_path()获取沙盒用户资源库所在目录。
如果MFP是在编译安装后的MFP应用中运行,资源文件保存在安卓的asset中的resource.zip包内,所以我们需要调用load_image_from_zip函数。我们通过函数is_mfp_app()返回true来断定MFP是在编译安装后的MFP应用中运行,通过调用函数get_asset_file_path("resource")获取resource包在安卓asset中的具体位置。
如果MFP是在可编程科学计算器中运行,不论是基于JAVA还是安卓,资源文件都保存在本地存储器上,读取图像文件时我们用load_image并指定路径即可。food.png所在的具体目录可以通过调用get_upper_level_path(get_src_file_path())来定位。
如果读取的不是图像文件而是声音文件,上述代码的逻辑基本不变,区别就是调用play_sound或者play_sound_from_zip而不是load_image或者load_image_from_zip。
还需要注意的是,由于函数is_sandbox_session()和is_mfp_app()可能均返回true(比如服务端是一个MFP应用),开发者必须首先判断当前MFP是否在沙盒中运行,也就是说is_sandbox_session()条件语句必须放在最前面,其次才是is_mfp_app()条件语句。
将上述代码加入一个call程序块中,或者将上述代码编入一个运行游戏的函数然后在call程序块中调用,MFP就可以自动地替开发者传递代码和资源文件并在另一个设备中执行该游戏了。
1.8.0.79版的可编程科学计算器已经提供了这样一个例子。在服务器端,使用者选择“探索示例”,然后选择“并行计算例子”,然后选择“运行”,输入2,点击确定按钮,进入服务器模式,这时该程序会列出本设备所有的IP地址,使用者选择一个作为服务地址,然后回车(ENTER键,注意不是运行键)。在客户端设备上,同样地,使用者选择“探索示例”,然后选择“并行计算例子”,然后选择“运行”,输入3,点击确定按钮,进入客户端模式,这时该程序会列出本设备所有的IP地址,使用者选择一个作为客户端地址,然后回车(ENTER键,注意不是运行键),然后再输入刚才选择的服务器地址,然后再次ENTER键回车,客户端会将call程序块发送到服务端,然后在服务端运行超级超级小白兔游戏。不管服务器端的用户代码库中是不是已经有了该游戏均不影响该游戏正常运行,因为该游戏的代码和资源都是通过客户端发送过来的。
需要注意的是,服务器和客户端的IP地址最好位于同一个子网内,也就是中间没有NAT(Network Address Translation)层,如果做不到这一点,最少也必须做到通过客户端IP地址能够ping通服务端的IP地址,在现有的IPv4体系下,由于用户的设备(不管是客户端还是服务端)的IP地址往往被一重或多重NAT层所遮蔽,所以想要执行远程并行计算的难度比较大,但是如果多个设备在同一个内部网中,互联并行则毫无问题;未来IPv6体系下,NAT层不再会像现在这么大行其道,执行远程并行计算将不再是梦想。此外,开发者还在尝试引入NetOTC协议,真正实现远程点对点连接。总之,在编程语言级别上实现并行计算是一个全新思想,MFP语言作为全球第一种引入此思想的编程语言必然有着光明的前景。
2020年01月27日 11点01分 3
1