`错误:在Fortran 95中返回函数`的类型不匹配

时间:2014-07-21 23:26:13

标签: recursion functional-programming fortran gfortran fortran95

我决定尝试在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中的尾递归因子实现如何?

2 个答案:

答案 0 :(得分:5)

您向我们展示的错误可能与您调用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是一个整数来解决这个问题,但更好的解决方案是完全指定接口,以便编译器可以检查参数而不仅仅是返回类型。在我的代码中,主程序调用{​​{1}},因此这是添加显式接口块的地方。同样,factorial调用factorial,同样需要一个接口。

通过在facHelper语句后包含模块内或主程序中的函数,可以避免使用显式接口块。正如上面的例子所示,你提出的算法中没有任何改变,这些改变仅限于Fortran处理外部程序和隐式类型的问题 - 在这两个例子中,这个例子选择了显式的最佳实践。

使用模块:

我个人会选择在模块中包含这些函数,完全避免使用显式接口。见这个例子:

contains

在此示例中,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让程序知道函数的接口。

答案 1 :(得分:0)

问题和解决方案

在搞乱隐含无等问题之后,似乎问题实际上是在阶乘函数中。

即使用

定义了facHelper
recursive integer function facHelper(n, acc) result(returner)

并且在facHelper之后定义了阶乘函数,factorial仍然不知道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