2016-08-08 6 views
0

Ich schreibe einen einfachen Demo-Code, um meine Frage schnell zu präsentieren. Hier ist der Code, der nicht erfolgreich erstellt werden kann.Deklarieren Sie das Argument in der ausgewählten Case-Struktur

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 

Ich habe versucht, zu kompilieren, aber die 'Unerwartete Datendeklarationsanweisung' Fehler zeigt tritt in der Unterprogramm-Datei. Bedeutet es, dass ich den Argumenttyp nicht innerhalb der SELECT CASE-Struktur deklarieren kann? Das Problem ist, dass ich den Wert von a im Hauptprogramm definieren und in Subroutine sub (a) übergeben will. Der Argumenttyp von b und c sollte durch a entschieden werden, daher kann ich nicht vorher bestimmen. Ich möchte auch den Wert von b und c zurück an das Hauptprogramm übergeben, von dem ich nicht weiß, wie ich das machen soll. Wie kann ich das erreichen? Vielen Dank.

+0

Haben Sie Zugang zu einem modernen Fortran (2008+) Compiler? Wenn dies der Fall ist, können Sie das Konstrukt "Block" verwenden. – jlokimlin

+0

Danke. Ich versuche tatsächlich einige Änderungen an einem Programm mit Fortran 90 vorzunehmen, also möchte ich es auf diese Weise behalten, um mögliche Probleme zu vermeiden. – Ruizhi

+0

Ich verstehe deine Position nicht. Jedes standardkonforme Fortran-90-Programm, das keine gelöschten Features verwendet (fast niemand verwendete sie, da sie bereits als veraltet deklariert wurden) bleibt Fortran 2015 konform. Bedenken Sie, dass Fortran 90 fast 3 Jahrzehnte alt ist und nicht viele unterstützt moderne Programmierpraktiken. Welchen Compiler benutzen Sie? – jlokimlin

Antwort

2

Der Code, wie Sie es schreiben, ist illegal, wie Sie herausgefunden haben. Jetzt haben einige Leute auf die 2008-Feature von BLOCK Aussagen hingewiesen, und wenn das ist, was Sie brauchen, können Sie das versuchen. Aber ich würde gerne mehr darüber erfahren, was Sie damit machen wollen.

Die Tatsache, dass Sie ihnen den gleichen Namen geben, deutet darauf hin, dass Sie sie auf die gleiche Weise später behandeln wollen, was die Dinge wirklich schwierig macht.

Hier sind ein paar Alternativen:

1) Verwenden Sie separate Variablen:

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) Verwenden Sie zuweisbaren Arrays:

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 

Jetzt müssen Sie daran denken, dass b und c sind Arrays, möglicherweise mit der Länge 1. Sie müssen sie so behandeln.

Alle diese haben Vor- und Nachteile. Ohne zu wissen, warum Sie tun, was Sie tun, weiß ich nicht wirklich, wie ich Sie am besten beraten soll.

Zu Ihrer zweiten Frage: Die einfache Möglichkeit, sie zurückzugeben, ist als INTENT(OUT) Dummy-Argument.Hier ist ein funktionierendes Beispiel:

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 
+1

Da 'b' und' c' sind 'Absicht (out)' der Test des Zuweisungsstatus ist redundant: an diesem Punkt sind sie sicherlich nicht zugeordnet. – francescalus

+0

Danke, @francescalus. Normalerweise versuche ich, vorbereitet zu sein. Wenn das tatsächlich redundant ist, erwarte ich, dass der Compiler das entfernt. – chw21

+0

@ chw21 Vielen Dank für die Antwort. Ich möchte den Namen von b und c sowohl in skalaren als auch in vektoriellen Anlässen behalten, weil ich sie als überladene Eingabe einer anderen Schnittstelle verwenden möchte. Der Überladungsvorgang wählt das Unterprogramm automatisch aus, indem er den Eingabetyp identifiziert. Auf diese Weise kann ich die Anzahl der Variablen und die Anzahl der Unterprogramme minimal halten. – Ruizhi

3

Sie fragen also tatsächlich, wie man Skalar oder Array von einer Subroutine zurückgibt, nicht wie man Konstrukt-lokale Variablen deklariert. In diesem Fall sollten Sie zwei separate Subroutinen verwenden. Eine Version für Skalare und eine für Arrays. Sie können sie als generische Prozedur unter einem Namen überladen, wenn Sie möchten.

Denken Sie auch über ELEMENTAL, aber wenn Sie Skalar a verwenden, wird es nicht mit den Arrays arbeiten.


Wenn Sie noch wissen wollen, wie lokale Variablen deklarieren:

können

Variablen nur zu Beginn des Verfahrens oder zu Beginn eines Blocks deklariert werden. Das ist eine Fortran 2008-Funktion, die in den neuesten Versionen der gebräuchlichsten Compiler unterstützt wird (von PC-Compilern mindestens GNU und Intel).

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

Interessant. Aber überleben diese deklarierten Variablen das Ende der BLOCK-Anweisung? Ich vermute nicht. – chw21

+1

@ chw21 Natürlich nicht. Und wenn die vom OP vorgeschlagene Syntax existierte, würden sie auch nicht überleben. –

+1

b und c werden gespeichert, wenn b = und c = oder wenn SAVE angehängt ist. – Holmz

0

Dies ist nicht übermäßig elegant ...

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 
+0

Yeah - Wenn es behoben, um b und c zu lesen (von a und b). Und chw21 stocherte in einem sehr ähnlichen Ansatz, während ich an den Schlüsseln stocherte. – Holmz

+1

Eigentlich in Chw21 Antwort bringen Sie einen interessanten Punkt. Der INTENT muss möglicherweise für B und C INOUT sein. Ich weiß, dass UBOUND nicht weiß, was die Grenzen von B sind, ohne INTENT mit IN oder INOUT zu haben. Es ist ein bisschen kontra intuitiv, wenn Sie wissen, dass es im Grunde OUT ist, aber Sie brauchen das IN für einige der Dinge, die nicht offensichtlich sind. – Holmz

+0

Ich bin mir nicht sicher, ob ich deinen letzten Kommentar verstehe: 'b' und' c' sind keine Dummy-Argumente (also kann keine Absicht haben). Das bedeutet auch, dass wir (da sie nicht 'save'd sind) zu Beginn des Fallkonstrukts wissen, dass' b' und 'c' nicht zugewiesen sind: Geben eines Teils der Bedingungsanteile, die niemals ausgeführt werden. – francescalus

Verwandte Themen