我想在Fortran 90中计算两个向量的叉积。例如,换句话说,(1,2,3)和(4,5,6)的叉积在笛卡尔坐标系中是(-3,6,-3)。我写了以下代码(主程序后面跟着函数定义):
PROGRAM crosstest
IMPLICIT NONE
INTEGER, DIMENSION(3) :: m, n
INTEGER, DIMENSION(3) :: cross
INTEGER, DIMENSION(3) :: r
m=(/1, 2, 3/)
n=(/4, 5, 6/)
r=cross(m,n)
END PROGRAM crosstest
FUNCTION cross(a, b)
INTEGER, DIMENSION(3) :: cross
INTEGER, DIMENSION(3), INTENT(IN) :: a, b
cross(1) = a(2) * b(3) - a(3) * b(2)
cross(2) = a(3) * b(1) - a(1) * b(3)
cross(3) = a(1) * b(2) - a(2) * b(1)
END FUNCTION cross
但是,我收到一条错误消息:
crosstest.f90:10.9:
r=cross(m,n)
1
Error: Rank mismatch in array reference at (1) (2/1)
其中线10是CCD_ 1。看来我指定的尺寸一定不正确。以下是我的一些想法:
也许主程序中函数
cross
的声明应该只是一个整数变量,而不是1by3整数数组。所以我尝试删除主程序中INTEGER, DIMENSION(3) :: cross
行中的, DIMENSION(3)
。但我收到一条错误消息:crosstest.f90:10.4: r=cross(m,n) 1 Error: The reference to function 'cross' at (1) either needs an explicit INTERFACE or the rank is incorrect
所以这可能更糟。
web上的一些(但不是全部)Fortran函数示例将
EXTERNAL
语句放在主程序中的函数声明之后。因此,我尝试在主程序中的声明块之后放置一行EXTERNAL cross
。我收到一条错误消息:crosstest.f90:8.16: EXTERNAL cross 1 Error: EXTERNAL attribute conflicts with DIMENSION attribute at (1)
所以这似乎也是不正确的。
web上的一些(但不是全部)Fortran函数示例将
RETURN
语句放在函数定义的倒数第二行。我试过这个,但我得到了原始排名不匹配的错误:crosstest.f90:10.9: r=cross(m,n) 1 Error: Rank mismatch in array reference at (1) (2/1)
因此,这并不能解决问题。
你能帮我看看我的错误吗?
最佳做法是将过程(子例程和函数)放在一个模块中,然后从主程序或其他过程中"使用"该模块。您不需要"使用"同一模块的其他过程中的模块。这将使过程的接口显式,以便调用程序或过程"知道"参数的特性。。。它允许编译器检查双方参数之间的一致性。。。呼叫者和被呼叫者。。这消除了很多错误。
在语言标准之外,但在实践中是必要的:如果你使用一个文件,请将模块放在使用它的主程序之前。否则编译器将不知道它。所以:
module my_subs
implicit none
contains
FUNCTION cross(a, b)
INTEGER, DIMENSION(3) :: cross
INTEGER, DIMENSION(3), INTENT(IN) :: a, b
cross(1) = a(2) * b(3) - a(3) * b(2)
cross(2) = a(3) * b(1) - a(1) * b(3)
cross(3) = a(1) * b(2) - a(2) * b(1)
END FUNCTION cross
end module my_subs
PROGRAM crosstest
use my_subs
IMPLICIT NONE
INTEGER, DIMENSION(3) :: m, n
INTEGER, DIMENSION(3) :: r
m= [ 1, 2, 3 ]
n= [ 4, 5, 6 ]
r=cross(m,n)
write (*, *) r
END PROGRAM crosstest
这是一个迟来的答案,但由于我偶然发现了这个问题,而且还没有真正的解释为什么会出现错误,我想我应该为其他偶然发现这个问题的人添加一个解释:
在程序中,定义一个名为cross
的数组,该数组的级别为1。然后调用您进一步定义的cross
函数。由于r=cross(m,n)
0函数没有显式接口(请参见M.S.B.的答案),编译器目前还不知道它。它所知道的是您声明的数组。如果编写r = cross(m, n)
,编译器会认为您想要访问数组cross
的位置(m,n)处的元素。由于此数组的秩为1,但您提供了两个参数,因此会得到错误
rank mismatch in array reference at (1) (2/1)
这意味着当编译器期望提供两个坐标时,您提供了两个坐标。
您可以将程序中使用的子例程放在程序中的contains
关键字之后。这消除了创建模块或添加接口定义的需要。
PROGRAM crosstest
IMPLICIT NONE
INTEGER, DIMENSION(3) :: m, n
INTEGER, DIMENSION(3) :: r
m = (/1, 2, 3/)
n = (/4, 5, 6/)
r = cross(m,n)
print *, r
CONTAINS
PURE FUNCTION cross(a, b)
INTEGER, DIMENSION(3) :: cross
INTEGER, DIMENSION(3), INTENT(IN) :: a, b
cross(1) = a(2) * b(3) - a(3) * b(2)
cross(2) = a(3) * b(1) - a(1) * b(3)
cross(3) = a(1) * b(2) - a(2) * b(1)
END FUNCTION cross
END PROGRAM crosstest
cross
解释为数组,而不是函数名。因此,主程序中的cross
与您定义的函数无关。为了让编译器知道cross
是一个函数名,您可以在主程序中定义:
INTEGER :: cross
然后,当编译器看到行r=cross(m,n)
时,编译器知道这不是数组索引,因为cross
在主程序中没有定义为数组。剩下的可能性是这是一个函数调用,Fortran编译器通常采用这种可能性。
对于您的情况,编译器会发现cross
函数返回的是数组,而不是标量。对于这种情况,编译器坚持要求您为cross
提供一个显式接口。因此,此变通方法不适用于您的情况。正如其他anwser建议的那样,您需要通过提供一个显式接口
- 在模块中包含函数定义,并在主程序中使用该模块
或
- 包含主程序中的函数定义
Fortran将圆括号用于函数调用和数组索引。这种设计经常会引起混乱,因为程序员在没有额外信息的情况下无法弄清楚f(3)
是一个函数调用或数组索引。