2009-06-18 32 views
13

Sau khi tìm kiếm một lúc trong sách, ở đây trên stackoverflow và trên web nói chung, tôi đã tìm thấy rằng rất khó để tìm thấy một lời giải thích đơn giản cho sự khác biệt thực sự giữa ý định đối số fortran. Cách tôi đã hiểu điều này, là:Sự khác biệt rõ ràng giữa các ý định fortran (trong, ngoài, inout) là gì?

  • intent(in) - Đối số thực tế được sao chép vào đối số giả ở mục nhập.
  • intent(out) - Đối số giả trỏ đến đối số thực tế (cả hai đều trỏ đến cùng một vị trí trong bộ nhớ).
  • intent(inout) - đối số giả được tạo cục bộ và sau đó được sao chép vào đối số thực tế khi quy trình kết thúc.

Nếu hiểu biết của tôi là chính xác, thì tôi cũng muốn biết tại sao người ta muốn sử dụng intent(out), vì intent(inout) yêu cầu ít công việc hơn (không sao chép dữ liệu).

+0

[Bạn có thể quan tâm đến bài đăng này về cách đối số được truyền đạt và tham gia.] (Https://software.intel.com/en-us/blogs/2009/03/31/doctor-fortran-in-ive -come-here-for-an-argument) –

Trả lời

19

Ý định chỉ là gợi ý cho trình biên dịch, và bạn có thể ném thông tin đó đi và vi phạm nó. Ý định tồn tại gần như hoàn toàn để đảm bảo rằng bạn chỉ làm những gì bạn dự định làm trong một chương trình con. Một trình biên dịch có thể chọn để tin tưởng bạn và tối ưu hóa một cái gì đó.

Điều này có nghĩa là intent(in) không được chuyển giá trị. Bạn vẫn có thể ghi đè lên giá trị ban đầu.

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

Curios: Tôi có đánh dấu sai câu trả lời đúng không? –

+0

@ KarlYngveLervåg, tôi không tin như vậy, vì câu trả lời này không thực sự trả lời câu hỏi ban đầu của bạn. Đó là thông tin hữu ích, nhưng dù sao, đó là cuộc gọi của bạn câu trả lời "đúng" (tức là hữu ích nhất) là gì. Tôi nghĩ câu trả lời khác gần hơn để trả lời câu hỏi của bạn. – patrickvacek

+0

Tôi đã đánh dấu câu hỏi này là câu trả lời chính xác, xem xét câu trả lời khác là sai ('intent (in)' KHÔNG phải là giá trị trả theo giá trị). –

5
  • intent(in) - có vẻ như truyền theo giá trị (và thay đổi này không được phản ánh trong mã bên ngoài) nhưng thực tế là do tham chiếu và thay đổi nó bị trình biên dịch cấm. Nhưng nó vẫn có thể được thay đổi.
  • intent(out) - chuyển bằng cách nào đó bằng tham chiếu, trên thực tế, đối số trả về
  • intent(inout) - chuyển theo tham chiếu, tham số vào/ra thông thường.

Sử dụng intent(out) nếu đồng bằng, để ghi lại thiết kế của bạn. Không quan tâm đến hiệu suất rất ít nếu có. (Các ý kiến ​​đề nghị không có số nào là intent(in) về mặt kỹ thuật cũng được chuyển qua tham chiếu.)

+0

Bạn có chắc chắn hiệu suất đạt được là không đáng kể? Điều gì sẽ xảy ra nếu một hàm được gọi thường xuyên và nhận các mảng rất lớn làm đối số? –

+0

Chắc chắn, nếu bạn CHỈ gọi phương pháp trong một microbenchmark, nó sẽ mất rất nhiều thời gian. Và có, nếu bạn vượt qua VERY mảng lớn, nó có thể được xem xét. Nhưng hầu hết thời gian hãy nhớ rằng, "Tối ưu hóa sớm là gốc rễ của mọi điều ác". Bạn vẫn có thể thay đổi nó sau này khi bạn yêu cầu nút cổ chai ở đó và nó nằm trong top 3 thứ chiếm thời gian của bạn. Tôi sẽ luôn sử dụng nguyên tắc thiết kế trước tiên. –

+0

Ok. Cảm ơn bạn đã xây dựng :) –

1

Không rõ liệu các phần của câu hỏi của OP có thực sự được trả lời hay không. Ngoài ra, chắc chắn có vẻ có nhiều nhầm lẫn và nhiều lỗi trong các câu trả lời/thảo luận tiếp theo có thể được hưởng lợi từ một số giải thích rõ ràng.

A) Câu hỏi của OP Re

"sau đó tôi cũng muốn biết lý do tại sao ai muốn sử dụng mục đích (trong), vì mục đích (inout) đòi hỏi ít công việc (không sao chép dữ liệu)."

có thể chưa trả lời hoặc ít nhất là trực tiếp/chính xác.

