포트란 의도(in, out, inout)의 명시적인 차이점은 무엇입니까?

StackOverflow https://stackoverflow.com/questions/1011604

  •  06-07-2019
  •  | 
  •  

문제

책, 여기 stackoverflow 및 일반 웹에서 잠시 검색한 후 포트란 인수 의도 간의 실제 차이점에 대한 간단한 설명을 찾기가 어렵다는 것을 발견했습니다.제가 이해한 방식은 이렇습니다.

  • intent(in) -- 실제 인수는 입력 시 더미 인수에 복사됩니다.
  • intent(out) -- 더미 인수는 실제 인수를 가리킵니다(둘 다 메모리의 동일한 위치를 가리킵니다).
  • intent(inout) -- 더미 인수가 로컬로 생성된 다음 프로시저가 완료되면 실제 인수에 복사됩니다.

내 이해가 정확하다면 왜 누군가가 사용하고 싶어하는지 알고 싶습니다. intent(out), 이후 intent(inout) 작업이 덜 필요합니다(데이터 복사 없음).

도움이 되었습니까?

해결책

의도는 컴파일러의 힌트 일 뿐이며 해당 정보를 버리고 위반할 수 있습니다. 의도는 서브 루틴에서 계획 한 일만 할 수 있도록 거의 전적으로 존재합니다. 컴파일러는 당신을 신뢰하고 무언가를 최적화하도록 선택할 수 있습니다.

이것은 그것을 의미합니다 intent(in) 가치별로 통과하지 않습니다. 원래 값을 덮어 쓸 수 있습니다.

program xxxx  
    integer i  
    i = 9  
    call sub(i)  
    print*,i ! will print 7 on all compilers I checked  
end  
subroutine sub(i)  
    integer,intent(in) :: i  
    call sub2(i)  
end  
subroutine sub2(i)  
    implicit none  
    integer i  
    i = 7  ! This works since the "intent" information was lost.  
end

program xxxx  
    integer i  
    i = 9  
    call sub(i)  
end  
subroutine sub(i)  
    integer,intent(out) :: i  
    call sub2(i)  
end  
subroutine sub2(i)  
    implicit none   
    integer i  
    print*,i ! will print 9 on all compilers I checked, even though intent was "out" above.  
end  

다른 팁

  • intent(in) - 통과 값으로 보입니다 (그리고 이것의 변경 사항은 외부 코드에 반영되지 않지만 실제로 참조로 통과하고 변경하면 컴파일러에 의해 금지됩니다. 그러나 여전히 변경 될 수 있습니다.
  • intent(out) - 참조로 어떻게 든 통과, 사실 반환 인수
  • intent(inout) - 참조별로 전달, 정상 인/아웃 매개 변수.

사용 intent(out) 분명한 경우 디자인을 문서화하십시오. 성능이 적은 성능 이득을 신경 쓰지 마십시오. (의견은 아무것도 없다고 제안합니다 intent(in) 기술적으로도 참조로 통과합니다.)

OP 질문의 일부가 실제로 답변되었는지는 확실하지 않습니다.게다가, 확실히 몇 가지 설명을 통해 도움이 될 수 있는 후속 답변/토론에는 많은 혼란과 다양한 오류가 있는 것 같습니다.

A) OP의 질문 Re

"그런 다음 의도(inout)에는 작업이 덜 필요하므로(데이터 복사 없음) 왜 의도(out)를 사용하려는지 알고 싶습니다."

답변하지 않았거나 적어도 너무 직접적으로/정확하게 답변했을 수 있습니다.

먼저, 명확하게 말하자면 Intent 속성에는 최소한 두 가지 목적이 있습니다."안전/위생" 및 "간접 성과" 문제("직접 성과" 문제 아님).

1) 안전/위생:"엉망진창"할 가능성을 줄이면서 "안전하고 합리적인" 코드를 생성하는 데 도움을 줍니다.따라서 Intent(In)은 덮어쓸 수 없습니다(적어도 로컬로 또는 일부 상황에서는 "전역적으로", 아래 참조).

마찬가지로 Intent(Out)에서는 Arg에 "명시적 답변"이 할당되어야 하므로 "쓸데없는" 결과를 줄이는 데 도움이 됩니다.

예를 들어, 아마도 계산 수학에서 가장 일반적인 문제의 해결에서, 즉소위 "Ax=b 문제", 사람이 찾고 있는 "직접 결과/대답"은 벡터 x의 값입니다.x에 "명시적" 답변이 할당되도록 하려면 Intent(Out)이어야 합니다.예를 들어 x가 Intent(InOut) 또는 "의도 없음"으로 선언된 경우 Fortran은 x에 일부 "기본값"(디버그 모드에서는 "0"일 수 있지만 릴리스 모드에서는 "쓰레기"일 가능성이 높음)을 할당합니다. Args 포인터 위치의 메모리), 사용자가 명시적으로 x에 올바른 값을 할당하지 않으면 "쓰레기"가 반환됩니다.Intent(Out)는 사용자에게 x에 명시적으로 값을 할당하도록 "알림/강제"하여 이러한 종류의 "(우연한) 쓰레기"를 방지합니다.

