在Fortran 90中计算两个向量的叉积



我想在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。看来我指定的尺寸一定不正确。以下是我的一些想法:

  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
    

    所以这可能更糟。

  2. web上的一些(但不是全部)Fortran函数示例将EXTERNAL语句放在主程序中的函数声明之后。因此,我尝试在主程序中的声明块之后放置一行EXTERNAL cross。我收到一条错误消息:

    crosstest.f90:8.16:
      EXTERNAL cross
                    1
    Error: EXTERNAL attribute conflicts with DIMENSION attribute at (1)
    

    所以这似乎也是不正确的。

  3. 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)是一个函数调用或数组索引。

最新更新