Đầu tiên, phải rõ ràng thuộc tính Intent có ít nhất HAI mục đích: "an toàn/vệ sinh" và "các vấn đề gián tiếp" (không phải là "các vấn đề hiệu suất trực tiếp").

1) An toàn/vệ sinh: Để hỗ trợ trong việc sản xuất mã "an toàn/hợp lý" với cơ hội giảm bớt "những điều lộn xộn". Vì vậy, một Intent (In) không thể được ghi đè (ít nhất là tại địa phương, hoặc thậm chí "toàn cầu" trong một số trường hợp, xem bên dưới).

Tương tự, Intent (Out) yêu cầu Arg được gán một "câu trả lời rõ ràng", do đó giúp giảm kết quả "rác". Ví dụ, trong giải pháp có lẽ là vấn đề phổ biến nhất trong toán học tính toán, tức là cái gọi là "Ax = b vấn đề", "kết quả trực tiếp/câu trả lời" mà người ta đang tìm kiếm là các giá trị cho vectơ x . Những người nên được Intent (Out) để đảm bảo x được chỉ định một câu trả lời "rõ ràng". Nếu x được khai báo là Intent (InOut) hoặc "no Intent" thì Fortran sẽ gán x một số "giá trị mặc định" (có thể là "zero" trong chế độ Debug, nhưng có thể là "rác" trong chế độ Release). bộ nhớ tại vị trí con trỏ Args) và nếu người dùng không gán giá trị chính xác cho x một cách rõ ràng, nó sẽ trả về "rác". Intent (Out) sẽ "nhắc nhở/buộc" người dùng gán các giá trị một cách rõ ràng cho x, và do đó loại bỏ loại rác "(ngẫu nhiên)" này.

Trong quá trình giải pháp, một (hầu như chắc chắn) sẽ tạo ra nghịch đảo của ma trận A. Người dùng có thể muốn trả về giá trị nghịch đảo đó cho lệnh gọi thay vì A, trong trường hợp A nên là Intent (InOut)). Ngoài ra, người dùng có thể muốn đảm bảo rằng không có thay đổi nào được thực hiện cho ma trận A hoặc vector b, trong trường hợp này chúng sẽ được khai báo Intent (In) và do đó đảm bảo rằng các giá trị quan trọng không bị ghi đè.

2 a) "Hiệu suất gián tiếp" (và "an toàn/vệ sinh toàn cầu"): Mặc dù Intent không trực tiếp ảnh hưởng đến hiệu suất, chúng làm gián tiếp. Đáng chú ý là một số loại tối ưu hóa nhất định và đặc biệt là cấu trúc Nguyên tử và Nguyên tố Fortran, có thể tạo ra hiệu suất được cải thiện nhiều. Các cài đặt này thường yêu cầu tất cả các Arg có Intent được khai báo một cách rõ ràng. Nói chung, nếu trình biên dịch biết trước ý định của tất cả các vars, thì nó có thể tối ưu hóa và "kiểm tra ngu ngốc" mã dễ dàng hơn và hiệu quả hơn. Điều quan trọng, nếu sử dụng cấu trúc Pure etc, thì với xác suất cao, sẽ có "loại an toàn/vệ sinh toàn cầu", vì Pure/Elemental s/p chỉ có thể gọi các Tinh khiết/Nguyên tố khác/p và vì vậy người ta KHÔNG THỂ đến một tình huống của loại được chỉ ra trong ví dụ "The Glazer Guy's".

Ví dụ, nếu Sub1() được khai báo là Pure, thì Sub2() cũng phải được khai báo là Pure, và sau đó nó sẽ được yêu cầu khai báo Intents ở mọi cấp, và do đó "garbage out" được tạo ra Ví dụ "The Glazer Guy" không thể xảy ra. Đó là, các mã sẽ là:

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 

... trên biên dịch, điều này sẽ tạo ra một cái gì đó giống như

"|| Lỗi: đối số Dummy 'i' với mục đích (IN) trong bối cảnh định nghĩa biến (bài tập) tại (1) | "

Tất nhiên, sub2 không cần thuần khiết để tôi khai báo là Intent (In), một lần nữa sẽ cung cấp" an toàn/vệ sinh "đang tìm kiếm.

Lưu ý rằng ngay cả khi tôi được tuyên bố Intent (InOut), nó vẫn sẽ thất bại với Pure. Đó là:

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 

...trên biên dịch, điều này sẽ tạo ra một cái gì đó như

"|| Lỗi: đối số giả 'i' với INTENT (IN) trong ngữ cảnh biến (đối số thực tế cho INTENT = OUT/INOUT) tại (1) |"

Do đó, sự phụ thuộc chặt chẽ hoặc rộng rãi vào các cấu trúc Pure/Elemental sẽ đảm bảo (chủ yếu) "an toàn/vệ sinh toàn cầu".

