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

Recursion `错误:Fortran 95中函数`的返回类型不匹配,recursion,functional-programming,fortran,gfortran,fortran95,Recursion,Functional Programming,Fortran,Gfortran,Fortran95,我决定尝试在Fortran 95中实现阶乘函数(f2py限制),但我的努力只产生了两个返回类型不匹配错误 解决方案的灵感 在Haskell,我们可以做一些类似的事情 fac\u助手n acc= 如果n您向我们显示的错误可能与调用factorial()的方式有关,而不是在此代码中。如果我将代码包装在以下示例中: program test implicit none integer :: i do i=1,10 write (*,'(i2,"! = ",i8)') i, fac

我决定尝试在Fortran 95中实现阶乘函数(f2py限制),但我的努力只产生了两个返回类型不匹配错误


解决方案的灵感

在Haskell,我们可以做一些类似的事情

fac\u助手n acc=

如果n您向我们显示的错误可能与调用
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
在本例中,
factorial
位于模块中,
fachhelper
位于模块中,但不可在外部调用(声明为私有且对主程序隐藏)。您将看到,这里的算法与建议的代码几乎相同,唯一的区别是添加了
隐式none
。在主程序中,行
use fact
让程序知道函数的接口

问题与解决方案 在对诸如隐式无等问题大惊小怪之后,似乎问题实际上在阶乘函数中

即使facHelper已定义为

recursive integer function facHelper(n, acc) result(returner)
如果factorial函数是在fachhelper之后定义的,factorial仍然不知道fachhelper返回一个整数

然后,解决方案是在阶乘函数中添加一行,告诉它fachhelper是整数:

function factorial(n)
  integer::n
  integer::factorial
  integer::facHelper ! <-- Missing information now available
  factorial = facHelper(n, 1)
end function factorial

多余的复活节彩蛋 将f2py v2与NumPy v1.8.0一起使用

虽然上述两个版本的fac.f95都将使用gfortran编译,但第二个版本将使f2py认为来自fachhelper的返回程序是真实的。但是,f2py确实正确处理了fac.f95的第一个版本

我想在Fortran中对尾部递归阶乘进行基准测试。我添加了阶乘的非尾部递归版本(名为vanillaFac)。整数大小也增加到种类=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
我写了一个python脚本timer.py

导入liboptfac
导入libnooptfac
导入时间信息
def py_vanilla_fac(n):

如果n如何调用
factorial
?我在接下来的几个小时内没有代表回答我自己的问题,但问题实际上是在factorial函数中,不知道fachhelper是整数。一旦我得到允许,我会发布我的答案。你没有发布你自己的答案,凯西的答案很好地展示了正确的用法。他甚至提到了隐式无的问题。@Vladimir F你能为我详细说明一下隐式无的位置吗,这样我尝试的解决方案就可以像现在写的那样工作?如果casey真的在逐字使用我尝试的解决方案,那么他唯一会使用隐式none的地方就是在他的程序测试中。重点是将您的程序放在模块或主程序中。然后,它们之间就有了明确的接口。隐式none只是帮助您更早地看到代码中的问题。你可以把它放在两个函数中。只将
fachhelper
声明为整数是处理外部过程的一种相当古老的方式。声明显式接口或将过程放在模块中更有意义。显式接口的好处是编译器可以检查参数类型,而不仅仅是返回类型。这是一种非常不幸的修复方法。当您有更高级的参数类型并且它不提供任何错误检查时,它将失败。使用模块或将其内部化要好得多。对于f2py来说,模块方法是一条路。@Vladmir错误检查与实现阶乘的演示请求无关。Haskell示例不执行错误检查,对于问题的意图和目的完全有效。也不执行任何错误检查。@casey通过将facHelper声明为整数,您是指“递归整数函数facHelper”,还是指阶乘内部的“integer::facHelper”?如果您可以根据底线问题“Fortran95中的尾部递归阶乘实现看起来如何?”修改您的答案,以包含一个完整的示例,我会更倾向于接受您的答案。我尝试的解决方案和Haskell实现只是作为激发底线问题答案的素材:“尾部递归阶乘实现在Fortran95中会是什么样子?”如果您可以修改您的答案来解决这个问题,我会更倾向于接受您的答案。如果上面不清楚,您在问题中建议的解决方案是它的外观(请注意,我复制并粘贴到我的示例中,根本没有修改它)。您的问题不在于实现尾部递归,而是与通用Fortran问题有关,接口规范与您的算法无关。这对我来说没有意义。问题中建议的解决方案没有说明“integer::facHelper”“在阶乘函数中,我甚至无法编译,直到它这么说。@user0xf00查看我的编辑。如果仍然不清楚,请告诉我。@user0xf00我还为您添加了一个模块示例。
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
recursive integer function facHelper(n, acc) result(returner)
function factorial(n)
  integer::n
  integer::factorial
  integer::facHelper ! <-- Missing information now available
  factorial = facHelper(n, 1)
end function factorial
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
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
gfortran --std=f95 -c ./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
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
*****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