主题:请教一个数组函数问题
ghostnightmare
[专家分:0] 发布于 2011-02-11 23:30:00
能不能实现这么一个函数y(x):
如果x是大小为n的一维数组,返回值y也是大小为n的一维数组,y(i)=x(i)**2
如果x是标量,返回值y也是标量,y=x**2
比较笨的办法就是写成两个函数,一个用来返回数组,一个用来返回标量;有没有办法写成一个函数呢?
最后更新于:2011-02-11 23:31:00
回复列表 (共9个回复)
沙发
adda [专家分:1520] 发布于 2011-02-12 18:17:00
根本不需要用函数:
real a, b(3), c(3,3)
print *, a ** 2.0
print *, b ** 2.0
print *, c ** 2.0
板凳
zinsser_1982 [专家分:400] 发布于 2011-02-13 09:56:00
楼上方法对于一般数组应用已经足够,但如果处理较大数组,会受堆栈大小限制。
另一种方案是采用“重载”方法:
module aaa
implicit none
interface yy
module procedure yy_scalar
module procedure yy_vector
end interface
contains
subroutine yy_scalar(a,b)
implicit none
integer::a,b
b=a**2
end subroutine yy_scalar
subroutine yy_vector(a,b)
implicit none
integer,dimension(:)::a,b
integer::len,i
len=size(a)
do i=1,len
b(i)=a(i)**2
enddo
end subroutine yy_vector
end module
program main
use aaa
implicit none
integer::a,b
integer::aa(10),bb(10)
integer::i
a=1
call yy(a,b)
write(*,*) b
do i=1,10
aa(i)=i
enddo
call yy(aa,bb)
write(*,*) bb
end
3 楼
adda [专家分:1520] 发布于 2011-02-13 11:21:00
[quote]楼上方法对于一般数组应用已经足够,但如果处理较大数组,会受堆栈大小限制。
另一种方案是采用“重载”方法:
module aaa
implicit none
interface yy
module procedure yy_scalar
module procedure yy_vector
end interface
contains
subroutine yy_scalar(a,b)
implicit none
integer::a,b
b=a**2
end subroutine yy_scalar
subroutine yy_vector(a,b)
implicit none
integer,dimension(:)::a,b
integer::len,i
len=size(a)
do i=1,len
b(i)=a(i)**2
enddo
end subroutine yy_vector
end module
program main
use aaa
implicit none
integer::a,b
integer::aa(10),bb(10)
integer::i
a=1
call yy(a,b)
write(*,*) b
do i=1,10
aa(i)=i
enddo
call yy(aa,bb)
write(*,*) bb
end[/quote]
会受堆栈限制吗?
即使用重载,也明显是写成函数形式更方便啊:
function fn(a) result(b)
integer, intent(in) :: a(:)
integer :: b(size(a))
b = a ** 2.0
end function
4 楼
臭石头雪球 [专家分:23030] 发布于 2011-02-13 13:42:00
我不认为 b = a ** 2 这样的算法会使用到多少堆栈。
编译器应该会选择直接在他们的储存地址空间上操作,最多利用一些寄存器作为缓存。
如果开辟堆栈来计算,那这个编译器似乎傻了点
Program Main
Use Kernel32
Implicit None
real a(30,30), b(30,30) , i
b = 3.0
call OutputDebugString("Begin")
a = b ** 2.0
call OutputDebugString("End")
End Program Main
这个代码在 IVF 上编译,然后汇编级调试。
00401072 C70424 3C824700 mov dword ptr [esp], 0047823C ; ASCII "Begin"
00401079 E8 7E000000 call <jmp.&KERNEL32.OutputDebugStringA>
0040107E C745 F8 0100000>mov dword ptr [ebp-8], 1
00401085 8B45 F8 mov eax, dword ptr [ebp-8]
00401088 83F8 1E cmp eax, 1E !//此处比较第一层循环是否大于 1E(30)
0040108B 7F 5B jg short 004010E8
0040108D C745 FC 0100000>mov dword ptr [ebp-4], 1
00401094 8B45 FC mov eax, dword ptr [ebp-4]
00401097 83F8 1E cmp eax, 1E !//!//此处比较第二层循环是否大于 1E(30)
0040109A 7F 41 jg short 004010DD
0040109C 8B45 F8 mov eax, dword ptr [ebp-8]
0040109F 8BD0 mov edx, eax
004010A1 C1E2 03 shl edx, 3
004010A4 C1E0 07 shl eax, 7
004010A7 2BC2 sub eax, edx
004010A9 8B55 FC mov edx, dword ptr [ebp-4]
004010AC F3:0F108490 44B>movss xmm0, dword ptr [eax+edx*4+48B544]
004010B5 F3:0F59C0 mulss xmm0, xmm0
004010B9 8B45 F8 mov eax, dword ptr [ebp-8]
004010BC 8BD0 mov edx, eax
004010BE C1E2 03 shl edx, 3
004010C1 C1E0 07 shl eax, 7
004010C4 2BC2 sub eax, edx
004010C6 8B55 FC mov edx, dword ptr [ebp-4]
004010C9 F3:0F118490 44C>movss dword ptr [eax+edx*4+48C744], xmm0
004010D2 FF45 FC inc dword ptr [ebp-4]
004010D5 8B45 FC mov eax, dword ptr [ebp-4]
004010D8 83F8 1E cmp eax, 1E
004010DB ^ 7E BF jle short 0040109C
004010DD FF45 F8 inc dword ptr [ebp-8]
004010E0 8B45 F8 mov eax, dword ptr [ebp-8]
004010E3 83F8 1E cmp eax, 1E
004010E6 ^ 7E A5 jle short 0040108D !//此处结束
004010E8 50 push eax
004010E9 C70424 44824700 mov dword ptr [esp], 00478244 ; ASCII "End"
004010F0 E8 07000000 call <jmp.&KERNEL32.OutputDebugStringA>
循环体内没有任何的 push 和 pop。全部是内存地址与寄存器之间的数据传输或者移位。
在Begin 处插入断点,堆栈顶为:00E2FE64
在循环体内插入断点,并自由运行 30 次,60次,90次。堆栈顶依然是:00E2FE64
直到运行至 End 前,堆栈顶(ESP寄存器)没有发生任何改变
5 楼
ghostnightmare [专家分:0] 发布于 2011-02-13 19:58:00
我说做平方运算实际上只是一个比喻,实际要实现一个比较复杂的运算,需要写成自定义函数的形式。我是希望这个函数的返回值y能够根据自变量X变化:如果X是一个数组,那么Y是一个对应大小的数组,Y(i)等于X(i)做对应运算的结果。如果X是一个标量,那么Y也是一个对应标量,等于X做对应运算的结果。我写的程序如下:
module
contains
function y(x)
implicit none
real, intent(in) :: x(:)
real, allocatable :: y(:)
allocate(y(size(x))
....
end function y
end module
但是只能计算x是数组的情况。
我不知道在子程序中该怎样对输入进行数组还是标量的判断,而且返回值y在子程序中应该做怎样的对应声明?
当然我一开始是把程序写成了输入x是标量,输出y也是标量,然后在外层程序调用的时候用循环调用。我想问,这样在子程序外面循环调用的效率高,还是把这个子程序写成数组形式,在内部进行循环运算来得高呢?特别是数据量比较大的情况。
第一次上论坛问问题,也不知怎么才能说清楚。谢谢大家的回复~
6 楼
ghostnightmare [专家分:0] 发布于 2011-02-13 20:00:00
这是个好方法,彭国伦的书中看到过。没怎么在意。。。
7 楼
ghostnightmare [专家分:0] 发布于 2011-02-13 20:15:00
汗,firefox居然没法正常显示。
谢谢大家的回复。二楼的答案正解,彭国伦的书上有看到过,不过也没仔细留心注意。
实际上我要写的y(x)是一个比较复杂的函数。我一开始是把它写成标量输入输出的形式,在外层函数中循环调用;现在我想把循环写在y里面。主要是想这样是不是比在外层反复循环调用要高效一些(数据量比较大的时候)。
8 楼
臭石头雪球 [专家分:23030] 发布于 2011-02-13 20:59:00
如果你的 x 是一个数组
且计算 k + 1 个元素时,与之前的 k 个没有关系。
那么循环在函数里面还是外面,区别不大,实际的计算量是一样的。
但再严格的来说,由于进入函数,退出函数,系统需要进行堆栈整理(处理实参和虚参的对应),所以这部分也会消耗一些时间。
所以在要求很高效的计算时,尽量避免频繁的进入函数和退出函数确实可以提高效率。
这要综合考虑,如果计算一次 y 函数的时间比进入函数的堆栈整理花费的时间多得多,那么堆栈整理所花费的时间几乎就忽略不计了,两者的差别就很小的。你的情况就是这样,y 很复杂,所以它的时间应该比堆栈整理多得多。你把循环写在外面和里面差别不会很大。
PS:解释一下堆栈整理,是指进入函数时压入实参到堆栈,离开函数时弹出。所以频繁的进入函数,离开函数并不要求很多堆栈(Fortran是传址调用的)。
9 楼
zinsser_1982 [专家分:400] 发布于 2011-02-14 23:54:00
旧的语法不支持函数返回数组,新的语法还没时间学,呵呵!
关于堆栈的问题纯粹是基于以前的应用经验,还是雪球兄专业!
刚才用IVF试了一下,大数组确实不存在问题了,看来应该是编译器有了改变!
我来回复