[英]`Error: Return type mismatch of function` in Fortran 95
我決定嘗試在Fortran 95中實現階乘函數(f2py限制),但我的努力只產生兩個返回類型不匹配錯誤。
解決方案的靈感
在Haskell中,我們可以做類似的事情
fac_helper n acc =
if n <= 1
then acc
else fac_helper (n - 1) (acc * n)
factorial n = fac_helper n 1
試圖解決方案:fac.f95
recursive function facHelper(n, acc) result(returner)
integer::n
integer::acc
integer::returner
if (n <= 1) then
returner = acc
else
returner = facHelper(n - 1, n * acc)
endif
end function facHelper
function factorial(n)
integer::n
integer::factorial
factorial = facHelper(n, 1)
end function factorial
當fac.f95上使用GNU Fortran(GCC)4.8.3時
gfortran -std=f95 ./fac.f95 -o fac
結果是:
Error: Return type mismatch of function 'fachelper' at (1) (REAL(4)/INTEGER(4))
Error: Return type mismatch of function 'factorial' at (1) (REAL(4)/INTEGER(4))
這些錯誤(對於不熟悉的Fortraner)顯示為與嘗試編譯的代碼脫節。 我確信在嘗試的解決方案中沒有聲明或使用的實數。 ...?
如何在Fortran95中實現尾遞歸因子實現?
您向我們展示的錯誤可能與您調用factorial()
,而與此代碼無關。 如果我在以下示例中包裝您的代碼:
program test
implicit none
integer :: i
do i=1,10
write (*,'(i2,"! = ",i8)') i, factorial(i)
end do
contains
[cut and paste the code from your question]
end program
並使用gfortran 4.8.3編譯:
gfortran -std=f95 -o fac fac.f90
我得到輸出:
1! = 1
2! = 2
3! = 6
4! = 24
5! = 120
6! = 720
7! = 5040
8! = 40320
9! = 362880
10! = 3628800
確保使用正確的參數類型調用factorial()
,如果它不在模塊或內部過程中,請使用顯式接口。 我注意到您在代碼中沒有使用implicit none
,因此還要驗證您是否明確聲明要調用factorial
的變量以確保使用正確的類型。
如果您將這些過程用作外部過程(例如,不在主程序中或包含在模塊中),那么為了讓調用過程知道要發生什么,您應該使用顯式接口。 請參閱以下示例。
program test
implicit none
integer :: i
interface
function factorial(n)
integer, intent(in) :: n
integer :: factorial
end function factorial
end interface
do i=1,10
write (*,'(i2,"! = ",i8)') i, factorial(i)
end do
end program
recursive function facHelper(n, acc) result(returner)
implicit none
integer, intent(in) :: n, acc
integer :: returner
if (n <= 1) then
returner = acc
else
returner = facHelper(n - 1, n * acc)
endif
end function facHelper
function factorial(n)
implicit none
integer, intent(in) :: n
integer :: factorial
interface
function facHelper(n,acc)
integer, intent(in) :: n, acc
integer :: facHelper
end function
end interface
factorial = facHelper(n, 1)
end function factorial
我對您的示例所做的更改僅限於:
implicit none
以禁止隱式輸入 如果implicit none
您編譯代碼的原始嘗試就會失敗。 因為你沒有它,你的外部函數調用factorial
,以f
開頭的假設是真實的。 當函數返回一個整數時,這會導致類型不匹配。 您通過明確聲明factorial
是一個整數來解決這個問題,但更好的解決方案是完全指定接口,以便編譯器可以檢查參數而不僅僅是返回類型。 在我的代碼中,主程序調用factorial
,因此這是添加了顯式接口塊的地方。 同樣, factorial
調用facHelper
並且同樣需要一個接口。
通過在contains
語句之后contains
模塊內部或主程序中的函數,可以避免使用顯式接口塊。 正如上面的例子所示,你提出的算法中沒有任何改變,這些改變僅限於Fortran處理外部程序和隱式類型的問題 - 在這兩個例子中,這個例子選擇了顯式的最佳實踐。
我個人會選擇在模塊中包含這些函數,完全避免使用顯式接口。 看這個例子:
module fact
private :: facHelper
contains
recursive function facHelper(n, acc) result(returner)
implicit none
integer, intent(in) :: n, acc
integer :: returner
if (n <= 1) then
returner = acc
else
returner = facHelper(n - 1, n * acc)
endif
end function facHelper
function factorial(n)
implicit none
integer, intent(in) :: n
integer :: factorial
factorial = facHelper(n, 1)
end function factorial
end module fact
program test
use fact
implicit none
integer :: i
do i=1,10
write (*,'(i2,"! = ",i8)') i, factorial(i)
end do
end program
在此示例中, factorial
位於模塊中, facHelper
位於模塊中,但不能在外部調用(聲明為私有且對主程序隱藏)。 您會看到此處的算法與您提出的代碼幾乎相同,唯一的區別是添加的implicit none
。 在主程序中,行use fact
讓程序知道函數的接口。
在與隱式無等之類的東西混亂之后,似乎問題實際上是在階乘函數中。
即使已經定義了facHelper
recursive integer function facHelper(n, acc) result(returner)
並且在facHelper之后定義了階乘函數,因子仍然不知道facHelper返回一個整數。
然后解決方案是在factorial函數中添加一行,告訴它facHelper是整數:
function factorial(n)
integer::n
integer::factorial
integer::facHelper ! <-- Missing information now available
factorial = facHelper(n, 1)
end function factorial
如何在Fortran95中實現尾遞歸因子實現?
Fortran95中的尾遞歸因子函數可以實現為:
fac.f95
recursive function facHelper(n, acc) result(returner)
integer::n
integer::acc
integer::returner
if (n <= 1) then
returner = acc
else
returner = facHelper(n - 1, n * acc)
endif
end function facHelper
function factorial(n)
integer::n
integer::factorial
integer::facHelper
factorial = facHelper(n, 1)
end function factorial
或者,更令人愉悅(在我看來):
fac.f95
recursive integer function facHelper(n, acc) result(returner)
integer::n
integer::acc
if (n <= 1) then
returner = acc
else
returner = facHelper(n - 1, n * acc)
endif
end function facHelper
integer function factorial(n)
integer::n
integer::facHelper
factorial = facHelper(n, 1)
end function factorial
這兩個現在將在GNU Fortran(GCC)4.8.3下編譯
gfortran --std=f95 -c ./fac.f95
使用f2py v2和NumPy v1.8.0
雖然兩個版本的fac.f95 abover都將使用gfortran編譯,但第二個版本將導致f2py認為來自facHelper的返回者是真實的。 但是,f2py可以正確處理fac.f95的第一個版本。
我想在Fortran中對尾部遞歸因子進行基准測試(及時)。 我添加了一個非尾遞歸版本的階乘(名為vanillaFac)。 整數大小也增加到kind = 8。
fac.f95現在包含
recursive function tailFacHelper(n, acc) result(returner)
integer (kind=8)::n
integer (kind=8)::acc
integer (kind=8)::returner
if (n <= 1) then
returner = acc
else
returner = tailFacHelper(n - 1, n * acc)
endif
end function tailFacHelper
function tailFac(n)
integer (kind=8)::n
integer (kind=8)::tailFac
integer (kind=8)::tailFacHelper
tailFac = tailFacHelper(n, 1_8)
end function tailFac
recursive function vanillaFac(n) result(returner)
integer (kind=8)::n
integer (kind=8)::returner
if (n <= 1) then
returner = 1
else
returner = n * vanillaFac(n - 1)
endif
end function vanillaFac
新的fac.f95編譯了
f2py --overwrite-signature --no-lower fac.f95 -m liboptfac -h fac.pyf;
f2py -c --f90flags=--std=f95 --opt=-O3 fac.pyf fac.f95;
f2py --overwrite-signature --no-lower fac.f95 -m libnooptfac -h fac.pyf;
f2py -c --f90flags=--std=f95 --noopt fac.pyf fac.f95;
我寫了一個python腳本timer.py
import liboptfac
import libnooptfac
import timeit
def py_vanilla_fac(n):
if n <= 1:
return 1
else:
return n * py_vanilla_fac(n - 1)
def py_tail_fac_helper(n, acc):
if n <= 1:
return acc
else:
return py_tail_fac_helper(n - 1, n * acc)
def py_tail_fac(n):
return py_tail_fac_helper(n, 1)
LOOPS = 10 ** 6
print "\n*****Fortran (optimizations level 03 enabled)*****"
print "\nliboptfac.vanillaFac(20)\n" + str(LOOPS) + " calls\nBest of ten: ", min(timeit.repeat(lambda: liboptfac.vanillaFac(20), repeat = 10, number = LOOPS))
print "\nliboptfac.tailFac(20)\n" + str(LOOPS) + " calls\nBest of ten: ", min(timeit.repeat(lambda: liboptfac.tailFac(20), repeat = 10, number = LOOPS))
print "\nliboptfac.tailFacHelper(20, 1)\n" + str(LOOPS) + " calls\nBest of ten: ", min(timeit.repeat(lambda: liboptfac.tailFacHelper(20, 1), repeat = 10, number = LOOPS))
print "\n\n*****Fortran (no optimizations enabled)*****"
print "\nlibnooptfac.vanillaFac(20)\n" + str(LOOPS) + " calls\nBest of ten: ", min(timeit.repeat(lambda: libnooptfac.vanillaFac(20), repeat = 10, number = LOOPS))
print "\nlibnooptfac.tailFac(20)\n" + str(LOOPS) + " calls\nBest of ten: ", min(timeit.repeat(lambda: libnooptfac.tailFac(20), repeat = 10, number = LOOPS))
print "\nlibnooptfac.tailFacHelper(20, 1)\n" + str(LOOPS) + " calls\nBest of ten: ", min(timeit.repeat(lambda: libnooptfac.tailFacHelper(20, 1), repeat = 10, number = LOOPS))
print "\n\n*****Python*****"
print "\npy_vanilla_fac(20)\n" + str(LOOPS) + " calls\nBest of ten: ", min(timeit.repeat(lambda: py_vanilla_fac(20), repeat = 10, number = LOOPS))
print "\npy_tail_fac(20)\n" + str(LOOPS) + " calls\nBest of ten: ", min(timeit.repeat(lambda: py_tail_fac(20), repeat = 10, number = LOOPS))
print "\npy_tail_fac_helper(20, 1)\n" + str(LOOPS) + " calls\nBest of ten: ", min(timeit.repeat(lambda: py_tail_fac_helper(20, 1), repeat = 10, number = LOOPS))
print "\n\n\n"
最后,打電話
python timer.py
輸出:
*****Fortran (optimizations level 03 enabled)*****
liboptfac.vanillaFac(20)
1000000 calls
Best of ten: 0.813575983047
liboptfac.tailFac(20)
1000000 calls
Best of ten: 0.843787193298
liboptfac.tailFacHelper(20, 1)
1000000 calls
Best of ten: 0.858899831772
*****Fortran (no optimizations enabled)*****
libnooptfac.vanillaFac(20)
1000000 calls
Best of ten: 1.00723600388
libnooptfac.tailFac(20)
1000000 calls
Best of ten: 0.975327014923
libnooptfac.tailFacHelper(20, 1)
1000000 calls
Best of ten: 0.982407093048
*****Python*****
py_vanilla_fac(20)
1000000 calls
Best of ten: 6.47849297523
py_tail_fac(20)
1000000 calls
Best of ten: 6.93045401573
py_tail_fac_helper(20, 1)
1000000 calls
Best of ten: 6.81205391884
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.