回 帖 发 新 帖 刷新版面

主题:请教一个数组函数问题

能不能实现这么一个函数y(x):
如果x是大小为n的一维数组,返回值y也是大小为n的一维数组,y(i)=x(i)**2
如果x是标量,返回值y也是标量,y=x**2

比较笨的办法就是写成两个函数,一个用来返回数组,一个用来返回标量;有没有办法写成一个函数呢?

回复列表 (共9个回复)

沙发

根本不需要用函数:

real a, b(3), c(3,3)

print *, a ** 2.0
print *, b ** 2.0
print *, c ** 2.0

板凳

楼上方法对于一般数组应用已经足够,但如果处理较大数组,会受堆栈大小限制。
另一种方案是采用“重载”方法:
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 楼

[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 楼

我不认为 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 楼

&#25105;&#35828;&#20570;&#24179;&#26041;&#36816;&#31639;&#23454;&#38469;&#19978;&#21482;&#26159;&#19968;&#20010;&#27604;&#21947;&#65292;&#23454;&#38469;&#35201;&#23454;&#29616;&#19968;&#20010;&#27604;&#36739;&#22797;&#26434;&#30340;&#36816;&#31639;&#65292;&#38656;&#35201;&#20889;&#25104;&#33258;&#23450;&#20041;&#20989;&#25968;&#30340;&#24418;&#24335;&#12290;&#25105;&#26159;&#24076;&#26395;&#36825;&#20010;&#20989;&#25968;&#30340;&#36820;&#22238;&#20540;y&#33021;&#22815;&#26681;&#25454;&#33258;&#21464;&#37327;X&#21464;&#21270;&#65306;&#22914;&#26524;X&#26159;&#19968;&#20010;&#25968;&#32452;&#65292;&#37027;&#20040;Y&#26159;&#19968;&#20010;&#23545;&#24212;&#22823;&#23567;&#30340;&#25968;&#32452;&#65292;Y(i)&#31561;&#20110;X(i)&#20570;&#23545;&#24212;&#36816;&#31639;&#30340;&#32467;&#26524;&#12290;&#22914;&#26524;X&#26159;&#19968;&#20010;&#26631;&#37327;&#65292;&#37027;&#20040;Y&#20063;&#26159;&#19968;&#20010;&#23545;&#24212;&#26631;&#37327;&#65292;&#31561;&#20110;X&#20570;&#23545;&#24212;&#36816;&#31639;&#30340;&#32467;&#26524;&#12290;&#25105;&#20889;&#30340;&#31243;&#24207;&#22914;&#19979;&#65306;

module
   contains
      function y(x)
         implicit none
         real, intent(in) :: x(:)
         real, allocatable :: y(:)
         
         allocate(y(size(x))
         ....
      end function y
end module

&#20294;&#26159;&#21482;&#33021;&#35745;&#31639;x&#26159;&#25968;&#32452;&#30340;&#24773;&#20917;&#12290;
&#25105;&#19981;&#30693;&#36947;&#22312;&#23376;&#31243;&#24207;&#20013;&#35813;&#24590;&#26679;&#23545;&#36755;&#20837;&#36827;&#34892;&#25968;&#32452;&#36824;&#26159;&#26631;&#37327;&#30340;&#21028;&#26029;&#65292;&#32780;&#19988;&#36820;&#22238;&#20540;y&#22312;&#23376;&#31243;&#24207;&#20013;&#24212;&#35813;&#20570;&#24590;&#26679;&#30340;&#23545;&#24212;&#22768;&#26126;&#65311;

&#24403;&#28982;&#25105;&#19968;&#24320;&#22987;&#26159;&#25226;&#31243;&#24207;&#20889;&#25104;&#20102;&#36755;&#20837;x&#26159;&#26631;&#37327;&#65292;&#36755;&#20986;y&#20063;&#26159;&#26631;&#37327;&#65292;&#28982;&#21518;&#22312;&#22806;&#23618;&#31243;&#24207;&#35843;&#29992;&#30340;&#26102;&#20505;&#29992;&#24490;&#29615;&#35843;&#29992;&#12290;&#25105;&#24819;&#38382;&#65292;&#36825;&#26679;&#22312;&#23376;&#31243;&#24207;&#22806;&#38754;&#24490;&#29615;&#35843;&#29992;&#30340;&#25928;&#29575;&#39640;&#65292;&#36824;&#26159;&#25226;&#36825;&#20010;&#23376;&#31243;&#24207;&#20889;&#25104;&#25968;&#32452;&#24418;&#24335;&#65292;&#22312;&#20869;&#37096;&#36827;&#34892;&#24490;&#29615;&#36816;&#31639;&#26469;&#24471;&#39640;&#21602;&#65311;&#29305;&#21035;&#26159;&#25968;&#25454;&#37327;&#27604;&#36739;&#22823;&#30340;&#24773;&#20917;&#12290;

&#31532;&#19968;&#27425;&#19978;&#35770;&#22363;&#38382;&#38382;&#39064;&#65292;&#20063;&#19981;&#30693;&#24590;&#20040;&#25165;&#33021;&#35828;&#28165;&#26970;&#12290;&#35874;&#35874;&#22823;&#23478;&#30340;&#22238;&#22797;&#65374;

6 楼


&#36825;&#26159;&#20010;&#22909;&#26041;&#27861;&#65292;&#24429;&#22269;&#20262;&#30340;&#20070;&#20013;&#30475;&#21040;&#36807;&#12290;&#27809;&#24590;&#20040;&#22312;&#24847;&#12290;&#12290;&#12290;

7 楼

汗,firefox居然没法正常显示。
谢谢大家的回复。二楼的答案正解,彭国伦的书上有看到过,不过也没仔细留心注意。
实际上我要写的y(x)是一个比较复杂的函数。我一开始是把它写成标量输入输出的形式,在外层函数中循环调用;现在我想把循环写在y里面。主要是想这样是不是比在外层反复循环调用要高效一些(数据量比较大的时候)。

8 楼

如果你的 x 是一个数组

且计算 k + 1 个元素时,与之前的 k 个没有关系。

那么循环在函数里面还是外面,区别不大,实际的计算量是一样的。

但再严格的来说,由于进入函数,退出函数,系统需要进行堆栈整理(处理实参和虚参的对应),所以这部分也会消耗一些时间。

所以在要求很高效的计算时,尽量避免频繁的进入函数和退出函数确实可以提高效率。

这要综合考虑,如果计算一次 y 函数的时间比进入函数的堆栈整理花费的时间多得多,那么堆栈整理所花费的时间几乎就忽略不计了,两者的差别就很小的。你的情况就是这样,y 很复杂,所以它的时间应该比堆栈整理多得多。你把循环写在外面和里面差别不会很大。

PS:解释一下堆栈整理,是指进入函数时压入实参到堆栈,离开函数时弹出。所以频繁的进入函数,离开函数并不要求很多堆栈(Fortran是传址调用的)。

9 楼

旧的语法不支持函数返回数组,新的语法还没时间学,呵呵!
关于堆栈的问题纯粹是基于以前的应用经验,还是雪球兄专业!
刚才用IVF试了一下,大数组确实不存在问题了,看来应该是编译器有了改变!

我来回复

您尚未登录,请登录后再回复。点此登录或注册