풀이 과정에서 행렬 A의 역행렬이 (거의 확실하게) 생성됩니다.사용자는 A 대신 호출 s/r에 그 역을 반환하기를 원할 수 있으며, 이 경우 A는 Intent(InOut)이어야 합니다.

또는 사용자는 행렬 A 또는 벡터 b가 변경되지 않았는지 확인하고 싶을 수도 있습니다. 이 경우 Intent(In)으로 선언되어 중요한 값을 덮어쓰지 않도록 할 수 있습니다.

2 a) "간접 성과"(및 "글로벌 안전/위생"):인텐트가 성능에 직접적으로 영향을 미치는 것은 아니지만 간접적으로 영향을 미칩니다.특히 특정 유형의 최적화, 특히 Fortran Pure 및 Elemental 구성은 훨씬 향상된 성능을 제공할 수 있습니다.이러한 설정에서는 일반적으로 모든 Arg에서 의도를 명시적으로 선언해야 합니다.

대략적으로 말하자면, 컴파일러가 모든 변수의 의도를 미리 알고 있다면 코드를 더 쉽고 효과적으로 최적화하고 "어리석음 검사"할 수 있습니다.

결정적으로 Pure 등의 구성을 사용하는 경우 Pure/Elemental s/p는 다른 Pure/Elemental s/p만 호출할 수 있으므로 "일종의 글로벌 안전/위생"도 있을 가능성이 높습니다. "The Glazer Guy's" 예에 표시된 것과 같은 상황에 도달할 수 없습니다.

예를 들어 Sub1()이 Pure로 선언된 경우 Sub2()도 Pure로 선언되어야 하며 모든 수준에서 Intents를 선언해야 하므로 "The Glazer Guy's"에서 "쓰레기 배출"이 생성됩니다. "예는 일어날 수 없습니다.즉, 코드는 다음과 같습니다.

Pure subroutine sub_P(i)
    integer,intent(in) :: i
    call sub2_P(i)
end  subroutine sub_P

Pure subroutine sub2_P(i)
    implicit none
!        integer i          ! not permitted to omit Intent in a Pure s/p
    integer,intent(in) :: i
    i = 7   ! This WILL NOT WORK/HAPPEN, since Pure obviates the possibility of omitting Intent, and Intent(In) prohibits assignment ... so "i" remains "safe".
end  subroutine sub2_P

...컴파일하면 다음과 같은 결과가 생성됩니다.

" ||오류:(1)| "

물론 i를 Intent(In)로 선언하기 위해 sub2가 Pure일 필요는 없습니다. 이는 다시 찾고 있는 "안전성/위생"을 제공합니다.

Intent(InOut)으로 선언되더라도 Pure에서는 여전히 실패할 것입니다.그건:

Pure subroutine sub_P(i)
    integer,intent(in) :: i
    call sub2_P(i)
end  subroutine sub_P

Pure subroutine sub2_P(i)
    implicit none
    integer,intent(inOut) :: i
    i = 7   ! This WILL NOT WORK, since Pure obviates the possibility of "mixing" Intent's.
end  subroutine sub2_P

...컴파일하면 다음과 같은 결과가 생성됩니다.

"||오류:(1)|"에서 변수 정의 컨텍스트(INTENT = OUT/INOUT에 대한 실제 인수)에서 INTENT(IN)가 있는 더미 인수 'i'

따라서 Pure/Elemental 구성에 대한 엄격하거나 광범위한 의존은 (대부분) "글로벌 안전/위생"을 보장합니다.

모든 경우에 Pure/Elemental 등을 사용할 수는 없습니다(예:많은 혼합 언어 설정 또는 통제할 수 없는 외부 라이브러리에 의존하는 경우 등).

그럼에도 불구하고 인텐트를 일관되게 사용하고 가능할 때마다 Pure 등을 사용하면 많은 이점을 얻을 수 있고 많은 슬픔을 없앨 수 있습니다.

순수하든 아니든 가능하다면 항상 모든 곳에서 인텐트를 선언하는 습관을 들일 수 있습니다.이것이 권장되는 코딩 방법입니다.

