自定义函数

阅读(3622) 标签: 自定义函数,

本节讲解如何使用自定义函数,非应用程序员如不需了解可以跳过,不影响正常阅读。

在集算器中,除了使用系统提供的各类函数,还可以调用用户自定义的函数,用来处理一些比较特殊的运算,或者封装某些计算过程。在这里,我们了解一下用invoke函数调用自定义函数的方法,并简单说明如何在自定义函数中使用参数及返回所需结果。

调用自定义函数的基本方法

使用invoke函数,可以调用指定的自定义JAVA类中的静态方法。如下面的JAVAtest.Calc01

package test;

public class Calc01 {

public static Double distance1(Number loc) {

double len = Math.abs(loc.doubleValue());

len = Math.round(len*1000)/1000d;

return Double.valueOf(len);

}

}

在这个简单的类中,静态方法distance1计算给定的一个坐标,计算与原点之间的距离,计算结果保留3位小数。在集算器中能够调用的方法,除了必须是静态static外,还必须公开public。使用时,需要把自定义函数类放置在集算器的类路径,即[安装目录]\esProc\classes路径下,如果在web端使用,相应的,应该放置在WEB-INF/classes路径下。这样,就可以在网格中,用invole函数调用自定义函数了:

 

A

1

-12.34567

2

=invoke(test.Calc01.distance1,A1)

3

=invoke(test.Calc01.distance1, -512)

如例子中所示,使用invoke函数时,先指明所调用的类的全路径以及静态方法名称,然后把需要用到的参数依次列出。使用的参数既可以是网格中的格值、网格参数等,也可以直接输入。计算后,A2A3中结果分别如下:

 

一个类中,可以有多个自定义函数,如再增加一个方法:

public static Double distance2(Number loc1, Number loc2) {

double len = Math.abs(loc1.doubleValue()-loc2.doubleValue());

len = Math.round(len*1000)/1000d;

return Double.valueOf(len);

}

新增的distance2用来计算数轴上两个坐标之间的距离。集算器只根据方法的名称来调用,因此不同的自定义函数需要使用不同的方法名称。只要自定义函数类放置在了应用的类路径中,无论多个自定义函数来自于多个类还是同一个类,都可以在同一个网格中调用:

 

A

1

=invoke(test.Calc01.distance1,-12.3456)

2

=invoke(test.Calc01.distance2,12,-12)

调用时,使用参数的数目和类型需与对应的方法匹配。A1A2中的计算结果分别如下:

 

根据需要返回结果

在自定义函数中,可以返回计算结果,也可以不返回,如:

public static void distance3(Number loc1, Number loc2) {

double len = Math.abs(loc1.doubleValue()-loc2.doubleValue());

len = Math.round(len*1000)/1000d;

System.out.println(Double.toString(len));

}

静态方法distance3完成计算后,仅在后台打印出结果,而不返回任何数据。这样的函数类似于网格中执行格的情况,调用时可以用>开头,如:

 

A

1

>invoke(test.Calc01.distance3,-12.3,15)

此时,可以在控制台中看到执行时后台输出的情况,打开控制台可以在菜单栏中选择Tool>Options,并在Option窗口的General页面中选定Console takeover。结果如下:

 

如果自定义函数中需要使用参数或返回结果,应该使用可与集算器匹配的数据类型,否则有可能在展现或调用时出现错误。集算器中常用数据类型对应的Java类型如下:

整数

java.lang.Integer

长整数

java.lang.Long

浮点数

java.lang.Double

长实数

java.math.BigInteger

实数

java.lang.Number

布尔型

java.lang.Boolean

字符串

java.lang.String

日期

java.sql.Date

时间

java.sql.Time

日期时间

java.sql.TimeStamp

序列

com.scudata.dm.Sequence

序表

com.scudata.dm.Table

二进制数据

byte[]

在前面的例子中,返回的数据都是Double对象,相当于集算器中的浮点数,而使用的参数都是Number,相当于集算器中的实数。在自定义函数中可以根据需要返回结果,例如,在test.Calc01中再增添两个自定义函数,返回不同类型的数据:

 

public static String distance4(Number loc1, Number loc2) {

double len = Math.abs(loc1.doubleValue()-loc2.doubleValue());

len = Math.round(len*1000)/1000d;

return Double.toString(len);

}

public static Sequence distance5(Number loc1, Number loc2) {

double len = Math.abs(loc1.doubleValue()-loc2.doubleValue());

len = Math.round(len*1000)/1000d;

com.scudata.dm.Sequence result = new com.scudata.dm.Sequence();

result.add(loc1);

result.add(loc2);

result.add(Double.valueOf(len));

return result;

}

