簡體   English   中英

`錯誤:在Fortran 95中返回函數`的類型不匹配

[英]`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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM