ما هو الفرق الواضح بين نوايا فورتران (داخل، خارج، داخل)؟

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 قد تمت الإجابة عليها بالفعل.بالإضافة إلى ذلك، يبدو بالتأكيد أن هناك الكثير من الارتباك والأخطاء المختلفة في الإجابات/المناقشات اللاحقة التي قد تستفيد من بعض التوضيحات.

أ) سؤال OP Re

"ثم أريد أيضًا أن أعرف سبب رغبة المرء في استخدام القصد (الخروج)، نظرًا لأن القصد (الخروج) يتطلب عملاً أقل (لا يوجد نسخ للبيانات)."

ربما لم يجيب، أو على الأقل بشكل مباشر/صحيح.

أولا، لتوضيح Intent السمات لها غرضين على الأقل:قضايا "السلامة/النظافة" و"الأداء غير المباشر" (وليست قضايا "الأداء المباشر").

1) السلامة/النظافة:للمساعدة في إنتاج كود "آمن/معقول" مع تقليل فرصة "إفساد الأمور".وبالتالي، لا يمكن الكتابة فوق Intent(In) (على الأقل محليًا، أو حتى "عالميًا" في بعض الظروف، انظر أدناه).

وبالمثل، تتطلب Intent(Out) تعيين "إجابة صريحة" للوسيط، مما يساعد على تقليل النتائج "غير المرغوب فيها".

على سبيل المثال، في حل المشكلة الأكثر شيوعًا في الرياضيات الحسابية، أي.ما يسمى "مشكلة Ax=b"، "النتيجة/الإجابة المباشرة" التي يبحث عنها المرء هي قيم المتجه x.يجب أن تكون هذه Intent(Out) للتأكد من تعيين x إجابة "صريحة".إذا تم الإعلان عن x كـ Intent(InOut) أو "no Intent"، على سبيل المثال، فسيقوم Fortran بتعيين x بعض "القيم الافتراضية" (ربما "صفر" في وضع التصحيح، ولكن من المحتمل أن تكون "قمامة" في وضع الإصدار، كونها كل ما هو موجود في الذاكرة في موقع مؤشر Args)، وإذا لم يقم المستخدم بعد ذلك بتعيين القيم الصحيحة إلى x بشكل صريح، فسوف يُرجع "قمامة".إن Intent(Out) من شأنه "تذكير/إجبار" المستخدم على تعيين قيم صريحة لـ x، وبالتالي تجنب هذا النوع من "القمامة (العرضية)".

أثناء عملية الحل، يمكن للمرء (بشكل شبه مؤكد) إنتاج معكوس المصفوفة A.قد يرغب المستخدم في إعادة ذلك المعكوس إلى s/r المتصل بدلاً من A، وفي هذه الحالة يجب أن يكون A Intent(InOut).

وبدلاً من ذلك، قد يرغب المستخدم في التأكد من عدم إجراء أي تغييرات على المصفوفة A أو المتجه b، وفي هذه الحالة سيتم الإعلان عنها Intent(In)، وبالتالي ضمان عدم الكتابة فوق القيم الحرجة.

2 أ) "الأداء غير المباشر" (و"السلامة/النظافة العامة على المستوى العالمي"):على الرغم من أن المقاصد ليست للتأثير بشكل مباشر على الأداء، إلا أنها تفعل ذلك بشكل غير مباشر.ومن الجدير بالذكر أن أنواعًا معينة من التحسين، وخاصة بنيات Fortran Pure وElemental، يمكن أن تؤدي إلى تحسين الأداء كثيرًا.تتطلب هذه الإعدادات عادةً أن يتم الإعلان عن نواياها بشكل صريح لجميع الوسائط.

بشكل تقريبي، إذا كان المترجم يعرف مسبقًا نوايا جميع المتغيرات، فيمكنه تحسين الكود و"التحقق من غباءه" بسهولة وفعالية أكبر.

بشكل حاسم، إذا استخدم المرء بنيات نقية وما إلى ذلك، فعندئذ، مع احتمال كبير، سيكون هناك "نوع من السلامة/النظافة العالمية" أيضًا، نظرًا لأن Pure/Elemental s/p's يمكنه فقط استدعاء Pure/Elemental s/p's الأخرى وهكذا لا يمكن الوصول إلى موقف من النوع المشار إليه في مثال "The Glazer Guy's".

