【问题标题】:Declare argument inside select case structure在选择案例结构中声明参数
【发布时间】:2016-08-08 02:52:44
【问题描述】:

我编写了一个简单的演示代码来快速提出我的问题。这是代码,无法成功构建。

Main.f90

PROGRAM test
IMPLICIT NONE

    INTEGER    ::   a
    a = 1
    CALL sub(a)

END PROGRAM

sub.f90

SUBROUTINE sub(a)
IMPLICIT NONE

    INTEGER    ::   a
    SELECT CASE(a)
        CASE(1)
            INTEGER     ::   b,c
            b = a
            c = a*2
        CASE(2)
            INTEGER     ::   b(4),c(4)
            b(:) = a
            c(:) = a*2
    END SELECT

END SUBROUTINE

我试图编译,但错误显示子程序文件中出现'Unexpected data declaration statement'。这是否意味着我不能在 SELECT CASE 结构中声明参数类型?问题是我想在主程序中定义 a 的值并将其传递给子程序 sub(a)。 b 和 c 的参数类型应该由 a 决定,因此我无法提前确定。我还想将 b 和 c 的值传递回主程序,我不知道该怎么做。那么我该如何实现呢?谢谢。

【问题讨论】:

  • 您可以使用现代 Fortran (2008+) 编译器吗?如果是这样,您可以使用block 构造。
  • 谢谢。我实际上是在尝试对使用 Fortran 90 的程序进行一些更改,所以我可能希望保持这种方式以防止潜在的麻烦。
  • 我不明白你的立场。任何不使用已删除功能的符合标准的 Fortran 90 程序(几乎没有人使用它们,因为它们已被宣布过时)仍然符合标准的 Fortran 2015。请记住,Fortran 90 已有近 3 年的历史并且不支持很多现代编程实践。你用的是什么编译器?

标签: fortran


【解决方案1】:

所以您实际上是在问如何从某个子例程返回标量或数组,而不是如何声明构造局部变量。在这种情况下,请考虑使用两个单独的子例程。一种用于标量,另一种用于数组。如果需要,您可以将它们作为通用过程重载在一个名称下。

还可以考虑ELEMENTAL,但如果您使用标量a,它将不适用于数组。


如果你还想知道如何声明局部变量:

变量只能在过程的开头或块的开头声明。这是最新版本的最常见编译器(至少来自 GNU 和 Intel 的 PC 编译器)支持的 Fortran 2008 功能。

SELECT CASE(a)
    CASE(1)
        BLOCK
          INTEGER     ::   b,c
          b = a
          c = a*2
        END BLOCK

【讨论】:

  • 有趣。但是这些声明的变量是否在BLOCK 语句的末尾仍然存在?我怀疑不是。
  • @chw21 当然不是。如果 OP 建议的语法存在,它们也将无法生存。
  • 如果 b= 和 c=,或者如果附加了 SAVE,则保存 b 和 c。
  • 不同的是,保存它们保留值但仍然只在块内有效。
  • 在这种情况下,他应该创建两个子程序。一个典型的例子,有人要求一些细节,但他的真正问题是不同的。
【解决方案2】:

正如您所发现的,您编写的代码是非法的。现在有些人指出了BLOCK 语句的2008 年特性,如果这是你需要的,你可以试试。但我想了解更多关于你想用这个做什么。

你给他们取相同的名字这一事实向我表明,你以后想以同样的方式对待他们,这让事情变得非常棘手。

这里有几个选择:

1) 使用单独的变量:

INTEGER :: b_scalar, c_scalar, b_array(4), c_array(4)
select case(a)
    case(1)
        b_scalar = a
        c_scalar = 2*b_scalar
    case(2)
        b_array = a
        c_array = 2*b_array
end select

2) 使用可分配数组:

integer, dimension(:), allocatable :: b, c
select case(a)
    case(1)
        allocate(b(1), c(1))
    case(2)
        allocate(b(4), c(4))
end select
b = a
c = 2 * b

现在您必须记住 bc 是数组,长度可能为 1。您必须这样对待它们。