...이는 또한 Intent(InOut)와 Intent(Out)이 모두 존재하는 또 다른 이유를 제시합니다. Pure의 경우 모든 Arg의 Intent가 선언되어야 하기 때문에 일부 Arg는 Out 전용이고 다른 Arg는 InOut입니다(예:In, InOut, Out 인텐트가 각각 없이 Pure를 사용하는 것은 어려울 것입니다.

2 b) "복사가 필요하지 않기 때문에 "성능 향상"을 기대하는 OP의 의견은 Fortran에 대한 오해와 참조에 의한 전달의 광범위한 사용을 나타냅니다.참조로 전달된다는 것은 본질적으로 포인터만 필요하다는 뜻이며 실제로는 배열의 첫 번째 요소에 대한 포인터(몇 가지 숨겨진 배열 정보 포함)만 필요한 경우가 많습니다.

실제로 "옛날"을 고려하면 어느 정도 통찰력이 제공될 수 있습니다(예:Fortran IV, 77 등), 배열을 전달할 때 다음과 같이 코딩되었을 수 있습니다.

Real*8 A(1000)

Call Sub(A)

Subroutine Sub(A)

Real*8 A(1) ! this was workable since Fortran only passes the pointer/by ref to the first element of A(1000)
                ! modern Fortran may well throw a bounds check warning

현대 Fortran에서 "동등한" 방법은 s/r에서 A를 Real(DP) A(:)로 선언하는 것입니다(엄밀히 말하면 배열의 경계를 전달하고 경계를 명시적으로 선언하면 이점을 얻을 수 있는 다양한 설정이 있지만 다른 날에는 긴 여담이 될 것입니다).

즉, Fortran은 Args/Dummy vars에 대해 값을 전달하거나 "복사본 만들기"를 수행하지 않습니다.호출 s/r의 A()는 s/r에서 사용된 것과 "동일한 A"입니다(물론 s/r에서는 A() 또는 기타 항목의 복사본을 만들 수 있습니다. 작업/공간 요구 사항이 있지만 이는 또 다른 문제입니다).

이러한 이유로 인텐트는 대규모 배열 Arg 등의 경우에도 성능에 큰 영향을 미치지 않습니다.

B) "값 전달" 혼동에 관해:위의 다양한 응답을 통해 Intent를 사용하는 것이 "값 전달이 아님"을 확인했지만 문제를 명확히 하는 것이 도움이 될 수 있습니다.

"의도는 항상 참조로 전달됩니다"라는 문구를 변경하는 것이 도움이 될 수 있습니다.이는 "값을 전달하지 않음"과 동일하지 않으며 중요한 미묘함입니다.특히 인텐트는 "byRef"일 뿐만 아니라 값으로 전달되는 것을 방지할 수 있습니다.

특별하거나 훨씬 더 복잡한 설정이 있지만(예:많은 추가 논의가 필요한 혼합 언어 Fortran DLL 등), "표준 Fortran"의 대부분의 경우 Args는 Ref에 의해 전달됩니다.이 "의도의 미묘함"에 대한 시연은 다음과 같이 "The Glazer Guys" 예제의 간단한 확장에서 볼 수 있습니다.

subroutine sub(i)
    integer, intent(in) :: i, j
    integer, value     :: iV, jV
    call sub2(i)
    call sub3(i, j, jV, iV)
end
subroutine sub2(i)
    implicit none
    integer i
    i = 7  ! This works since the "intent" information was lost.
end
subroutine sub3(i, j, jV, iV)
    implicit none
    integer, value, Intent(In)          :: i    ! This will work, since passed in byRef, but used locally as byVal
    integer, value, Intent(InOut)       :: j    ! This will FAIL, since ByVal/ByRef collision with calling s/r,
                                                ! ||Error: VALUE attribute conflicts with INTENT(INOUT) attribute at (1)|
    integer, value, Intent(InOut)       :: iV   ! This will FAIL, since ByVal/ByRef collision with calling s/r,
                                                ! ... in spite of "byVal" in calling s/r
                                                ! ||Error: VALUE attribute conflicts with INTENT(INOUT) attribute at (1)|
    integer, value, Intent(Out)         :: jV   ! This will FAIL, since ByVal/ByRef collision with calling s/r
                                                ! ... in spite of "byVal" in calling s/r
                                                ! ||Error: VALUE attribute conflicts with INTENT(OUT) attribute at (1)|
    jV = -7
    iV = 7
end

즉, 호출 s/r이 "byRef"를 기대하기 때문에 "Out" 측면이 있는 모든 항목은 "byRef"(적어도 일반 설정에서는)여야 합니다.따라서 모든 s/r이 Args를 "Value"로 선언하더라도 로컬에서만 "byVal"입니다(표준 설정에서도 마찬가지입니다).따라서 모든 종류의 Out Intent를 사용하여 Value로 선언된 Arg를 반환하려는 호출된 s/r의 시도는 전달 스타일의 "충돌"로 인해 실패하게 됩니다.

"Out" 또는 "InOut" 및 "Value"여야 하는 경우 Intent를 사용할 수 없습니다.이는 단순히 "가치에 의한 전달이 아니다"라고 말하는 것 이상입니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top