على سبيل المثال، إذا تم الإعلان عن Sub1() على أنها نقية، فيجب أيضًا الإعلان عن Sub2() على أنها نقية، وبعد ذلك سيُطلب منها الإعلان عن النوايا على جميع المستويات، وبالتالي يتم إنتاج "القمامة" في "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) | "

بالطبع، لا يلزم أن يكون sub2 خالصًا حتى أعلن أنه Intent(In)، والذي سيوفر مرة أخرى "السلامة/النظافة" التي يبحث عنها المرء.

لاحظ أنه حتى لو تم الإعلان عن Intent(InOut) فإنه سيظل يفشل مع Pure'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,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 وما إلى ذلك في جميع الحالات (على سبيل المثال.العديد من إعدادات اللغة المختلطة، أو عند الاعتماد على libs خارجية خارجة عن إرادتك، وما إلى ذلك).

ومع ذلك، فإن الاستخدام المستمر للنوايا، وكلما أمكن ذلك، الخالص، سينتج عنه الكثير من الفوائد، ويزيل الكثير من الحزن.

يمكن للمرء ببساطة أن يعتاد على إعلان النوايا في كل مكان وفي كل وقت عندما يكون ذلك ممكنًا، سواء كان ذلك نقيًا أم لا...هذه هي ممارسة الترميز الموصى بها.

...يؤدي هذا أيضًا إلى تسليط الضوء على سبب آخر لوجود BOTH Intent(InOut) و Intent(Out)، نظرًا لأنه يجب أن يتم الإعلان عن جميع نوايا Arg في Pure، فسيكون هناك بعض Args خارجة فقط، بينما يكون البعض الآخر InOut (أي.سيكون من الصعب الحصول على Pure's بدون نوايا الدخول والخروج والخروج).

2 ب) تشير تعليقات البروتوكول الاختياري التي تتوقع "تحسينات في الأداء "نظرًا لعدم الحاجة إلى النسخ" إلى سوء فهم لـ 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

في فورتران الحديثة، "المعادل" هو إعلان A كـ Real(DP) A(:) في s/r (على الرغم من وجود إعدادات مختلفة تستفيد من تمرير حدود المصفوفة والإعلان بوضوح عن الحدود، ولكن ذلك سيكون استطرادا طويلا ليوم آخر).

وهذا يعني أن Fortran لا يمر من حيث القيمة، ولا "يقوم بعمل نسخ" لـ Args/Dummy vars.إن A() في الاستدعاء s/r هو "نفس A" المستخدم في s/r (بالطبع، في s/r، يمكن للمرء عمل نسخة من A() أو أي شيء آخر، مما قد يؤدي إلى إنشاء المزيد متطلبات العمل/المساحة، ولكن هذه مسألة أخرى).

ولهذا السبب في المقام الأول، لا تؤثر النوايا بشكل مباشر على الأداء إلى حد كبير، حتى بالنسبة للصفيفات الكبيرة وما إلى ذلك.

ب) فيما يتعلق بالارتباك "التمرير حسب القيمة":على الرغم من أن الإجابات المختلفة أعلاه تؤكد أن استخدام Intent "لا يمر عبر القيمة"، فقد يكون من المفيد توضيح الأمر.

قد يكون من المفيد تغيير الصياغة إلى "يتم تمرير النية دائمًا حسب المرجع".هذا ليس مثل "عدم تجاوز القيمة"، وهو دقة مهمة.والجدير بالذكر أن النوايا ليست فقط "byRef"، بل يمكن أن تمنع النية المرور حسب القيمة.

على الرغم من وجود إعدادات خاصة/أكثر تعقيدًا (على سبيل المثال.(ملفات Fortran DLL ذات اللغات المختلطة وما إلى ذلك) التي تتطلب الكثير من المناقشة الإضافية، بالنسبة للجزء الأكبر من "Fortran القياسي"، يتم تمرير Args بواسطة المرجع.يمكن رؤية عرض توضيحي لـ "دقة النية" هذه في امتداد بسيط لمثال "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's أن Args هي "Value"، فهي "byVal" محليًا فقط (مرة أخرى في الإعدادات القياسية).لذا، فإن أي محاولة بواسطة s/r المُستدعى لإرجاع وسيط تم الإعلان عنه كقيمة بأي نوع من نية الخروج، ستفشل بسبب "تصادم" أنماط التمرير.

إذا كان يجب أن يكون "Out" أو "InOut" و"Value"، فلا يمكن للمرء استخدام Intent:وهو ما هو أكثر من مجرد قول "لا يتم تمريره بالقيمة".

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top