这两个静态方法仍然用来计算数轴上两点间的距离,distance4返回字符串结果,distance5返回序列作为结果,其中存储了两点坐标与它们之间的距离。在网格中调用时,仍然使用invoke函数:

 

A

1

=invoke(test.Calc01.distance2,-12.3,15)

2

=invoke(test.Calc01.distance4,-12.3,15)

3

=invoke(test.Calc01.distance5,-12.3,15)

为了对比,A1中调用了前面使用过的distance2。执行后,A1~A3中的结果如下:

   

注意其中A1A2中结果的数据类型不同,显示模式是有区别的。自定义函数返回的结果,可以在后面的计算中使用。

使用序列类型的参数

自定义函数中使用的参数,是来自于集算器的,各类数据类型所对应的Java对象和上一节中介绍的相同。在调用自定义函数时,要注意参数类型要静态方法中的参数匹配。

特别的,序列是集算器中最常使用的数据类型,在自定义函数中,除了返回序列作为结果,同样可以使用序列类型的参数。如:

public static Double distance6(com.scudata.dm.Sequence seq1, com.scudata.dm.Sequence seq2) {

int len1 = seq1.length();

int len2 = seq2.length();

double x1 = len1 > 0 ? ((Number) seq1.get(1)).doubleValue(): 0;

double x2 = len2 > 0 ? ((Number) seq2.get(1)).doubleValue(): 0;

double y1 = len1 > 1 ? ((Number) seq1.get(2)).doubleValue(): 0;

double y2 = len2 > 1 ? ((Number) seq2.get(2)).doubleValue(): 0;

double len = Math.sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));

len = Math.round(len*1000)/1000d;

return Double.valueOf(len);

}

自定义函数distance6计算平面直角坐标系中两个点之间的距离。在调用时,两个点的坐标需用序列类型的参数传入。如:

 

A

1

[1,1]

2

[3,3]

3

=invoke(test.Calc01.distance6,A1,A2)

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

 

使用自定义游标

如果需要用自定义函数返回游标,那么需要返回的类要实现接口com.scudata.dm.IlineInput。如下面的test.RandDataCursor

package test;

public class RandDataCursor implements com.scudata.dm.ILineInput {

private int rowno = 0;

private int range = 1000;

public RandDataCursor(Integer rg) {

this.range = rg;

}

 

public Object[] readLine() throws java.io.IOException {

rowno++;

Object[] result = new Object[2];

result[0] = Integer.valueOf(rowno);

result[1] = Integer.valueOf((int) (Math.random() * range ));

return result;

}

 

public boolean skipLine() throws java.io.IOException {

rowno++;

if (rowno <= 10000) return true;

return false;

}

 

public void close() throws IOException {

rowno = 10001;

}

}

IlineInput类中需要实现readLine,skipLineclose3个方法。上面的RandDataCursor比较简单,用来生成由随机整数构成的序列,其中随机数的范围可以用参数指定。readLine返回的每条记录,都由顺次增加的行号和随机数构成,类似于报表的两个字段。在skipLine方法中,可以跳过1条记录,并返回游标中是否仍有剩余数据,上面例子中设定这个游标最多返回10000条数据。close方法则用来关闭游标,释放不需要的资源,如数据库连接等。

这样,返回游标的自定义函数即可将RandDataCursor作为返回类,如:

package test;

public class Calc02 {

public static test.RandDataCursor getCursor(Integer range) {

RandDataCursor rdc = new RandDataCursor(range);

return rdc;

}

}

这个自定义函数只需定义一个参数,即随机数的范围,返回的结果就是RandDataCursor,用来生成随机记录的游标。在调用时,返回的IlineInput类并不能直接作为游标使用,还需要调用com.scudata.dm.UserUtils类中的newCursor方法,如:

 

A

1

=invoke(test.Calc02.getCursor,1000000)

2

=invoke(com.scudata.dm.UserUtils.newCursor,A1,"")

3

>A2.skip(100)

4

=A2.fetch@x(100)

A1中,返回自定义游标类,RandDataCursorA2中,调用UserUtils中的newCursor方法,将A1中的结果作为参数传入,A2中的数据即为集算器中的游标数据了。A3中跳过前100条记录后,A4中取出100条数据后关闭游标,A4中结果如下:

返回数据时,也可以类似与读取文本数据一样,添加@t选项,将读到的第1行数据作为标题,或者添加@1选项,将数据读为1列。如将A2中的代码改为=invoke(com.scudata.dm.UserUtils.newCursor,A1,"t"),即相当于添加了@t选项,A4中结果如下:

当然,这里的RandDataCursor中实际上并未设计类名,执行的结果只是将第1条数据作为列名返回。从结果中可以看到,数据中的第2列和之前的结果不同,是随机整数。