【程序设计】6.1 [重复用] 自定义函数
6.1 自定义函数
还是假定没有阶乘函数,现在想计算组合数。
A |
B |
C |
|
1 |
10 |
4 |
|
2 |
=1 |
for A1 |
>A2=A2*B2 |
3 |
=1 |
for B1 |
>A3=A3*B3 |
4 |
=1 |
for A1-B1 |
>A4=A4*B4 |
5 |
=A2\A3\A4 |
n 和 k 分别填入 A1 和 B1,第 2、3、4 行分别计算出 n!,k! 和 (n-k)!。
这个计算没问题,但是,几乎同样的代码写了三遍。
如果问题再麻烦一点,比如我们要计算二项式定理中的系数序列(也就是帕斯卡三角的某一行)。循环函数可以一句算出多个值,但必须在表达式中容易写出来的才可以,还需要几行代码才能实现 的计算,就还得用循环语句来做了:
A |
B |
C |
D |
|
1 |
10 |
=1 |
for A1 |
>B1=B1*C1 |
2 |
for A1-1 |
=1 |
for A2 |
>B2=B2*C2 |
3 |
=1 |
for A1-A2 |
>B3=B3*C3 |
|
4 |
=@|(B1\B2\B3) |
|||
5 |
=1|B4|1 |
其实,包括 SPL 在内的大多数程序语言都允许我们把要重复执行的代码写成自己的函数,然后就可以反复调用了。
A |
B |
C |
D |
|
1 |
func |
=1 |
for A1 |
>B1=B1*C1 |
2 |
return B1 |
|||
3 |
=func(A1,10) |
=func(A1,4) |
=func(A1,6) |
=A3\B3\C3 |
A1 格的 func 语句自定义了一个函数,这个函数也就用 A1 来标识,它的代码就是 A1 的代码块。SPL 碰到 func 格子,会先跳过这个代码块往后看去执行后续的代码。
A3 格中有 func() 函数,其中第一个参数是要调用的自定义函数的位置,这里是 A1,意味着要去调用在 A1 定义的函数了,后面的参数 10 是要传入这个自定义函数 A1 的参数。SPL 会把这个参数写进 A1,然后让程序跳转到 A1 的代码块去执行,即开始执行 B1:D2。
B1:D1 的代码我们已经能理解了,它能够计算出 A1 的阶乘填入 B1。当 A1 填入 10 之后,这段代码就计算出 10! 填入 B1。然后 B2 格的 return 语句将 B1 的值返回,这时候程序又会跳回到调用这个函数的地方,即 A3,并得到了 func() 的返回值 10!,A3 的格值就是 10!
也就是说自定义函数 A1 将计算出其参数的阶乘值返回。
同理,B3 将计算出 4!,C3 将计算出 6!,最后在 D3 计算出组合数 。
整个过程看起来还是有点复杂,但无论如何,这 3 个阶乘数使用了同一段代码计算,不需要把代码写三遍了。
总结一下:用 func 语句定义函数,其中要有 return 语句返回计算结果。然后使用 func() 函数来传入参数调用这个函数。
func() 就是个函数,它和其它函数使用方法是一样的,可以放在表达式里参与计算。
A |
B |
C |
D |
|
1 |
func |
=1 |
for A1 |
>B1=B1*C1 |
2 |
return B1 |
|||
3 |
=func(A1,10)\func(A1,4)\func(A1,6) |
这样也能正确计算出结果。
而且,做成自定义函数之后,就可以用到循环函数里去了。
A |
B |
C |
D |
|
1 |
func |
=1 |
for A1 |
>B1=B1*C1 |
2 |
return B1 |
|||
3 |
10 |
=func(A1,A3) |
||
4 |
=1|(A3-1).(B3\func(A1,~)\func(A1,A3-~))|1 |
二项式的系数计算也看着更清晰了。
我们再举一个有点实际用途的例子:在一批平面上的点中找出异常点。办法是这样:计算每个点到其它点的距离之和,然后从大到小排序,前 10% 的点就认为是异常点,因为它们距离其它点相对比较远,可以认为是异常点。
A |
B |
C |
D |
|
1 |
100 |
>X=A1.(rand()) |
>Y=A1.(rand()) |
|
2 |
=to(A1) |
=A2.(A1.sum( func(A5,X(A2.~),X(~),Y(A2.~),Y(~)) )) |
||
3 |
=B2.psort@z().to(int(A1*0.1)) |
|||
4 |
||||
5 |
func |
|||
6 |
return sqrt((A5-B5)*(A5-B5)+(C5-D5)*(C5-D5)) |
在 A5 定义一个函数用来计算两个点的距离,它有四个参数,分别是两个点的横坐标和纵坐标,使用勾股定理可以计算出两点的距离返回。当自定义函数有多个参数时,会从定义格开始向右填入。这个函数定义 A5,其四个参数会分别填入 A5,B5,C5,D5。这样,我们要把函数体写到下一行,即 B6 格。
函数定义在任何地方都可以,在主程序的前面和后面都不会影响代码的执行。
主程序中,先随机生成 100 个点的横坐标和纵坐标。然后在 B2 格就可以调用自定义函数 A5 来计算距离之和了,这是个两层循环函数,内层计算 100 个距离求和(自己和自己的距离为 0,多加一个不会出错,不必把自己排除掉),外层则循环这 100 个点。注意里面的 A2.~ 和 ~ 的差别。
然后用 psort 排序取出前 10% 的点的序号就可以了。