Не ясно, были ли ответы на некоторые вопросы ОП. Кроме того, безусловно, в последующих ответах / обсуждениях, похоже, много путаницы и различных ошибок, которые могут извлечь пользу из некоторых разъяснений.
А) Вопрос ОП Re
«тогда я также хочу знать, почему кто-то хочет использовать намерение (out), поскольку намерение (inout) требует меньше работы (без копирования данных)».
, возможно, не ответил, или, по крайней мере, слишком прямо / правильно.
Во-первых, для ясности, атрибуты Intent
имеют по крайней мере ДВА цели: проблемы "безопасности / гигиены" и "косвенной производительности" (не проблемы "прямой производительности").
1) Безопасность / гигиена: Помогать в создании «безопасного / разумного» кода с ограниченной возможностью «испортить вещи». Таким образом, Intent (In) не может быть перезаписан (по крайней мере, локально или даже «глобально» при некоторых обстоятельствах, см. Ниже).
Аналогичным образом, Intent (Out) требует, чтобы Arg был назначен «явный ответ», что помогает уменьшить «мусорные» результаты.
Например, при решении, пожалуй, самой распространенной проблемы вычислительной математики, т. Е. Так называемой «проблемы Ax = b», ищется «прямой результат / ответ» - значения для вектора x. Они должны быть Intent (Out), чтобы гарантировать, что x назначен «явный» ответ. Если бы x было объявлено как, скажем, Intent (InOut) или «no Intent», то Fortran назначил бы x некоторые «значения по умолчанию» (вероятно, «нули» в режиме отладки, но, скорее всего, «мусор» в режиме Release, независимо от того, что находится в памяти в месте расположения указателя Args), и если пользователь не назначит правильные значения x явно, он выдаст «мусор». Intent (Out) будет «напоминать / заставлять» пользователя явно назначать значения для x, и таким образом устранять этот вид «(случайного) мусора».
Во время процесса решения можно было бы (почти наверняка) произвести инверсию матрицы A. Пользователь может пожелать вернуть эту инверсию вместо вызывающего s / r вместо A, и в этом случае A должно быть Intent (InOut) ,
В качестве альтернативы пользователь может пожелать убедиться, что в матрицу A или вектор b не было внесено никаких изменений, и в этом случае они будут объявлены Intent (In), и, таким образом, гарантировать, что критические значения не будут перезаписаны.
2 a) «Непрямая производительность» (и «глобальная безопасность / гигиена»): хотя Intents не оказывают непосредственного влияния на производительность, они делают это косвенно. Примечательно, что некоторые типы оптимизации, в частности конструкции Fortran Pure и Elemental, могут значительно улучшить производительность. Эти настройки обычно требуют, чтобы у всех Args было явно объявлено намерение.
Грубо говоря, если компилятор заранее знает Intent всех переменных, он может оптимизировать и "проверить глупость" кода с большей легкостью и эффективностью.
Крайне важно, если кто-то использует конструкции Pure и т. Д., То, с высокой вероятностью, также будет существовать «вид глобальной безопасности / гигиены», поскольку Pure / Elemental s / p могут вызывать только другие Pure / Elemental s / p. и поэтому один НЕ МОЖЕТ прийти к ситуации, подобной той, которая указана в примере «Глазерского парня».
Например, если Sub1 () объявлен как Pure, то Sub2 () также должен быть объявлен как Pure, а затем потребуется объявить Intents на всех уровнях, и, таким образом, «мусор», произведенный в Пример Глейзер Парня не может произойти. То есть код будет:
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
... при компиляции это выдает что-то вроде
"|| Ошибка: фиктивный аргумент 'i' с INTENT (IN) в контексте определения переменной (присваивании) в (1) |"
Конечно, sub2 не обязательно должен быть Pure, чтобы я объявил его Intent (In), что опять-таки обеспечит "безопасность / гигиену", которую ищет каждый.
Обратите внимание, что даже если бы меня объявили 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
... при компиляции это выдает что-то вроде
"|| Ошибка: фиктивный аргумент 'i' с INTENT (IN) в контексте определения переменной (фактический аргумент для INTENT = OUT / INOUT) в (1) |"
Таким образом, строгое или широкое использование конструктов Pure / Elemental обеспечит (в основном) «глобальную безопасность / гигиену».
Будет невозможно использовать Pure / Elemental и т. Д. Во всех случаях (например, во многих смешанных языковых настройках или когда вы полагаетесь на внешние библиотеки вне вашего контроля и т. Д.).
Тем не менее, последовательное использование Intents, и, по возможности, Pure и т. Д., Принесет много пользы и избавит от большого горя.
Можно просто привыкнуть объявлять намерения везде, когда это возможно, будь то Чистый или нет ... это рекомендуемая практика кодирования.
... это также выдвигает на передний план еще одну причину существования ОБА Intent (InOut) и Intent (Out), поскольку у Pure должны быть объявлены все намерения Arg, будут только некоторые Args, которые Out, а другие являются InOut (то есть было бы трудно иметь Pure без каждого из In, InOut и Out Intents).
2 b) Комментарии ФП, ожидающие «улучшения производительности», поскольку копирование не требуется », указывают на неправильное понимание Фортрана и его широкого использования передачи по ссылке. Пропущенный по ссылке означает, по сути, требуются только указатели, и фактически часто требуется только указатель на первый элемент в массиве (плюс небольшая скрытая информация о массиве).
Действительно, некоторая информация может быть предложена с учетом «старых времен» (например, Фортран 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
В современном Фортране «эквивалент» - объявлять A как Real (DP) A (:) в s / r (хотя, строго говоря, существуют различные настройки, которые выигрывают от передачи границ массива и явного объявления с границами , но это будет длительное отступление на следующий день).
То есть Фортран не передает по значению и не "делает копии" для переменных Args / Dummy. A () в вызывающем s / r является «тем же A», что и используемый в s / r (конечно, в s / r можно сделать копию A () или что-то еще, что создаст дополнительные требования к работе / месту, но это другое дело).
По этой причине, в первую очередь, намерения не оказывают существенного влияния на производительность в значительной степени, даже для большого массива Arg и т. Д.
B) Относительно путаницы «передача по значению»: хотя различные приведенные выше ответы подтверждают, что использование намерения является «не передачей по значению», может оказаться полезным прояснить этот вопрос.
Может помочь изменить формулировку на «Намерение всегда передается по ссылке». Это не то же самое, что «не передать по значению», и это важная тонкость. Примечательно, что Intent не только "byRef", но и Intent может PREVENT передавать по значению.
Несмотря на то, что существуют специальные / гораздо более сложные настройки (например, библиотеки Фортрана на разных языках и т. Д.), Где требуется много дополнительных обсуждений, для большей части "стандартного Фортрана" аргументы передаются с помощью 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
То есть все, что имеет аспект «Out», должно быть «byRef» (по крайней мере, в обычных настройках), поскольку вызывающий s / r ожидает «byRef». Таким образом, даже если все s / r объявляют Args как «Value», они «byVal» только локально (опять же в стандартных настройках). Таким образом, любая попытка вызываемого s / r вернуть Arg, который объявлен как Value с любым видом Out Intent, потерпит неудачу из-за «столкновения» передаваемых стилей.
Если это должны быть «Out» или «InOut» и «Value», то нельзя использовать Intent: что-то большее, чем просто сказать «оно не передается по значению».