Sẽ không thể sử dụng Pure/Elemental etc trong mọi trường hợp (ví dụ: nhiều cài đặt ngôn ngữ hỗn hợp hoặc khi dựa vào lib bên ngoài ngoài tầm kiểm soát của bạn, v.v.).

Tuy nhiên, sử dụng nhất quán Mục đích, và bất cứ khi nào có thể Tinh khiết vv, sẽ tạo ra nhiều lợi ích và loại bỏ nhiều đau buồn.

Người ta có thể dễ dàng tham gia vào thói quen tuyên bố Intents ở mọi nơi mọi lúc mọi nơi, dù là Pure hay không ... đó là thực hành mã hóa được khuyến nghị.

... điều này cũng mang lại một lý do khác cho sự tồn tại của BOTH Intent (InOut) và Intent (Out), vì Pure phải có tất cả Intent's Intent's tuyên bố, sẽ có một số Args chỉ Out, trong khi những người khác là InOut (tức là sẽ khó có được Pure nếu không có In, InOut và Out Out).

2 b) Nhận xét của OP mong đợi "cải tiến hiệu suất" vì không yêu cầu sao chép "cho biết sự hiểu lầm của Fortran và việc sử dụng rộng rãi thông tin tham chiếu của nó bằng tham chiếu. thực tế, thường chỉ có con trỏ đến phần tử đầu tiên trong mảng (cộng với một số thông tin mảng ẩn nhỏ) là bắt buộc.

Thật vậy, một số thông tin chi tiết có thể được cung cấp bằng cách xem xét "ngày cũ" (ví dụ: Fortran IV, 77, v.v.), khi chuyển một mảng có thể đã được mã hóa như sau:

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 

Trong Fortran hiện đại, "e "" là khai báo A là Real (DP) A (:) trong s/r (mặc dù nói đúng là có nhiều cài đặt khác nhau có lợi từ việc vượt qua giới hạn của mảng và khai báo rõ ràng với các giới hạn, nhưng đó sẽ là một sự phân tích dài đối với ngày).

Tức là, Fortran không vượt qua giá trị, cũng không "tạo bản sao" cho các vlog Args/Dummy. Các A() trong gọi là s/r là "cùng một" như được sử dụng trong s/r (Tất nhiên, trong s/r, người ta có thể tạo một bản sao của A() hoặc bất cứ điều gì, mà sẽ tạo thêm yêu cầu công việc/không gian, nhưng đó là một vấn đề khác).

Đó là vì lý do này chủ yếu là của Ý định không trực tiếp ảnh hưởng đến hiệu suất đến một mức độ lớn, ngay cả đối với mảng lớn Arg của, vv

B) Về "vượt qua bởi giá trị gia tăng" sự nhầm lẫn: Mặc dù các phản ứng khác nhau trên xác nhận rằng việc sử dụng Intent là "không chuyển giá trị", có thể hữu ích khi làm rõ vấn đề.

Có thể giúp thay đổi từ ngữ thành "Mục đích luôn được chuyển bằng tham chiếu". Điều này không giống như "không vượt qua bởi giá trị", và nó là một sự tinh tế quan trọng. Đáng chú ý, không chỉ là Intents "byRef", Intent có thể PREVENT truyền theo giá trị.

Mặc dù có các cài đặt đặc biệt/phức tạp hơn nhiều (ví dụ: ngôn ngữ hỗn hợp của Fortran DLL vv), trong đó phần lớn "tiêu chuẩn Fortran", Arg được chuyển bởi Ref.Một cuộc biểu tình của "tinh tế Ý định" có thể được nhìn thấy trong một phần mở rộng đơn giản của "The Glazer Guys" Ví dụ, như:

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 

Đó là, bất cứ điều gì với một khía cạnh "Out" để nó phải là "ByRef" (ít nhất là trong cài đặt bình thường), vì s/r đang gọi là "byRef". Vì vậy, ngay cả khi tất cả các s/r tuyên bố Args là "Giá trị", họ là "byVal" chỉ tại địa phương (một lần nữa trong cài đặt tiêu chuẩn). Vì vậy, bất kỳ nỗ lực nào được gọi là s/r để trả về một Arg được khai báo là Value với bất kỳ loại Out Intent nào, sẽ FAIL do "va chạm" của các kiểu đi qua.

Nếu nó phải là "Out" hoặc "InOut" và "Value", thì người ta không thể sử dụng Intent: mà phần nào đơn giản hơn là nói "nó không được chuyển giá trị".

+0

OMG dài, không đọc hết, tôi chắc chắn khuyên bạn nên giữ mọi thứ trên SO. Nhưng tôi không nhận được quan điểm của bạn với tinh khiết và pha trộn ý định. IMHO trong ví dụ đó với 'i = 7' thuần túy không đóng vai trò nào cả. Và BTW 'real (8)' là một thói quen xấu xấu và sẽ không biên dịch trong một số trình biên dịch nào đó. –

Các vấn đề liên quan