所有这些都有优点和缺点。在不知道你为什么要做你正在做的事情的情况下,我真的不知道如何最好地给你建议。

关于您的第二个问题:返回它们的简单方法是作为 INTENT(OUT) 虚拟参数。这是一个工作示例:

module mod_allocatable
contains
    subroutine my_sub(a, b, c)
        implicit none
        integer, intent(in) :: a
        integer, dimension(:), allocatable, intent(out) :: b, c
        if (allocated(b)) deallocate(b)
        if (allocated(c)) deallocate(c)
        select case(a)
            case(1)
                allocate(b(1), c(1))
            case(2)
                allocate(b(4), c(4))
        end select
        b = a
        c = 2 * b
      end subroutine my_sub
end module mod_allocatable

program test_alloc
    use mod_allocatable
    implicit none
    integer :: a
    integer, allocatable, dimension(:) :: b, c
    a = 1
    call my_sub(a, b, c)
    print *, "b is ", b
    print *, "c is ", c
end program test_alloc

【讨论】:

  • 由于bcintent(out),分配状态的测试是多余的:在这一点上它们肯定没有被分配。
  • 谢谢,@francescalus。我通常会尽量做好准备。如果这确实是多余的,我希望编译器将其删除。
  • @chw21 非常感谢您的回答。我想在标量和向量情况下保留 b 和 c 的名称,因为我想将它们用作另一个接口的重载输入。重载程序将通过识别输入类型自动选择子程序。这样我可以保持变量数和子程序数最少。
【解决方案3】:

这并不过分优雅……

  SUBROUTINE sub(a)
    IMPLICIT NONE

    INTEGER,               INTENT(IN)   ::   a
    INTEGER, DIMENSION(:), ALLOCATABLE  :: b, c

    SELECT CASE(a)
        CASE(1)
            IF(ALLOCATED(B)) THEN
              IF(UBOUND(B)) .NE. 1) THEN
                DEALLOCATE(B)
                ALLOCATE(B(1))
              ENDIF
            ELSE
              ALLOCATE(B(1))
            ENDIF

            IF(ALLOCATED(C)) THEN
              IF(UBOUND(C)) .NE. 1) THEN
                DEALLOCATE(c)
                ALLOCATE(C(1))
              ENDIF
            ELSE
              ALLOCATE(C(1))
            ENDIF

            b = a
            c = a*2
        CASE(2)
            IF(ALLOCATED(B)) THEN
              IF(UBOUND(B)) .NE. 4) THEN
                DEALLOCATE(B)
                ALLOCATE(B(4))
              ENDIF
            ELSE
              ALLOCATE(B(4))
            ENDIF

            IF(ALLOCATED(C)) THEN
              IF(UBOUND(C)) .NE. 4) THEN
                DEALLOCATE(C)
                ALLOCATE(C(4))
              ENDIF
            ELSE
              ALLOCATE(C(4))
            ENDIF

            b(:) = a
            c(:) = a*2
        CASE(DEFAULT)
            WRITE(*,*)'how did we get here?... a=',a
    END SELECT

END SUBROUTINE Sub

【讨论】:

  • 是的 - 如果将其固定为读取 b 和 c(来自 a 和 b)。而 chw21 在我拨弄按键时以非常相似的方式拨弄。
  • 实际上在chw21的回答中提出了一个有趣的观点。 B 和 C 的 INTENT 可能需要为 INOUT。我知道 UBOUND 在没有 IN 或 INOUT 的情况下不知道 B 的边界是什么。当你知道它基本上是 OUT 时,这有点反直觉,但你需要 IN 来处理一些不明显的东西。
  • 我不确定我是否理解您的最终评论:bc 不是虚拟参数(因此不能有意图)。这也意味着(因为它们不是 saved)我们在 case 构造的开头就知道 bc 没有分配:给出一块永远不会执行的条件部分。
  • 我想 b 和 c 是虚拟参数,只是错误地被排除在参数列表之外。
猜你喜欢
  • 2013-08-08
  • 1970-01-01
  • 2022-07-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-11-18
  • 2016-11-24
相关资源
最近更新 更多