Giáo trình Ngôn ngữ lập trình Fortran 90

pdf 223 trang phuongnguyen 6640
Bạn đang xem 20 trang mẫu của tài liệu "Giáo trình Ngôn ngữ lập trình Fortran 90", để tải tài liệu gốc về máy bạn click vào nút DOWNLOAD ở trên

Tài liệu đính kèm:

  • pdfgiao_trinh_ngon_ngu_lap_trinh_fortran_90.pdf

Nội dung text: Giáo trình Ngôn ngữ lập trình Fortran 90

  1. Đại học Quốc gia Hà Nội Tr−ờng Đại học Khoa học Tự nhiên −−−−−W X−−−−− Phan Văn Tân Ngôn ngữ lập trình Fortran 90 Hà Nội − 2005
  2. Mục lục Lời giới thiệu 7 Mở đầu 9 Ch−ơng 1. Những yếu tố cơ bản của ngôn ngữ FORTRAN 11 1.1 Chạy một ch−ơng trình FORTRAN 11 1.2 Cấu trúc chung của một ch−ơng trình FORTRAN 15 1.3 Cấu trúc câu lệnh 16 1.3.1 ý nghĩa của dấu cách (Blank) 16 1.3.2 Lời chú thích 17 1.3.3 Dòng nối tiếp 17 1.4 Kiểu dữ kiệu 17 1.4.1 Lớp các kiểu số (Integer, Real, Complex) 18 1.4.2 Kiểu ký tự (Character) và kiểu lôgic (Logical) 21 1.4.3 Phép toán trên các kiểu dữ liệu 23 1.5 Hằng 25 1.5.1 Hằng nguyên 25 1.5.2 Hằng thực 26 1.5.3 Hằng ký tự 26 1.6 Tên biến và tên hằng 27 1.7 Qui tắc kiểu ẩn 28 1.8 Phong cách lập trình 30 1.9 Biểu thức số 31 1.9.1 Phép chia với số nguyên 31 1.9.2 Biểu thức hỗn hợp 31 1.10 Lệnh gán. Gán hằng, gán biểu thức 32 1.11 Lệnh vào ra đơn giản 33 1.11.1 Lệnh vào dữ liệu 33 1.11.2 Đọc dữ liệu từ file TEXT 35 1.11.3 Lệnh kết xuất dữ liệu 36 1.11.4 Kết xuất ra máy in 37 1.12 Sử dụng hàm trong fortran 37 Bài tập ch−ơng 1 40 Ch−ơng 2. Các câu lệnh cơ bản của Fortran 44 2.1 Lệnh chu trình (DO Loops) 44 2.2 Lệnh rẽ nhánh với IF 48 2.2.1 Dạng 1 48 2.2.2 Dạng 2 48 2.2.3 Dạng 3 49 2.2.4 Dạng 4 50 2.2.5 Lệnh nhảy vô điều kiện GOTO 52 2.2.6 Lệnh IF số học 54 2.3 Kết hợp DO và IF 55 2.4 Rẽ nhánh với cấu trúc SELECT CASE 56 2.5 Thao tác với hằng và biến ký tự (CHARACTER) 58 Bài tập ch−ơng 2 61 Ch−ơng 3. Các cấu trúc mở rộng 63 3.1 Chu trình DO tổng quát và chu trình DO lồng nhau 63
  3. 3.2 Cấu trúc IF tổng quát và cấu trúc IF lồng nhau 64 3.3 Chu trình ngầm 67 3.4 Định dạng dữ liệu bằng lệnh FORMAT 67 3.5 Chu trình lặp không xác định 69 3.5.1 Cấu trúc kết hợp IF và GOTO 69 3.5.2 Cấu trúc DO và EXIT 70 3.5.3 Cấu trúc DO WHILE END DO 72 3.5.4 Lệnh CYCLE 73 3.5.5 Một số ví dụ về chu trình lặp không xác định 75 Bài tập ch−ơng 3 78 Ch−ơng 4. Ch−ơng trình con (SUBROUTINE và FUNCTION) và modul 82 4.1 Khái niệm 82 4.2 Th− viện các hàm trong 82 4.3 Các ch−ơng trình con trong 83 4.3.1 Hàm trong (Internal FUNCTION) 83 4.3.2 Thủ tục trong (Internal SUBROUTINE) 84 4.4 Câu lệnh CONTAINS 85 4.5 Một số ví dụ về ch−ơng trình con trong 86 4.6 Biến toàn cục và biến địa ph−ơng 89 4.7 Định nghĩa hàm bằng câu lệnh đơn 91 4.8 Ch−ơng trình con ngoài 92 4.8.1 Câu lệnh EXTERNAL 93 4.8.2 Khai báo khối giao diện (INTERFACE BLOCK) 94 4.9 Các thuộc tính của đối số 95 4.9.1 Thuộc tính INTENT 95 4.9.2 Thuộc tính OPTIONAL 96 4.9.3 Thuộc tính SAVE 98 4.10 Modul 98 4.11 Phép đệ qui 99 Bài tập ch−ơng 4 101 Ch−ơng 5. Mảng 103 5.1 Khái niệm về mảng trong FORTRAN 103 5.2 Khai báo mảng 103 5.3 L−u trữ mảng trong bộ nhớ và truy cập đến các phần tử mảng 106 5.3.1 Sử dụng lệnh DATA để khởi tạo mảng 108 5.3.2 Biểu thức mảng 109 5.3.3 Cấu trúc WHERE ELSEWHERE END WHERE 109 5.4 Mảng động (Dynamical Array) 110 5.5 Kiểu con trỏ 113 5.5.1 Trạng thái con trỏ 114 5.5.2 Cấp phát và giải phóng biến con trỏ 114 5.6 Hàm trả về nhiều giá trị 115 Bài tập ch−ơng 5 117 Ch−ơng 6. Biến ký tự 121 6.1 Khai báo biến ký tự 121 6.2 Các xâu con (substring) 121 6.3 Xử lý biến ký tự 122 6.4 Phép toán gộp xâu ký tự 127 6.5 Tạo định dạng FORMAT bằng xâu ký tự 127 4
  4. 6.6 Mảng xâu ký tự 128 Bài tập ch−ơng 6 130 Ch−ơng 7. Kiểu file 132 7.1 Khái niệm 132 7.2 Phân loại file 134 7.2.1 File có định dạng (Formatted Files) 134 7.2.2 File không định dạng (Unformatted Files) 134 7.2.3 File dạng nhị phân (Binary Files) 135 7.2.4 File truy cập tuần tự (Sequential-Access Files) 135 7.2.5 File truy cập trực tiếp (Direct-Access Files) 136 7.3 Tổ chức dữ liệu trong file 136 7.3.1 File truy cập tuần tự có định dạng 136 7.3.2 File truy cập trực tiếp có định dạng 137 7.3.3 File truy cập tuần tự không định dạng 138 7.3.4 File truy cập trực tiếp không định dạng 139 7.3.5 File truy cập tuần tự dạng nhị phân 140 7.3.6 File truy cập trực tiếp dạng nhị phân 141 7.4 Lệnh mở (OPEN) và đóng (CLOSE) file 141 7.4.1 Lệnh mở file 141 7.4.2 Lệnh đóng file 145 7.5 Các lệnh vào ra dữ liệu với file 145 7.5.1 Lệnh đọc dữ liệu từ file (READ) 145 7.5.2 Lệnh ghi dữ liệu ra file (WRITE) 147 7.5.3 Vào ra dữ liệu với NAMELIST 148 7.5.4 Một số ví dụ thao tác với file 151 Bài tập ch−ơng 7 155 Ch−ơng 8. Một số kiến thức mở rộng 157 8.1 Khai báo dùng chung bộ nhớ 157 8.1.1 Lệnh COMMON 157 8.1.2 Lệnh EQUIVALENT 158 8.2 Ch−ơng trình con BLOCK DATA 159 8.3 Câu lệnh INCLUDE 159 8.4 Lệnh INQUIRE 160 8.5 Điều khiển con trỏ file 162 8.5.1 Lệnh REWIND 162 8.5.2 Lệnh BACKSPACE 162 8.5.3 Lệnh ENDFILE 162 8.6 Cấu trúc dữ liệu do ng−ời dùng định nghĩa 163 Bài tập ch−ơng 8 168 Ch−ơng 9. Một số bài toán thông dụng 169 9.1 các bài toán thống kê cơ bản 169 9.1.1 Tính trung bình số học của một chuỗi số liệu 169 9.1.2 Tính độ lệch chuẩn của một chuỗi số liệu 170 9.1.3 Sắp xếp chuỗi theo thứ tự tăng dần và xác định giá trị lớn nhất, nhỏ nhất của chuỗi 170 9.1.4 Xác định các phân vị của chuỗi 171 9.1.5 Tính các mômen phân bố 173 9.1.6 Tính một số đặc tr−ng thống kê khác 175 9.1.7 Tính mômen t−ơng quan và hệ số t−ơng quan 176 9.2 Một số bài toán về ma trận 181 9.2.1. Tích hai ma trận 181 5
  5. 9.2.2. Định thức của ma trận 182 9.2.3. Phần phụ đại số 185 9.2.4. Ma trận nghịch đảo 186 9.2.5. Giải hệ ph−ơng trình đại số tuyến tính 188 9.3 T−ơng quan và hồi qui tuyến tính 191 9.3.1. Xây dựng ph−ơng trình hồi qui tuyến tính 191 9.3.2. Tính hệ số t−ơng quan riêng 194 9.3.3. Tính hệ số t−ơng quan bội 196 9.4 Ph−ơng pháp số 196 9.4.1. Tìm nghiệm ph−ơng trình 196 9.4.2. Tính tích phân xác định 198 9.4.3. Sai phân hữu hạn và đạo hàm 200 9.4.4. Toán tử Laplaxian 203 9.4.5. Giải ph−ơng trình truyền nhiệt 205 9.4.6. Xây dựng cơ sở dữ liệu 210 Bài tập ch−ơng 9 216 Tài liệu tham khảo 218 Phụ lục 219 1. Trình tự các câu lệnh trong một đơn vị ch−ơng trình Fortran 219 2. Tóm tắt các câu lệnh của Fortran 219 3. Một số hàm và thủ thục của Fortran 221 6
  6. Lời giới thiệu Trong những năm gần đây, cùng với sự phát triển mạnh mẽ của Công nghệ Thông tin và Điện tử Viễn thông, nhiều ch−ơng trình, phần mềm máy tính đã ra đời và đ−ợc ứng dụng rộng rãi, góp phần thúc đẩy sự phát triển kinh tế, xã hội. Trong số đó, các ngôn ngữ lập trình cũng ngày càng đ−ợc phát triển và phổ biến. Ngôn ngữ lập trình Fortran cũng không phải là một ngoại lệ. Từ những phiên bản đầu tiên với nhiều hạn chế cho đến nay Fortran luôn là một trong những ngôn ngữ thông dụng rất đ−ợc −a chuộng trong lập trình giải các bài toán khoa học kỹ thuật. Với nhiều thế mạnh v−ợt trội so với các ngôn ngữ lập trình khác, Fortran th−ờng đ−ợc ứng dụng để giải các bài toán lớn, đòi hỏi phải xử lý tính toán nhiều, nhất là tính toán song song. Tr−ớc những năm chín m−ơi của thế kỷ hai m−ơi, khi mà thế hệ máy PC hãy còn mới lạ ở Việt Nam, hầu nh− các bài toán ứng dụng đều đ−ợc chạy trên các máy tính lớn (MINSK−32, EC−1022, EC−1035, IBM−360, ) với các ch−ơng trình th−ờng đ−ợc lập bằng ngôn ngữ Fortran. Song, khi các máy PC ngày càng phổ biến hơn, với nhiều phần mềm tiện dụng đi kèm, thêm vào đó là sự đòi hỏi về cấu hình máy tính của Fortran, ngôn ngữ Fortran hầu nh− đã bị lãng quên trong một thời gian khá dài. Nhiều ng−ời đã phải thay đổi thói quen sử dụng Fortran, tự thích ứng bằng cách chuyển sang tiếp cận với các ngôn ngữ lập trình khác hoặc chuyển h−ớng nghiên cứu. Sự thiếu thông tin cập nhật đã làm nhiều ng−ời t−ởng rằng Fortran là một ngôn ngữ “cổ” rồi, không ai dùng nữa. Nh−ng không phải nh− vậy. Tr−ớc sự đòi hỏi phải giải quyết những bài toán lớn (chúng tôi muốn nhấn mạnh lớp các bài toán khoa học kỹ thuật), chạy ở chế độ thời gian thực (Real−time), Fortran đã ngày càng đ−ợc phát triển và hoàn thiện với nhiều đặc điểm mới. Điều đó đã cuốn hút nhiều ng−ời quay về với Fortran. Một lý do khác có tác động không nhỏ, khiến ng−ời ta tiếp tục lựa chọn ngôn ngữ lập trình Fortran là quá trình quan hệ hợp tác quốc tế. Khi làm việc với các đối tác n−ớc ngoài, trong nhiều lĩnh vực hầu hết các ch−ơng trình đ−ợc viết bằng ngôn ngữ Fortran, nếu không biết về nó, đồng nghĩa với việc đôi bên không cùng “tiếng nói”; và do đó có thể dẫn đến sự bất lợi, kém hiệu quả khi làm việc với nhau. Nhận thức đ−ợc tầm quan trọng của vấn đề này, những năm gần đây, ngôn ngữ lập trình Fortran đã đ−ợc đ−a vào ch−ơng trình đào tạo của một số khoa trong tr−ờng Đại học Khoa học Tự nhiên, Đại học Quốc gia Hà Nội. Mặt khác, đối với nhiều nhà khoa học, hiện nay ngôn ngữ Fortran đã trở thành một trong những công cụ làm việc không thể thiếu, và tất nhiên trong số đó có chúng tôi. Bởi vậy, quyển sách này ra đời với kỳ vọng của chúng tôi là cung cấp cho bạn đọc những kiến thức cơ bản nhất về ngôn ngữ lập trình Fortran 90. Qua đó bạn đọc có thể ứng dụng nó một cách hiệu quả trong các lĩnh vực chuyên môn của mình. Quyển sách có thể đ−ợc dùng làm giáo trình giảng dạy ở bậc đại học và sau đại học cho ngành Khí t−ợng Thủy văn và Hải d−ơng học, tr−ờng Đại học Khoa học Tự nhiên, Đại học Quốc gia Hà 7
  7. Nội. Tuy nhiên chúng tôi cũng mong muốn nó sẽ giúp cho sinh viên các bậc đào tạo thuộc các ngành khoa học khác, nh− Vật lý học, Hóa học, Toán học trong tr−ờng Đại học Khoa học Tự nhiên có thêm một tài liệu tham khảo bổ ích trong quá trình học tập tại tr−ờng. Quyển sách cũng có thể làm tài liệu tham khảo cho các cán bộ, kỹ s−, các nhà nghiên cứu thuộc nhiều lĩnh vực khác nhau. Trong quá trình biên soạn quyển sách, một số đồng nghiệp đã đề xuất chúng tôi đ−a thêm vào phần đồ họa của Fortran. Một số khác lại đề nghị gắn phần giao diện giữa những kết quả tính toán kết xuất với một số phần mềm đồ họa khác, nh− GrADS, NCAR Graphics, Chúng tôi xin chân thành cám ơn và ghi nhận những ý kiến đóng góp quí báu đó. Nhận thấy rằng phần đồ họa của Fortran chỉ đ−ợc tích hợp trong một số phiên bản chạy trên môi tr−ờng Microsoft Windows; còn để gắn kết các file kết xuất của Fortran với các phần mềm đồ họa khác ít nhất cần phải có một số kiến thức cơ bản về các phần mềm này. Vì khuôn khổ quyển sách có hạn, chúng tôi sẽ cố gắng trình bày những nội dung trên trong một ấn phẩm khác trong t−ơng lai. Mặc dù đã cố gắng chuyển tải nội dung quyển sách sao cho có thể đáp ứng đ−ợc nhiều đối t−ợng, từ những ng−ời mới làm quen cho đến những ng−ời đã từng có quá trình làm việc nhất định với ngôn ngữ Fortran, với bố cục từ dễ đến khó, từ đơn giản đến phức tạp, song do còn nhiều hạn chế về kinh nghiệm và kiến thức, quyển sách cũng không tránh khỏi những khiếm khuyết. Chúng tôi rất mong nhận đ−ợc sự đóng góp ý kiến của tất cả các bạn đọc. Để hoàn thành quyển sách này, chúng tôi nhận đ−ợc sự hỗ trợ cả về tinh thần và vật chất từ phía tr−ờng Đại học Khoa học Tự nhiên, Đại học Quốc gia Hà Nội, đặc biệt từ các đồng nghiệp thuộc Khoa Khí t−ợng Thủy văn và Hải d−ơng học của tr−ờng, nơi chúng tôi gắn bó trong công tác giảng dạy và hoạt động khoa học hàng chục năm nay. Nhân đây chúng tôi xin bày tỏ lòng biết ơn chân thành và lời cám ơn sâu sắc. Hà Nội, 2−2005 Tác giả 8
  8. Mở đầu Tập hợp các qui tắc đặc biệt để mã hoá những kiến thức cho máy tính hiểu đ−ợc gọi là ngôn ngữ lập trình. Có rất nhiều ngôn ngữ nh− vậy, ví dụ FORTRAN, BASIC, Pascal, C, FORTRAN là tên cấu tạo từ FORmula TRANslation (diễn dịch công thức, hay còn gọi là công thức dịch), là một trong những ngôn ngữ lập trình bậc cao đầu tiên. Nó có thể sử dụng những tên t−ợng tr−ng để biểu diễn định l−ợng toán học và viết các công thức toán học d−ới dạng thức hợp lý có thể hiểu đ−ợc, nh− X = (−B+DELTA)/(2*A). ý t−ởng của FORTRAN đ−ợc John Backus đề xuất vào khoảng cuối năm 1953 ở New York, và ch−ơng trình FORTRAN đầu tiên đã đ−ợc chạy vào tháng 4 năm 1957. Kể từ đó, việc sử dụng FORTRAN đã nhanh chóng đ−ợc phổ biến rộng rãi. Điều đó đòi hỏi cần phải sớm tiêu chuẩn hoá nó sao cho ch−ơng trình viết ra phải bảo đảm chạy đ−ợc ở mọi nơi. Vào năm 1966, lần đầu tiên phiên bản chuẩn của ngôn ngữ lập trình này đ−ợc ấn hành. Phiên bản này, nh− đã biết, là Fortran 66 (chính xác hơn là FORTRAN 66, nh−ng thực tế ng−ời ta cho cách viết hoa là không trang trọng). Phiên bản chuẩn mới sau đó, Fortran 77, đ−ợc ấn hành vào năm 1978. Không bằng lòng với sự cạnh tranh của các ngôn ngữ mới khác, nh− Pascal và C, FORTRAN tiếp tục phát triển một cách mạnh mẽ. Và phiên bản chuẩn gần đây, FORTRAN 90 (hoặc Fortran 90), với nhiều đặc tính đột phá, đã ra đời vào tháng 8 năm 1991. Cho đến nay, FORTRAN đã phát triển đến những phiên bản mới hơn, nh− FORTRAN 95, FORTRAN 2003. Trong khuôn khổ quyển sách này chúng tôi chỉ hạn chế trình bày những kiến thức cơ bản của FORTRAN 90. Những phần bổ sung của các phiên bản sau so với FORTRAN 90 không nhiều và cũng ch−a quá cần thiết phải đ−a vào đây. Trong một số tình huống cụ thể, để giúp ng−ời đọc đã từng làm quen với FORTRAN 77 hoặc cần có thêm kiến thức để đọc những ch−ơng trình của ng−ời khác viết bằng FORTRAN 77, chúng tôi sẽ có thêm những ghi chú “mở rộng” thích hợp. Những ng−ời thành thạo Fortran muốn quan tâm đến lịch sử phát triển của ngôn ngữ lập trình này có thể tham khảo thêm cuốn Fortran 90 Explained, Oxford University Press (Oxford, 1990) của Michael Metcalf và John ReidMetcalf và Reid. Nh− đã nói ở trên, chính xác hơn nên viết ngôn ngữ FORTRAN, nh−ng do “sở thích tuỳ tiện”, ở đây chúng tôi cũng sẽ viết Fortran thay cho cách viết FORTRAN. Quyển sách đ−ợc bố cục trong 9 ch−ơng. Ch−ơng 1: Những yếu tố cơ bản của ngôn ngữ Fortran. Trong ch−ơng này trình bày cách chạy một ch−ơng trình Fortran, cấu trúc chung của một ch−ơng trình, cấu trúc câu lệnh, các kiểu dữ kiệu, biểu thức số, câu lệnh gán, các lệnh vào ra đơn giản và cách sử dụng hàm trong Fortran. Ch−ơng 2: Các câu lệnh cơ bản của Fortran. ở đây trình bày các câu lệnh chu trình (DO Loops), lệnh rẽ nhánh với IF và SELECT CASE, cách sử dụng kết hợp DO và IF và một số thao tác với hằng và biến ký tự (CHARACTER). Ch−ơng 3: Các cấu trúc mở rộng. Ch−ơng này trình bày những kiến thức liên quan đến chu trình DO tổng quát và chu trình DO lồng nhau, cấu trúc IF tổng quát và cấu trúc IF lồng nhau, chu trình ngầm, định dạng dữ liệu bằng 9
  9. lệnh FORMAT và chu trình lặp không xác định. Ch−ơng 4: Ch−ơng trình con và modul. Ch−ơng này đề cập đến những khái niệm về th− viện các hàm chuẩn của Fortran, các ch−ơng trình con trong, ch−ơng trình con ngoài và modul, và một số kiến thức khác. Ch−ơng 5 trình bày những kiến thức về mảng trong Fortran, nh− cách khai báo mảng, l−u trữ mảng trong bộ nhớ và truy cập đến các phần tử mảng. Ch−ơng 6 trình bày về biến ký tự và xử lý biến ký tự. Ch−ơng 7 cung cấp những kiến thức về file, nh− phân loại file, tổ chức dữ liệu trong file, các lệnh vào ra dữ liệu với file. Ch−ơng 8: Một số kiến thức mở rộng. ở đây trình bày cách khai báo dùng chung bộ nhớ và ứng dụng, ch−ơng trình con BLOCK DATA, cấu trúc dữ liệu do ng−ời dùng định nghĩa và một số câu lệnh th−ờng gặp khác. Ch−ơng 9 dẫn ra một số bài toán thông dụng, nh− lớp các bài toán thống kê, các bài toán về ma trận, t−ơng quan và hồi qui tuyến tính, ph−ơng pháp số. Cuối mỗi ch−ơng là hệ thống các bài tập tự giải, nhằm củng cố những kiến thức có liên quan. Phần cuối của quyển sách là một số phụ lục, giúp bạn đọc có thể tra cứu nhanh ý nghĩa hệ thống các câu lệnh cũng nh− các hàm và thủ tục của Fortran trong quá trình lập trình. 10
  10. Ch−ơng 1. Những yếu tố cơ bản của ngôn ngữ FORTRAN 1.1 Chạy một ch−ơng trình FORTRAN Cũng nh− khi bắt đầu học một ngôn ngữ lập trình nào khác, nếu là ng−ời mới làm quen với Fortran, ta nên chạy các ch−ơng trình ví dụ trong phần này càng sớm càng tốt, không cần cố gắng hiểu một cách chi tiết chúng làm việc nh− thế nào. Việc giải thích chúng sẽ đ−ợc giới thiệu dần dần ở các phần sau. Để chạy đ−ợc các ch−ơng trình này tr−ớc hết ta cần phải có một bộ phần mềm biên dịch và đã đ−ợc cài đặt trên hệ thống máy tính. Ngoài ra, ta cũng cần phải làm quen với bộ phần mềm này, phải biết cách soạn thảo các ch−ơng trình Fortran và biên dịch rồi chạy nó nh− thế nào. Việc làm quen này không mất nhiều thời gian và cũng khá đơn giản, nên không đ−ợc trình bày ở đây. Hơn nữa, vì Fortran có thể làm việc trên nhiều hệ điều hành khác nhau, nh− các dòng UNIX, LINUX, WINDOWS, DOS, và nó cũng có nhiều phiên bản khác nhau đối với từng hệ điều hành, nên sẽ không đầy đủ nếu chỉ trình bày ở đây một hoặc một vài tr−ờng hợp. Ch−ơng trình sau đây sẽ đ−a ra lời chào mừng, nếu ta đ−a tên của mình vào khi đ−ợc hỏi: Ví dụ 1.1 Ch−ơng trình làm quen ! Vi du mo dau ! Loi Chao mung! CHARACTER NAME*20 PRINT*, 'Ten ban la gi?' READ*, NAME PRINT*, 'Xin chao ban ', NAME END Kết quả nhận đ−ợc trên màn hình khi chạy ch−ơng trình này nh− sau (câu trả lời là dòng chữ in nghiêng): Ten ban la gi? Nam Xin chao ban Nam Tuy nhiên, với ch−ơng trình trên, nếu ta gõ tên mình đầy đủ cả Họ và tên, và giữa các từ có dấu cách thì kết quả có thể hơi bất ngờ đấy. Nh−ng không sao, chúng ta sẽ tìm hiểu vấn đề này sau. L−u ý rằng, trong đoạn ch−ơng trình trên các từ tiếng Việt đ−ợc viết d−ới dạng không dấu, vì không phải khi nào ta cũng có thể gõ tiếng Việt có dấu, và không phải khi nào kết quả hiển thị trên màn hình máy tính cũng bằng tiếng Việt có dấu. Bởi vậy, trong đa số tr−ờng hợp, những câu, từ tiếng Việt xuất hiện trong các ch−ơng trình ví dụ sẽ đ−ợc dùng tiếng Việt không dấu. Có thể điều này sẽ gây khó chịu khi so sánh Fortran với một 11
  11. số ngôn ngữ khác. Nh−ng ta sẽ cảm thấy tự hài lòng với khiếm khuyết nhỏ này so với khả năng tuyệt vời của Fortran. 3 Ch−ơng trình sau đây cho phép tính giá trị của hàm A(t) = 174.6( t −1981.2 ) khi nhập vào giá trị của biến t Ví dụ 1.2: Tính giá trị của hàm ! PROGRAM TinhHam ! Tinh gia tri ham A(t)=174.6*(t−1981.2) 3 INTEGER T ! Biến nguyên l−u giá trị biến t REAL A ! Biến thực l−u giá trị hàm A(t) PRINT*,’Cho gia tri cua bien t:’ READ*, T A = 174.6 * (T - 1981.2) 3 PRINT*,'Gia tri ham A(t) khi t= ', T, ' la : ', A END PROGRAM TinhHam Khi chạy ch−ơng trình này, trên màn hình sẽ xuất hiện dòng chữ (phía d−ới dòng này là con trỏ màn hình () nhấp nháy): Cho gia tri cua bien t: Nếu đ−a vào giá trị 2000 (cho biến t) ta sẽ nhận đ−ợc kết quả: Gia tri ham A(t) khi t = 2000 la : 1.1601688E+06 Giá trị kết quả của hàm đ−ợc in ra d−ới dạng ký hiệu khoa học E+06, có nghĩa là số tr−ớc đó nhân với 10 luỹ thừa 6, tức là trị số của A(t) vào khoảng 1,16 triệu. Bây giờ ta hãy chạy ch−ơng trình này nhiều lần, mỗi lần thay đổi giá trị của biến t và thử tìm xem khi nào thì giá trị của hàm A(t) sẽ đạt khoảng 10 triệu. Sau đó, hãy thử gõ nhầm giá trị của t (ví dụ gõ vào 2,000 thay vì gõ 2000) để xem Fortran phản ứng lại nh− thế nào. Một ví dụ khác, giả sử ta có 1000 đôla gửi tiết kiệm trong ngân hàng với lãi suất 9% mỗi năm. Vậy, sau một năm số tiền sẽ có trong ngân hàng bằng bao nhiêu? Để lập ch−ơng trình cho máy tính giải bài toán này tr−ớc hết cần phải làm rõ vấn đề về mặt nguyên tắc. Nhận thấy rằng, số tiền sẽ có sau một năm sẽ là tổng của số tiền gốc đã gửi và số tiền lãi sẽ có. Nh− vậy, lôgic các b−ớc thực hiện bài toán sẽ là: 1) Nhập số liệu vào máy (số tiền gốc và lãi suất) 2) Tính tiền lãi (tức 9% của 1000, bằng 90) 3) Cộng tiền lãi vào số tiền gốc (90 + 1000, tức 1090) 4) In (hiển thị) số tiền sẽ có sau một năm. Với lôgic đó, ta có thể viết ch−ơng trình nh− sau: Ví dụ 1.3: Tính tiền gửi tiết kiệm ! Chuong trinh nay khong nhap du lieu tu ban phim 12
  12. PROGRAM TinhTien ! Tinh tien gui tiet kiem REAL SoTien, TienLai, LaiSuat SoTien = 1000.0 ! Số tiền gốc ban đầu LaiSuat = 0.09 ! Lãi suất TienLai = LaiSuat * SoTien SoTien = SoTien + TienLai PRINT*, 'So tien se co sau mot nam:', SoTien END PROGRAM TinhTien Ta gõ ch−ơng trình này vào máy rồi chạy tính, và chú ý rằng ở đây máy không đòi hỏi phải nhập đầu vào (input) từ bàn phím nh− ví dụ tr−ớc đây (Tại sao?). Kết quả nhận đ−ợc trên màn hình sẽ là: So tien se co sau mot nam: 1.0900000E+03 Sẽ rất có ích nếu ta cố gắng thực hiện lặp lại nhiều lần các ví dụ trên đây, mỗi lần nh− vậy thử sửa đổi một ít trong ch−ơng trình và theo dõi xem kết quả thay đổi nh− thế nào. Điều đó sẽ sẽ giúp cho ta tự tin hơn khi tiếp cận với những nội dung sau này của Fortran. Bây giờ ta tìm hiểu xem trong quá trình thực hiện, các ch−ơng trình Fortran sẽ làm những gì. Nói chung, sau khi gõ lời ch−ơng trình (source code) và tiến hành chạy (run) nó trong môi tr−ờng của hệ điều hành máy tính thích hợp (đã cài đặt phần mềm Fortran), sẽ có hai quá trình tách biệt xảy ra. Đầu tiên, ch−ơng trình đ−ợc biên dịch (compile), tức là mỗi câu lệnh đ−ợc dịch (translated) sang mã máy (machine code) sao cho máy tính có thể hiểu đ−ợc. Quá trình này xảy ra nh− sau. Tr−ớc hết các câu lệnh của ch−ơng trình sẽ đ−ợc kiểm tra về cú pháp (Syntax). Nếu không có lỗi, chúng sẽ đ−ợc dịch sang mã máy và l−u trữ vào một file gọi là đối t−ợng (Object) hay đích. Sau đó chúng sẽ đ−ợc liên kết (Link) với hệ thống th− viện chuẩn của Fortran để tạo thành file có thể thực hiện (executable) đ−ợc. Nếu ch−ơng trình còn lỗi, các lỗi sẽ đ−ợc chỉ ra và quá trình biên dịch kết thúc mà không tạo đ−ợc file đích, và do đó không xảy ra quá trình thứ hai. Nếu quá trình thứ nhất thực hiện thành công thì chuyển sang quá trình thứ hai, trong đó ch−ơng trình đã dịch (tức file có thể thực hiện đ−ợc) sẽ đ−ợc thực hiện (executed). ở b−ớc này mỗi một chỉ thị đã dịch của ch−ơng trình sẽ lần l−ợt đ−ợc thực hiện theo qui tắc đã lập. Bộ ch−ơng trình thực hiện trọn vẹn quá trình thứ nhất (tức là cho đến khi tạo đ−ợc file có thể thực hiện − executable) th−ờng gọi là trình biên dịch (compiler). Trong khi biên dịch, không gian bộ nhớ RAM của máy tính định vị cho mọi dữ liệu sẽ đ−ợc phát sinh bởi ch−ơng trình. Phần bộ nhớ này có thể hiểu nh− là những “vùng” bộ nhớ khu trú mà mỗi một trong chúng, tại một thời điểm, chỉ có thể xác định một giá trị dữ liệu. Các bộ nhớ khu trú này đ−ợc tham chiếu đến bởi các tên ký hiệu (định danh) trong ch−ơng trình. Bởi vậy, câu lệnh: SoTien = 1000.0 13
  13. là cấp phát số 1000.0 đến vị trí bộ nhớ có tên SoTien. Vì nội dung của SoTien có thể thay đổi trong khi ch−ơng trình chạy nên nó đ−ợc gọi là biến (variable). Về hình thức, ch−ơng trình tính tiền gửi tiết kiệm (ví dụ 1.3) trên đây đ−ợc biên dịch nh− sau: 1) Đ−a số 1000 vào vị trí bộ nhớ SoTien 2) Đ−a số 0.09 vào vị trí bộ nhớ LaiSuat 3) Nhân nội dung của LaiSuat với nội dung của SoTien và đ−a kết quả vào vị trí bộ nhớ TienLai 4) Cộng nội dung của SoTien với nội dung của TienLai và đ−a kết quả vào SoTien 5) In (hiển thị) thông báo nội dung của SoTien 6) Kết thúc. Khi chạy ch−ơng trình, các câu lệnh dịch này đ−ợc thực hiện theo thứ tự từ trên xuống d−ới. Quá trình thực hiện, các vị trí bộ nhớ đ−ợc sử dụng sẽ có những giá trị sau: SoTien : 1000 LaiSuat: 0.09 TienLai: 90 SoTien : 1090 Chú ý rằng nội dung ban đầu của SoTien đã bị thay thế bởi giá trị mới. Câu lệnh PROGRAM ở dòng thứ hai trong ví dụ 1.3 mở đầu cho ch−ơng trình. Nó là câu lệnh tuỳ chọn, và có thể kèm theo tên tuỳ ý. Dòng thứ nhất và dòng thứ ba, bắt đầu với dấu chấm than, là lời giải thích, có lợi cho ng−ời đọc ch−ơng trình, và không ảnh h−ởng gì tới ch−ơng trình dịch. Các biến trong ch−ơng trình có thể có các kiểu (type) khác nhau; câu lệnh REAL trong ví dụ này là khai báo kiểu. Các dòng trống (nếu có) trong ch−ơng trình đ−ợc xem nh− những câu lệnh không thực hiện (non-executable), tức là không có tác động nào đ−ợc thực hiện, có thể chèn thêm vào để cho ch−ơng trình đ−ợc sáng sủa, không rối mắt. Bây giờ ta hãy thử làm lại ví dụ này nh− sau. 1) Chạy ch−ơng trình và ghi nhớ lại kết quả 2) Thay đổi câu lệnh thực hiện SoTien = 1000.0 bởi câu lệnh SoTien = 2000.0 và chạy lại ch−ơng trình. Rõ ràng có thể hiểu đ−ợc tại sao kết quả mới lại khác với kết quả tr−ớc đó. 3) Tiếp đến, loại bỏ dòng lệnh SoTien = SoTien + TienLai và chạy lại ch−ơng trình. Kết quả nhận đ−ợc là số tiền không thay đổi! Nh− vậy, do loại bỏ dòng lệnh 14
  14. SoTien = SoTien + TienLai nên số tiền lãi sẽ không đ−ợc cộng vào, tức nội dung bộ nhớ của biến SoTien không đ−ợc cập nhật. Tóm lại, để giải một bài toán bằng lập trình với ngôn ngữ Fortran ta cần thực hiện theo trình tự các b−ớc sau: 1) Phân tích bài toán, xác định thuật giải, các b−ớc thực hiện và trình tự thực hiện các b−ớc. Đây là b−ớc hết sức quan trọng, vì nó quyết định sự đúng đắn về mặt lôgic của việc giải bài toán. Do đó, nói chung ta nên lập một dàn bài cụ thể và biểu diễn nó qua các sơ đồ (th−ờng gọi là sơ đồ khối) 2) Soạn thảo mã nguồn của ch−ơng trình (ch−ơng trình nguồn, hay lời ch−ơng trình), tức là ngôn ngữ hoá các thuật giải, theo đúng trình tự đã lập và l−u vào một (hoặc một số) file với phần mở rộng là *.f90 (hoặc *.f, *.for, ngầm định đối với Fortran 77). 3) Tiến hành biên dịch ch−ơng trình. ở b−ớc này nếu ch−ơng trình vẫn còn lỗi cú pháp ta sẽ quay lại b−ớc 2) để chỉnh sửa rồi tiếp tục biên dịch lại ch−ơng trình. Quá trình cứ tiếp diễn cho đến khi trình biên dịch tạo đ−ợc file đích (Ojective file) và thực hiện liên kết (link) để nhận đ−ợc file thực hiện (executable file). 4) Chạy ch−ơng trình (tức chạy file thực hiện) để nhận đ−ợc kết quả. Sau khi nhận đ−ợc kết quả tính ta cần phân tích, xem xét tính hợp lý, đúng đắn của nó. Nếu kết quả không phù hợp cần phải xem xét lại b−ớc 1) và b−ớc 2). 1.2 Cấu trúc chung của một ch−ơng trình FORTRAN Cấu trúc chung của một ch−ơng trình Fortran đơn giản nh− sau (những phần đặt trong dấu ngoặc vuông là tuỳ chọn, có thể có, cũng có thể không): [PROGRAM TenChuongTrinh] [Cac_cau_lenh_khai_bao] [Cac_cau_lenh_thuc_hien] END [PROGRAM [TenChuongTrinh]] Nh− đã thấy, chỉ có một câu lệnh bắt buộc trong ch−ơng trình Fortran là END. Câu lệnh này báo cho ch−ơng trình dịch rằng không còn câu lệnh nào hơn nữa để dịch. Ký hiệu END [PROGRAM [TenChuongTrinh]] có nghĩa rằng có thể bỏ qua TenChuongTrinh trong câu lệnh END, nh−ng nếu có TenChuongTrinh thì từ khoá PROGRAM là bắt buộc. TenChuongTrinh là tên của ch−ơng trình, th−ờng đ−ợc đặt một cách tùy ý sao cho mang tính gợi nhớ, rằng ch−ơng trình sẽ giải quyết vấn đề gì. Cac_cau_lenh_khai_bao là những câu lệnh khai báo biến, hằng, và kiểu dữ liệu t−ơng ứng của chúng để trình 15
  15. biên dịch cấp phát bộ nhớ, phân luồng xử lý. Cac_cau_lenh_thuc_hien là những câu lệnh xác định qui tắc và trình tự thực hiện tính toán, xử lý để đạt đ−ợc kết quả. Trong cấu trúc trên, các mục (nếu có) bắt buộc phải xuất hiện theo trình tự nh− đã mô tả. Có nghĩa là sau câu lệnh mô tả tên ch−ơng trình sẽ là các câu lệnh khai báo, tiếp theo là các câu lệnh thực hiện. Câu lệnh END phải đặt ở cuối ch−ơng trình. 1.3 Cấu trúc câu lệnh Dạng câu lệnh cơ bản của mọi ch−ơng trình Fortran 90 có thể gồm từ 0 đến 132 ký tự (câu lệnh có thể là trống rỗng; câu lệnh trống rỗng làm cho ch−ơng trình dễ đọc hơn bởi sự phân cách lôgic giữa các đoạn). Đối với phiên bản Fortran 77 và các phiên bản tr−ớc đó, nội dung các câu lệnh phải bắt đầu từ cột thứ 7 và kéo dài tối đa đến cột thứ 72. Nếu câu lệnh có nội dung dài hơn, nó sẽ đ−ợc ngắt xuống dòng d−ới, và ở dòng nối tiếp này phải có một ký tự bất kỳ (khác dấu cách) xuất hiện ở cột thứ 6. Bạn đọc cần l−u ý đặc điểm này khi sử dụng các ch−ơng trình của ng−ời khác, hoặc của chính mình, lập trình với các phiên bản Fortran 77 và tr−ớc đó. Fortran 90 không có sự hạn chế đó. Một câu lệnh cũng có thể có nhãn. Nhãn là một số nguyên d−ơng trong khoảng 1−99999. Nhãn (nếu có) phải là duy nhất trong một ch−ơng trình và phải đặt ở đầu câu lệnh, phân cách với nội dung câu lệnh bởi ít nhất một dấu cách. Đối với Fortran 77 và các phiên bản tr−ớc, nhãn đ−ợc ghi vào các cột 1−5. Tất cả các câu lệnh, trừ câu lệnh gán (ví dụ Sotien = 1000.0), đều bắt đầu bằng các từ khoá (keyword). Trên đây chúng ta đã gặp một số từ khoá nh− END, PRINT, PROGRAM, và REAL. Nói chung trên mỗi dòng có một câu lệnh. Tuy nhiên, nhiều câu lệnh cũng có thể xuất hiện trên một dòng, nh−ng chúng phải đ−ợc phân cách nhau bởi các dấu chấm phẩy (;). Để cho rõ ràng, chỉ nên viết những câu lệnh gán rất ngắn, nh−: A = 1; B = 1; C = 1 Những câu lệnh dài có thể đ−ợc viết trên nhiều dòng và phải có ký hiệu nối dòng (sẽ đ−ợc trình bày d−ới đây). 1.3.1 ý nghĩa của dấu cách (Blank) Nói chung các dấu cách là không quan trọng, ta có thể sử dụng chúng để làm cho ch−ơng trình dễ đọc hơn bằng cách viết thụt câu lệnh vào (thêm dấu cách vào phía bên trái) hoặc chèn vào giữa các câu lệnh. Tuy nhiên, cũng có những chỗ không đ−ợc phép chèn dấu cách vào, nh− các qui −ớc về cách viết từ khóa, tên biến, mà ta gọi là các ký hiệu qui −ớc. Ký hiệu qui −ớc trong Fortran 90 là một chuỗi liên tiếp các ký tự có ý nghĩa, chẳng hạn các nhãn, các từ khóa, tên, hằng, Nh− vậy, các cách viết INTE GER, So Tien và < 16
  16. = là không đ−ợc phép (<= là một phép toán), vì giữa chúng có dấu cách không hợp lệ, trong khi A * B thì đ−ợc phép và giống nh− A*B. Tuy nhiên, tên, hằng hoặc nhãn cần phải đ−ợc phân cách với các từ khoá, tên, hằng hoặc nhãn khác ít nhất một dấu cách. Nh− vậy REALX và 30CONTINUE là không đ−ợc phép (vì X là biến, còn 30 là nhãn). 1.3.2 Lời chú thích Mọi ký tự theo sau dấu chấm than (!) (ngoại trừ trong xâu ký tự) là lời chú thích, và đ−ợc ch−ơng trình dịch bỏ qua. Toàn bộ nội dung trên cùng một dòng có thể là lời chú thích. Dòng trắng cũng đ−ợc dịch nh− dòng chú thích. Lời chú thích có thể đ−ợc dùng một cách tuỳ ý để làm cho ch−ơng trình dễ đọc. Đối với Fortran 77, nếu cột đầu tiên có ký tự “C” hoặc “c” thì nội dung chứa trên dòng đó sẽ đ−ợc hiểu là lời chú thích. Qui tắc này không đ−ợc Fortran 90 chấp nhận. Nh−ng thay cho các ký tự “C” hoặc “c", nếu sử dụng ký tự dấu chấm than thì chúng lại t−ơng đ−ơng nhau. 1.3.3 Dòng nối tiếp Nếu câu lệnh quá dài nó có thể đ−ợc chuyển một phần xuống dòng tiếp theo bằng cách thêm ký hiệu nối dòng (&) vào cuối cùng của dòng tr−ớc khi ngắt phần còn lại xuống dòng d−ới. Ví dụ: A = 174.6 * & (T - 1981.2) 3 Nh− đã nói ở trên, Fortran 77 sử dụng cột thứ 6 làm cột nối dòng, do đó cách chuyển tiếp dòng của Fortran 90 sẽ không t−ơng thích với Fortran 77. Dấu & tại cuối của dòng chú thích sẽ không đ−ợc hiểu là sự nối tiếp của dòng chú thích, vì khi đó & đ−ợc xem nh− là một phần của chú thích. 1.4 Kiểu dữ kiệu Nh− đã thấy trên đây, các ch−ơng trình Fortran th−ờng đ−ợc bắt đầu bằng các câu lệnh khai báo biến, hằng và kiểu dữ liệu của chúng. Khái niệm kiểu dữ liệu (data type) là khái niệm cơ bản trong Fortran 90. Kiểu dữ liệu bao gồm tập hợp các giá trị dữ liệu (chẳng hạn, toàn bộ các số), cách thức biểu thị chúng (ví dụ, −2, 0, 999), và tập hợp các phép toán (ví dụ, phép toán số học) cho phép xuất hiện trong chúng. Fortran 90 định nghĩa 5 kiểu dữ liệu chuẩn, đ−ợc chia thành hai lớp là lớp các kiểu số (numeric) gồm số nguyên (integer), số thực (real) và số phức (complex), và lớp các kiểu không phải số (non-numeric) gồm kiểu ký tự (character) và kiểu lôgic (logical). 17
  17. Liên kết với mỗi kiểu dữ liệu là các loại (kind) dữ liệu. Về cơ bản điều đó liên quan đến khả năng l−u trữ và biểu diễn giá trị dữ liệu. Chẳng hạn, có thể có hai loại số nguyên (integer): số nguyên ngắn và số nguyên dài. Chúng ta sẽ đề cập đến vấn đề này sâu hơn ở các phần sau. Ngoài các kiểu dữ liệu chuẩn trên đây, ta có thể định nghĩa cho riêng mình các kiểu dữ liệu khác, chúng có thể có các tập giá trị và các phép toán riêng. Gắn liền với các kiểu dữ liệu còn có các thuộc tính dữ liệu. Fortran định nghĩa khá nhiều thuộc tính, sau đây là một số thuộc tính thông dụng: − PARAMETER: thuộc tính hằng, − DIMENSION: thuộc tính mảng, − ALLOCATABLE: thuộc tính cấp phát động, − POINTER: thuộc tính con trỏ, Thuộc tính có thể đ−ợc dùng đi kèm với câu lệnh khai báo kiểu dữ liệu để mô tả kiểu dữ liệu của biến, hằng. Trong nhiều tr−ờng hợp thuộc tính cũng có thể đ−ợc dùng độc lập nh− những câu lệnh khai báo. 1.4.1 Lớp các kiểu số (Integer, Real, Complex) a. Kiểu số nguyên Dữ liệu có kiểu số nguyên là những dữ liệu nhận các giá trị thuộc tập số nguyên, ví dụ 0, 1, 2, 3, , −5, −10, Đó là tập hợp các số có thể “đếm đ−ợc” hay tập có thứ tự, tức là một số nguyên bất kỳ luôn có một số liền tr−ớc và một số liền sau. Để khai báo biến hoặc hằng có kiểu số nguyên ta sử dụng câu lệnh: INTEGER [([KIND=]kind)][,attrs] ::] vname Trong đó: kind là loại, nhận một trong các giá trị 1, 2, 4 hoặc 8 (đối với UNIX hoặc LINUX). attrs là thuộc tính, nhận một, hoặc nhiều hơn, trong các giá trị PARAMETER, DIMENSION, ALLOCATABLE, POINTER, vname là danh sách biến hoặc hằng, đ−ợc viết cách nhau bởi các dấu phẩy. Tùy theo loại mà một biến/hằng nguyên sẽ chiếm dung l−ợng bộ nhớ và phạm vi giá trị là lớn hay nhỏ. Trong bảng 1.1 dẫn ra miền giá trị hợp lệ đối với các loại số nguyên đ−ợc khai báo, trong đó cột 1 biểu thị những cách có thể khai báo, cột 2 là dung l−ợng bộ nhớ bị chiếm giữ ứng với các loại số nguyên, và cột 3 là phạm vi giá trị của các loại số nguyên t−ơng ứng đã khai báo. Bảng 1.1 Miền giá trị và dung l−ợng bộ nhớ của kiểu số nguyên Cách khai báo Số byte chiếm giữ Phạm vi giá trị INTEGER 4 −2 147 483 648 đến 18
  18. 2 147 483 647 INTEGER*1 hoặc 1 −128 đến 127 INTEGER (1) hoặc INTEGER (KIND=1) INTEGER*2 hoặc 2 −32 768 đến 32 767 INTEGER (2) hoặc INTEGER (KIND=2) INTEGER*4 hoặc 4 −2 147 483 648 đến INTEGER (4) hoặc 2 147 483 647 INTEGER (KIND=4) Các ví dụ sau đây cho thấy có thể sử dụng các cách khác nhau để khai báo kiểu số nguyên cho các biến, hằng. INTEGER, DIMENSION(:), POINTER :: days, hours INTEGER(2), POINTER :: k, limit INTEGER(1), DIMENSION(10) :: min Tất cả các biến đ−ợc khai báo trên đây đều có kiểu số nguyên. Dòng thứ nhất khai báo các biến days, hours là những biến mảng một chiều có thuộc tính con trỏ, với kích th−ớc ch−a xác định, mỗi phần tử mảng là một số nguyên 4 byte; dòng thứ hai khai báo hai biến đơn (biến vô h−ớng) k, limit có thuộc tính con trỏ kiểu số nguyên loại 2 byte; dòng thứ ba khai báo một biến mảng min gồm 10 phần tử, mỗi phần tử là một số nguyên loại 1 byte. Những khai báo trên t−ơng đ−ơng với cách khai báo d−ới đây: INTEGER days, hours INTEGER(2) k, limit INTEGER(1) min DIMENSION days(:), hours(:), min (10) POINTER days, hours, k, limit Các biến trên cũng có thể đ−ợc khởi tạo giá trị ban đầu thông qua các lệnh khai báo, chẳng hạn: INTEGER (2) :: k=4 INTEGER (2), PARAMETER :: limit=12 Trong khai báo trên, biến limit có thuộc tính là PARAMETER nên giá trị của nó sẽ không bị biến đổi trong quá trình thực hiện ch−ơng trình. Bởi vậy nó đ−ợc gọi là hằng, khác với k là biến. Cũng có thể khai báo biến và hằng d−ới dạng sau đây: INTEGER days, hours INTEGER (2):: k=4, limit DIMENSION days(:), hours(:) POINTER days, hours PARAMETER (limit=12) Với cách khai báo này, các từ khóa DIMENSION, POINTER, PARAMETER (ở ba dòng cuối) đ−ợc gọi là các lệnh khai báo, dùng để định nghĩa biến, hằng và thuộc tính của chúng. b. Kiểu số thực Kiểu số thực nói chung gần giống với tập số thực trong toán học. Khác với kiểu số nguyên, kiểu số thực là tập hợp “không đếm đ−ợc”, hay tập không có thứ tự. Để biểu diễn số thực Fortran 90 sử dụng hai ph−ơng pháp gần đúng là độ chính xác đơn và độ chính xác kép. Có thể khai báo kiểu số thực bằng câu lệnh: 19
  19. REAL [([KIND=]kind)][[,attrs] ::] vname Đối với số thực độ chính xác kép (hay độ chính xác gấp đôi) ta còn có thể sử dụng câu lệnh khai báo: DOUBLE PRECISION [[,attrs] ::] vname Trong đó: kind là loại, nhận giá trị 4, 8 hoặc 16 (đối với UNIX hoặc LINUX). attrs là thuộc tính, nhận một, hoặc nhiều hơn, trong các giá trị PARAMETER, DIMENSION, ALLOCATABLE, POINTER, vname là danh sách biến hoặc hằng, viết cách nhau bởi các dấu phẩy. Cách khai báo, phạm vi giá trị, độ chính xác và dung l−ợng bộ nhớ bị chiếm giữ ứng với từng loại số thực đ−ợc cho trong bảng 1.2, trong đó các cột 1, 2, 4 đ−ợc mô tả t−ơng tự nh− các cột 1, 2, 3 trong bảng 1.1. Riêng cột thứ 3 ở đây, do số thực chỉ đ−ợc biểu diễn gần đúng nên giá trị của chúng chỉ đạt đ−ợc độ chính xác nhất định tùy theo dung l−ợng ô nhớ dùng để mô tả chúng. Độ chính xác trong tr−ờng hợp này đ−ợc hiểu là số chữ số có thể biểu diễn chính xác giá trị của biến/hằng thực. Ví dụ, nếu chạy ch−ơng trình sau đây Real x x = 123456789.0 print '(F30.2)', x end ta sẽ nhận đ−ợc kết quả trên màn hình là: X= 123456800.00 Có lẽ bạn đọc sẽ ngạc nhiên, vì biến x chỉ đ−ợc gán giá trị rồi in ra mà giá trị in ra lại khác với giá trị gán vào? Nguyên nhân của sự khác nhau này là ở chỗ, ta đã khai báo biến x là loại số thực 4 byte, do đó chỉ có 6 chữ số đầu tiên biểu diễn chính xác giá trị của biến x. Bảng 1.2 Miền giá trị và dung l−ợng bộ nhớ của kiểu số thực Số byte Độ chính Cách khai báo chiếm giữ xác (số Phạm vi giá trị chữ số) REAL −3.4028235E+38 đến −1.1754944E−38; REAL*4 4 6 0; REAL (KIND=4) +1.1754944E−38 đến +3.4028235E+38 REAL*8 −1.797693134862316D+308 đến REAL (KIND=8) 8 15 −2.225073858507201D−308; DOUBLE PRECISION 0; +2.225073858507201D−308 đến +1.797693134862316D+308 Sau đây là một số ví dụ khai báo các biến, hằng có kiểu số thực. ! Khai bao cac bien co kieu du lieu so thuc REAL X, Y(10) REAL*4 A,B REAL (KIND=8), DIMENSION (5) :: U,V DOUBLE PRECISION, DIMENSION (:), ALLOCATABLE :: T 20
  20. REAL, PARAMETER :: R_TDat = 6370.0 Dòng thứ nhất khai báo một biến đơn X và một biến mảng Y gồm 10 phần tử, chúng đều là những số thực loại 4 byte; dòng thứ hai khai báo hai biến đơn A và B là những biến thực loại 4 byte; dòng thứ ba khai báo hai biến mảng U, V, mỗi biến gồm 5 phần tử là những số thực loại 8 byte; dòng thứ t− khai báo biến mảng thuộc tính động T có độ chính xác gấp đôi, tức mỗi phần tử mảng chiếm 8 byte; dòng cuối cùng khai báo hằng đơn R_TDat, có giá trị khởi tạo bằng 6370.0. c. Kiểu số phức Số phức đ−ợc định nghĩa nh− một cặp có thứ tự của hai số thực đ−ợc gọi là phần thực và phần ảo. Dữ liệu kiểu số phức đ−ợc khai báo bằng câu lệnh: COMPLEX [([KIND =]kind)] [[,attrs] :: ] vname Trong đó tham số kind nhận giá trị 4 hoặc 8; tham số attrs là một hoặc nhiều thuộc tính, nhận các giá trị PARAMETER, DIMENSION, ALLOCATABLE, POINTER, ; vname là danh sách biến hoặc hằng, viết cách nhau bởi các dấu phẩy. Độ chính xác và phạm vi giá trị của kiểu số phức là độ chính xác và phạm vi giá trị của các phần thực và phần ảo. Dung l−ợng bộ nhớ chiếm giữ của một số phức là dung l−ợng của hai số thực. Bảng 1.3 liệt kê các cách khai báo và số byte chiếm giữ của các biến, hằng có kiểu số phức. Ví dụ, câu lệnh: COMPLEX (4), DIMENSION (8) :: cz, cq khai báo hai biến phức cz và cq, mỗi biến là một mảng gồm 8 phần tử phức, tức là 8 cặp số thực, mỗi số thực chiếm 4 byte. Câu lệnh này t−ơng đ−ơng với hai câu lệnh sau: COMPLEX(4) cz, cq DIMENSION(8) cz, cq Bảng 1.3 Miền giá trị và dung l−ợng bộ nhớ của kiểu số phức Cách khai báo Số byte chiếm giữ COMPLEX COMPLEX *4 8 COMPLEX (4) COMPLEX (KIND=4) COMPLEX *8 COMPLEX (8) 16 COMPLEX (KIND=8) DOUBLE CPMPLEX 1.4.2 Kiểu ký tự (Character) và kiểu lôgic (Logical) a. Kiểu ký tự Kiểu ký tự có tập giá trị là các ký tự lập thành xâu (chuỗi) ký tự. Độ dài của xâu là số ký tự trong xâu đã đ−ợc khai báo. Mỗi ký tự trong xâu ký tự chiếm 1 byte bộ nhớ. Do đó, số byte chiếm giữ bộ nhớ của biến, hằng kiểu ký tự tùy thuộc độ dài của xâu. Câu lệnh tổng quát khai báo biến, hằng kiểu ký tự có thể là một trong các cách sau. Cách 1: 21
  21. CHARACTER (length) vname Trong đó length là một số nguyên d−ơng chỉ độ dài cực đại của vname; vname là danh sách tên biến, hằng có kiểu xâu ký tự, viết cách nhau bởi dấu phẩy. Cách 2: CHARACTER (type[,type ])[attrib[,attrib] ] :: vname Với type là tham số độ dài và loại, nhận một trong các dạng: (LEN = type-value) (KIND = expr) (KIND = expr, LEN = type-value) ([LEN =] type-value, KIND = expr) trong đó type−value có thể là dấu sao (*), hằng nguyên không dấu, hoặc biểu thức nguyên; expr là biểu thức xác định giá trị hằng nguyên t−ơng ứng với ph−ơng pháp biểu diễn ký tự (chẳng hạn, chữ cái Latinh, chữ cái Hylạp, ). attrib là một hoặc nhiều thuộc tính, viết cách nhau bởi dấu phẩy. Nếu chỉ ra thuộc tính thì sau đó phải sử dụng dấu (::). Các thuộc tính có thể là: ALLOCATABLE, DIMENSION, PARAMETER, POINTER, Cách 3: CHARACTER [*chrs] vname [*lengths][(dim)] & [/values/][,vname [*lengths][(dim)]] [/values/] Trong đó: chrs là độ dài (cực đại) của các xâu, có thể là một số nguyên không dấu, biểu thức nguyên nằm trong ngoặc đơn, hoặc dấu sao nằm trong ngoặc đơn (*); lengths là độ dài (cực đại) của xâu, có thể là số nguyên không dấu, biểu thức nguyên nằm trong ngoặc đơn, hoặc dấu sao nằm trong ngoặc đơn (*); dim: khai báo mảng, tức vname nh− là mảng; /values/ là liệt kê các hằng ký tự, tức giá trị của các biến, hằng vname. Ví dụ: CHARACTER (20) St1, St2*30 CHARACTER wt*10, city*80, ch CHARACTER (LEN = 10), PRIVATE :: vs CHARACTER*(*) arg CHARACTER name(10)*20 CHARACTER(len=20), dimension(10):: plume CHARACTER(2) susan,patty,alice*12,dotty, jane(79) CHARACTER*5 word /'start'/ Các khai báo trên đây có ý nghĩa nh− sau: biến St1 có độ dài cực đại bằng 20 ký tự, biến St2 có độ dài cực đại bằng 30 ký tự; các biến wt, city, ch t−ơng ứng có độ dài cực đại là 10, 80 và 1 ký tự; biến vs có độ dài cực đại bằng 10 ký tự và có thuộc tính PRIVATE; biến arg có độ dài không xác định; các biến mảng một chiều name, plume mỗi mảng gồm 10 phần tử, mỗi phần tử là một xâu có độ dài cực đại 20 ký tự; các biến susan, patty, dotty có độ dài cực đại 2 ký tự, biến alice có độ dài cực đại 12 ký tự 22
  22. và biến mảng jane gồm 79 phần tử, mỗi phần tử là một xâu dài 2 ký tự; biến word dài tối đa 5 ký tự và đ−ợc khởi tạo giá trị đầu bằng 'start'. b. Kiểu lôgic Dữ liệu kiểu lôgic chỉ nhận các giá trị .TRUE. hoặc .FALSE. Câu lệnh khai báo kiểu dữ liệu lôgic có dạng: LOGICAL [([KIND=]kind)] [, attrs ::] vname Trong đó: kind: là độ dài tính bằng byte, nhận các giá trị 1, 2, hoặc 4. attrs: là các thuộc tính, có thể nhận một hoặc nhiều giá trị, phân cách nhau bởi dấu phẩy. vname: Danh sách các biến, hằng, phân cách nhau bởi dấu phẩy. Số byte chiếm giữ bộ nhớ của kiểu dữ liệu logic phụ thuộc vào loại dữ liệu nh− mô tả trong bảng 1.4. Bảng 1.4 Miền giá trị và dung l−ợng bộ nhớ của kiểu lôgic Cách khai báo Loại (KIND) Số byte chiếm giữ LOGICAL 4 4 LOGICAL*1 hoặc LOGICAL (1) hoặc 1 1 LOGICAL (KIND=1) LOGICAL*2 hoặc LOGICAL (2) hoặc 2 4 LOGICAL (KIND=2) LOGICAL*4 hoặc LOGICAL (4) hoặc 4 4 LOGICAL (KIND=4) Ví dụ, các câu lệnh sau đây khai báo các biến có kiểu lôgic d−ới các dạng khác nhau: LOGICAL, ALLOCATABLE :: flag1, flag2 LOGICAL (2), SAVE :: doit, dont = .FALSE. LOGICAL switch Cách khai báo đó hoàn toàn t−ơng đ−ơng với các câu lệnh khai báo sau đây: LOGICAL flag1, flag2 LOGICAL (2) doit, dont = .FALSE. ALLOCATABLE flag1, flag2 SAVE doit, dont 1.4.3 Phép toán trên các kiểu dữ liệu Trong các ví dụ tr−ớc đây ta đã thấy một số biểu thức viết bằng ngôn ngữ Fortran trong đó có sử dụng một số phép toán, nh− phép nhân hai số, phép cộng hai số, Tuy nhiên, với các kiểu dữ liệu khác nhau, các phép toán trên chúng cũng có thể khác nhau. Sau đây sẽ trình bày chi tiết hơn về vấn đề này. Nói chung, Fortran định nghĩa bốn lớp phép toán t−ơng ứng với các kiểu dữ liệu đã đ−ợc mô tả: 23
  23. − Phép toán số học: Sử dụng với các kiểu số nguyên, số thực và số phức. − Phép toán quan hệ, hay phép toán so sánh: Sử dụng với các kiểu số nguyên, số thực, kiểu ký tự, và cũng có thể đối với cả số phức trong tr−ờng hợp so sánh bằng hoặc không bằng. − Phép toán lôgic: Sử dụng với kiểu lôgic, và có thể với cả số nguyên. − Phép toán gộp xâu ký tự: Sử dụng với kiểu ký tự. Bảng 1.5 liệt kê ký hiệu các phép toán, thứ tự −u tiên, thứ tự thực hiện trong biểu thức và ý nghĩa của chúng, trong đó thứ tự −u tiên đ−ợc xếp sao cho mức −u tiên cao nhất là 1. Bảng 1.5 Định nghĩa các phép toán trong Fortran Ký hiệu phép Tên gọi/ý nghĩa Thứ tự −u Thứ tự thực hiện Ví dụ toán tiên Phép toán số học Phép lũy thừa 1 Phải sang trái A B * Phép nhân 2 Trái sang phải A * B / Phép chia 2 Trái sang phải A / B + Phép cộng 3 Trái sang phải A + B − Phép trừ 3 Trái sang phải A − B Phép toán quan hệ .EQ. ( == ) Bằng − Không phân định A.EQ.B; A == B .LT. ( ) Lớn hơn − Không phân định A.GT.B; A > B .GE. ( >= ) Lớn hơn hoặc bằng − Không phân định A.GE.B; A >= B .NE. ( /= ) Không bằng (Khác) − Không phân định A.NE.B; A /= B Phép toán lôgic .NOT. Phủ định 1 Không phân định .NOT. L1 .AND. Và (Phép hội) 2 Trái sang phải L1. AND. L2 .OR. Hoặc (Phép tuyển) 3 Trái sang phải L1. OR. L2 .XOR. Hoặc triệt tiêu 4 Trái sang phải L1. XOR. L2 .EQV. T−ơng đ−ơng 4 Trái sang phải L1. EQV. L2 .NEQV. Không t−ơng đ−ơng 4 Trái sang phải L1. NEQV. L2 Gộp ký tự // Gộp hai xâu ký tự − Trái sang phải ST1 // ST2 Mặc dù vậy, trong lúc viết ch−ơng trình ta cần chú ý một số điểm sau đây khi thực hiện các phép toán đối với các kiểu dữ liệu khác nhau: − Trong một biểu thức số học, nếu các toán hạng có cùng kiểu dữ liệu thì kiểu dữ liệu kết quả là kiểu dữ liệu của các toán hạng. Nếu các toán hạng khác kiểu dữ liệu thì kết quả nhận đ−ợc sẽ có kiểu dữ liệu của toán hạng có kiểu “mạnh nhất”. Chẳng hạn, biểu thức gồm hỗn hợp cả số nguyên và số thực thì kết quả sẽ có kiểu số thực, vì kiểu số thực “mạnh hơn” kiểu số nguyên. Tuy nhiên, khi gán kết quả đó cho một biến thì kiểu của kết quả sẽ đ−ợc chuyển thành kiểu dữ liệu của biến. Ví dụ, nếu a, b, x là các biến thực, còn n là biến nguyên thì: a=−22.9; b=6.1 => x=a+b=−16.8; nh−ng n=a+b=−16 a=2.9; b=6.8 => x=a+b=9.7; nh−ng n=a+b = 9 Nh− đã thấy, giá trị của a+b sau khi gán cho n thì phần thập phân sẽ bị “chặt cụt”. 24
  24. − Kết quả của biểu thức quan hệ và biểu thức lôgic luôn luôn nhận giá trị hoặc .TRUE. hoặc .FALSE. Sau đây là môt số ví dụ −ớc l−ợng giá trị của biểu thức: 2 9 0.5 cho kết quả là 8 10 + 3 * 2 4 − 16 / 2 cho kết quả là 50 3.5 > 7.2 cho kết quả là .FALSE. Nếu a và b là hai biến lôgic, khi đó các phép toán giữa a và b sẽ cho kết quả: Giá trị của a và a .AND. b a .OR. b a .EQV. b a .NEQV. b a.XOR.b của b a=.TRUE., .TRUE. .TRUE. .TRUE. .FALSE. .FALSE. b=.TRUE. a=.TRUE., .FALSE. .TRUE. .FALSE. .TRUE. .TRUE. b=.FALSE. a=.FALSE., .FALSE. .TRUE. .FALSE. .TRUE. .TRUE. b=.TRUE. a=.FALSE., .FALSE. .FALSE. .TRUE. .FALSE. .FALSE. b=.FALSE. Nếu ST1 và ST2 là hai xâu ký tự nhận các giá trị: ST1=’Hanoi’ ST2=’ − Vietnam’ thì ST1 // ST2 sẽ cho kết quả là ‘Hanoi − Vietnam’ 1.5 Hằng Hằng là những ký hiệu qui −ớc đ−ợc sử dụng để biểu thị các giá trị có kiểu riêng. Mỗi kiểu dữ liệu có một loại hằng t−ơng ứng. 1.5.1 Hằng nguyên Hằng nguyên đ−ợc sử dụng để biểu thị các giá trị kiểu số nguyên thực sự. Biểu diễn đơn giản nhất và rõ ràng nhất là số nguyên không dấu hoặc có dấu. Trong tr−ờng hợp hằng nguyên d−ơng, thì dấu là không bắt buộc (tuỳ ý). Ví dụ: 1000, 0, +753, −999999, 2501 là những hằng biểu diễn trong hệ cơ số thập phân (cơ số 10). Các số d−ơng cũng có thể đ−ợc biểu diễn d−ới dạng nhị phân (binary − cơ số 2), bát phân (octal − cơ số 8) hoặc thập lục phân (hexa − cơ số 16), ví dụ: trong hệ cơ số 2 (binary): B'1011' trong hệ cơ số 8 (octal): O'0767' trong hệ cơ số 16 (hexadecimal): Z'12EF' 25
  25. Trong các biểu diễn trên, có thể sử dụng chữ in th−ờng hoặc chữ in hoa. Dấu nháy kép (") có thể đ−ợc sử dụng thay cho dấu nháy đơn (') nh− là sự phân ranh giới. Tuy nhiên, các dạng thức này không đ−ợc dùng với câu lệnh DATA. 1.5.2 Hằng thực Hằng thực dùng để biểu thị các giá trị có kiểu số thực và có hai dạng. − Dạng thứ nhất đ−ợc viết rất rõ ràng, gọi là dạng dấu phẩy tĩnh, bao gồm một dãy số có chứa dấu chấm thập phân. Nó có thể có dấu hoặc không dấu. Ví dụ: 0.09 37. 37.0 .0 -.6829135 Nh− vậy, khi viết một hằng thực, có thể không có số nào phía bên trái hoặc phía bên phải dấu chấm thập phân (nh− 37. và .0), nh−ng chỉ có một dấu chấm thập phân không thôi thì không đ−ợc phép. − Dạng thứ hai đ−ợc gọi là dạng dấu phẩy động. Về cơ bản nó bao gồm hoặc một số nguyên hoặc một số thực dấu phẩy tĩnh (có thể có dấu hoặc không) và sau đó là chữ cái E (hoặc e), tiếp theo là số nguyên (có dấu hoặc không). Số nguyên đứng đằng sau E là chỉ số mũ của 10, hàm ý rằng đó là 10 luỹ thừa mà số đằng tr−ớc E phải nhân với nó. Ví dụ: 2.0E2 (2.0 ì 102 = 200.0) 2E2 (2 ì 102 = 200.0) 4.12E+2 (4.12 ì 10+2 = 412.0) -7.321E-4 (−7.321 ì 10−4 = -0.0007321) Hằng thực đ−ợc l−u trữ d−ới dạng luỹ thừa trong bộ nhớ, không quan trọng chúng đ−ợc viết thực sự nh− thế nào. Bởi vậy, nếu số thực có thể đ−ợc biểu diễn d−ới dạng phân số thì chúng cũng sẽ đ−ợc biểu diễn gần đúng. Thậm chí, giữa số nguyên và số thực có cùng giá trị, chúng cũng đ−ợc biểu diễn khác nhau. Ví vụ 43 là số nguyên, trong khi 43.0 (hoặc 43.) là số thực, và chúng sẽ đ−ợc biểu diễn khác nhau trong bộ nhớ. Phạm vi và độ chính xác của hằng thực không đ−ợc chỉ ra một cách chuẩn xác, nh−ng độ chính xác khoảng 6−7 chữ số thập phân. 1.5.3 Hằng ký tự Hằng ký tự là một chuỗi các ký tự nằm trong cặp dấu nháy đơn (‘ ’) hoặc nháy kép (“ ”). Ngoại trừ các ký tự điều khiển (chẳng hạn, #27 là ESC), những ký tự khác thuộc bảng mã ký tự ASCII (American Standard Code for Information Interchange − Bảng mã chuẩn dùng để trao đổi thông tin giữa các thiết bị máy tính, gồm 256 ký tự, kể cả các chữ cái, ký tự thông th−ờng, ký tự điều khiển và ký tự đồ họa) đều có thể đ−ợc sử dụng để biểu diễn hằng ký tự. Bởi vì mỗi ký tự trong bảng mã ASCII t−ơng ứng với số thứ tự duy nhất của nó, nên các giá trị của hằng ký tự có sự phân biệt giữa chữ th−ờng và chữ hoa. Ví dụ: “HANOI” khác với “Hanoi”, hoặc “Hai Phong” khác với “Hai phong”, 26
  26. Cũng cần phân biệt rõ khái niệm hằng mà ta vừa đề cập trên đây với hằng đ−ợc khai báo trong câu lệnh thuộc tính PARAMETER. Khái niệm hằng ở đây là những giá trị cụ thể, chúng có thể đ−ợc gán cho biến hoặc hằng có tên. Còn hằng trong khai báo PARAMETER là hằng có tên đ−ợc xác định bởi tên hằng và nhận giá trị cụ thể là hằng theo khái niệm ở đây; hằng đó sẽ không bị thay đổi giá trị trong quá trình thực hiện ch−ơng trình. Ví dụ, hãy xét đoạn ch−ơng trình sau: REAL, PARAMETER :: X = 12. REAL Y, Z Y = 23. Z = X + Y PRINT*, X, Y, Z END ở đây, X là hằng (có tên), nhận giá trị không đổi (là hằng số) bằng 12. Còn Y và Z là các biến, trong đó Y đ−ợc gán bởi hằng số có giá trị bằng 23. 1.6 Tên biến và tên hằng Ta đã thấy rằng vị trí bộ nhớ có thể đ−ợc chỉ định bởi tên t−ợng tr−ng (symbolic names), gọi là tên biến hoặc tên hằng, nh− SoTien và LaiSuat. Tên biến, tên hằng có thể gồm 1 đến 31 ký tự, và phải bắt đầu bởi một chữ cái tiếng Anh. Các ký tự đ−ợc sử dụng để cấu tạo tên biến, tên hằng gồm 26 chữ cái tiếng Anh, không phân biệt chữ th−ờng, chữ hoa, (A−Z và a−z), 10 chữ số (0−9), và dấu gạch d−ới (_). Ngoại trừ hằng xâu ký tự, Fortran không phân biệt tên viết bằng chữ th−ờng hay chữ hoa, ví dụ MYNAME và MyName là nh− nhau. Có lẽ những ng−ời có truyền thống lập trình Fortran lâu năm th−ờng viết ch−ơng trình chỉ bằng chữ cái in hoa. Tuy nhiên ta nên viết lẫn cả chữ th−ờng và chữ hoa cho dễ đọc. Chẳng hạn, nếu ta viết SoTien chắc chắn sẽ dễ hiểu hơn là viết SOTIEN. Mặt khác, vì Fortran 90, và cả các phiên bản mới hơn sau này, không khống chế độ dài tên chỉ 6 ký tự nh− các phiên bản cũ, nên để rõ ràng, tên viết dài có thể sẽ tốt hơn tên viết ngắn, vì nó mang tính gợi nhớ hơn. Chẳng hạn, nên viết SoTien thay cho cách viết đơn giản ST. Sau đây là một số ví dụ về cách đặt tên biến, tên hằng hợp lệ và không hợp lệ: Tên hợp lệ Tên không hợp lệ X X+Y (vì chứa dấu + là một phép toán) R2D2 SHADOW FAX (vì chứa dấu cách) Pay_Day 2A (vì ký tự đầu tiên là chữ số) ENDOFTHEMONTH OBI-WAN (vì chứa dấu − là một phép toán) Biến là vị trí bộ nhớ mà giá trị của nó có thể bị thay đổi trong quá trình thực hiện ch−ơng trình. Tên của biến đ−ợc cấu tạo theo qui tắc trên đây. Biến có kiểu và loại dữ liệu xác định, đ−ợc cho bởi khai báo kiểu, ví dụ: INTEGER X ! X là biến nguyên 4 byte REAL LaiSuat ! LaiSuat là biến thực 4 byte CHARACTER LETTER ! LETTER là biến ký tự độ dài bằng 1 27
  27. REAL :: A = 1 ! A là biến thực nhận giá trị khởi tạo 1 Chú ý rằng, biến có thể đ−ợc khởi tạo khi khai báo nó, nh− câu lệnh cuối cùng ở ví dụ trên. Trong tr−ờng hợp này phải sử dụng dấu hai chấm kép (::). Giá trị của biến đ−ợc khởi tạo theo cách này có thể bị thay đổi trong quá trình ch−ơng trình thực hiện. Mặc dù các biến X, LaiSuat và LETTER đã đ−ợc khai báo trong đoạn ch−ơng trình trên, nh−ng giá trị của chúng vẫn ch−a đ−ợc xác định. Bạn đọc (đặc biệt là những ng−ời mới bắt đầu lập trình) phải chú ý tránh việc tham chiếu đến các biến ch−a đ−ợc xác định này, vì nó có thể sẽ dẫn đến lỗi trong lúc thực hiện ch−ơng trình (Run−time error), rất khó gỡ rối. Ví dụ, khi chạy ch−ơng trình sau đây ta sẽ nhận đ−ợc kết quả đúng: Real x, y, z x = 3.0 y = 2.0 z = x / y print*, x, y, z end Nh−ng nếu bỏ đi dòng thứ hai và thứ ba rồi chạy lại ch−ơng trình thì lỗi Run−time error sẽ xuất hiện do câu lệnh z = x/y đã tham chiếu đến các biến x và y ch−a xác định. Biến có thể đ−ợc xác định bằng nhiều cách, ví dụ bằng việc khởi tạo nó (nh− ví dụ tr−ớc) hoặc bằng việc gán giá trị cho nó, nh− trong ví dụ trên đây hoặc ở những ví dụ tr−ớc. Biến cũng có thể đ−ợc gán giá trị ban đầu bằng lệnh DATA sau khi đã khai báo, ví dụ: REAL A, B INTEGER I, J DATA A, B / 1, 2 / I, J / 0, -1/ Câu lệnh DATA trên đây lần l−ợt gán các giá trị 1 cho biến A, 2 cho biến B, 0 cho biến I và −1 cho biến J. Tên (kể cả tên biến, tên hằng và tên ch−ơng trình) trong ch−ơng trình phải là duy nhất. Chẳng hạn, nếu ch−ơng trình đ−ợc đặt tên là TinhTien, thì việc khai báo một biến khác cùng tên sẽ dẫn đến lỗi. Các biến đã mô tả trong những ví dụ ở trên gọi là các biến vô h−ớng, hay biến đơn, vì tại một thời điểm chúng chỉ l−u một giá trị đơn nhất. Ngoài các biến vô h−ớng còn có các loại biến khác, chẳng hạn biến mảng. Ta sẽ đề cập chi tiết đến các loại biến này sau. 1.7 Qui tắc kiểu ẩn Các phiên bản tr−ớc của Fortran có một qui tắc đặt tên ngầm định đ−ợc gọi là qui tắc kiểu ẩn (implicit type rule). Theo qui tắc này, các biến bắt đầu bằng các chữ cái I, 28
  28. J, K, L, M, N đ−ợc tự động hiểu là biến có kiểu số nguyên (INTEGER), còn các biến bắt đầu bằng những chữ cái khác, nếu không đ−ợc khai báo rõ ràng, sẽ đ−ợc hiểu là biến thực (REAL). Để bảo đảm tính t−ơng thích của các ch−ơng trình viết với các phiên bản tr−ớc, qui tắc này vẫn đ−ợc áp dụng ngầm định trong Fortran 90. Tuy nhiên, trong một số tình huống, qui tắc kiểu ẩn có thể dẫn đến lỗi ch−ơng trình trầm trọng do những sơ suất đáng tiếc khi đặt tên biến. Giá trị thực có thể đ−ợc gán một cách không cố ý cho biến nguyên, làm cho phần thập phân sau dấu chấm thập phân bị chặt cụt. Ví dụ, nếu không khai báo kiểu REAL cho biến LaiSuat thì câu lệnh LaiSuat = 0.12 trong ch−ơng trình sẽ gán giá trị 0 cho biến LaiSuat, vì nó đ−ợc ngầm hiểu là biến nguyên. Để đề phòng những lỗi nh− vậy, ngay từ đầu ch−ơng trình ta nên đ−a vào câu lệnh sau IMPLICIT NONE Câu lệnh này sẽ xoá bỏ thuộc tính qui tắc kiểu ẩn, do đó tất cả các biến sử dụng trong ch−ơng trình bắt buộc phải đ−ợc khai báo. Đó là cách lập trình tốt, vì có khai báo ta mới buộc phải để tâm đến biến và ý nghĩa của nó. Sau đây ta sẽ xét một ví dụ về giải bài toán chuyển động trong tr−ờng trọng lực. Nếu một hòn đá đ−ợc tung lên thẳng đứng với tốc độ ban đầu u, quãng đ−ờng dịch gt 2 chuyển thẳng đứng s của nó sau thời gian t đ−ợc cho bởi công thức s( t ) = ut − , trong 2 đó g là gia tốc trọng tr−ờng. Bỏ qua lực cản của không khí, hãy tính giá trị của s, khi cho các giá trị của u và t. Để lập ch−ơng trình giải bài toán này ta có thể hình dung lôgic chuẩn bị ch−ơng trình nh− sau: 1) Nhập các giá trị g, u và t vào máy tính 2) Tính giá trị của s theo công thức đã cho 3) In giá trị của s 4) Kết thúc Nhìn dàn bài này có thể một số ng−ời cho là nó quá tầm th−ờng, thậm chí họ cho rằng nó lãng phí thời gian viết ra. Và do đó, ta sẽ không lấy làm ngạc nhiên tại sao một số ng−ời trong đó, nhất là những ng−ời mới bắt đầu lập trình, lại thích làm trực tiếp trên máy tính, và lập trình b−ớc 2 tr−ớc b−ớc 1, để rồi lúng túng tr−ớc kết quả nhận đ−ợc. Thực tế điều này rất quan trọng, vì nó tạo cho ta thói quen phân tích bài toán một cách kỹ l−ỡng, thiết kế ch−ơng trình có tính lôgic, và chọn tên biến, kiểu biến để khai báo một cách phù hợp nhất. 29
  29. Dựa theo các b−ớc trên đây ta có thể viết ch−ơng trình nh− sau: Ví dụ 1.4: Chuyển động trong tr−ờng trọng lực PROGRAM ChuyenDongThangDung ! Chuyen dong thang dung duoi truong luc trong IMPLICIT NONE ! Xóa bỏ qui tắc kiểu ẩn REAL, PARAMETER :: G = 9.8 ! Gia tốc trọng tr−ờng REAL S ! Quãng đ−ờng (m) REAL T ! Thời gian REAL U ! Tốc độ ban đầu (m/s) PRINT*, ' Thoi gian Quang duong' PRINT* U = 60 T = 6 S = U * T - G / 2 * T 2 PRINT*, T, S END PROGRAM ChuyenDongThangDung Tr−ớc hết, khai báo G là hằng, vì giá trị của nó đ−ợc xác định không thay đổi trong ch−ơng trình và nhận giá trị bằng 9.8. Vì trong ch−ơng trình có sử dụng câu lệnh IMPLICIT NONE do đó ta phải khai báo tất cả các biến. Bạn đọc có thể kiểm chứng tác dụng của câu lệnh này bằng cách thử bỏ qua một câu lệnh khai báo biến nào đó (thêm dấu chấm than vào đầu dòng lệnh) và chạy lại ch−ơng trình để xem Fortran phản ứng nh− thế nào. 1.8 Phong cách lập trình Trên thực tế có thể xảy ra tình huống ta cần sử dụng lại hoặc nâng cấp các ch−ơng trình đã lập từ rất lâu rồi, hoặc khai thác các ch−ơng trình do một ng−ời nào đó viết. Sẽ rất khó khăn nếu trong ch−ơng trình chẳng có một lời chú thích nào cả. Đối với những ch−ơng trình của mình, có thể ta đã quên đi những gì mình đã viết. Việc tìm hiểu lại một ch−ơng trình không có những lời chú thích nh− vậy đôi khi làm cho ta nản chí, không đủ kiên nhẫn để thực hiện. Để tránh tình trạng đó, cần phải có một phong cách lập trình tốt. Nghĩa là trong ch−ơng trình phải có những lời chú thích đúng chỗ, đầy đủ, rõ ràng; trong các câu lệnh nên chèn vào những dấu cách hợp lệ, sử dụng hợp lý các ký tự in th−ờng và in hoa; giữa các đoạn ch−ơng trình nên có các dòng trắng; nên phân cấp các câu lệnh để bố trí chúng sao cho có sự thụt, thò, dễ theo dõi. Chẳng hạn, trong các ch−ơng trình đ−ợc viết trên đây, chúng ta th−ờng đ−a vào những lời chú thích mang ý nghĩa mô tả, nh− dòng mô tả ch−ơng trình sẽ làm gì, các biến đ−ợc khai báo có ý nghĩa gì, 30
  30. 1.9 Biểu thức số Trong ch−ơng trình ChuyenDongThangDung ở ví dụ 1.4 ta đã sử dụng dạng mã nguồn sau: U * T - G / 2 * T 2 Đây là một ví dụ về biểu thức số biểu diễn bằng ngôn ngữ Fortran, là công thức liên kết các hằng, các biến (và các hàm, nh− hàm tính căn bậc hai) bằng các phép toán thích hợp. Nó chỉ ra qui tắc để tính giá trị của một biểu thức đại số thông th−ờng. Trong tr−ờng hợp trên đây, biểu thức chỉ tính một giá trị đơn nên nó đ−ợc gọi là biểu thức vô h−ớng. Thứ tự thực hiện các phép tính trong một biểu thức đ−ợc xác định bởi thứ tự −u tiên của các phép toán. Tuy nhiên, nếu trong biểu thức có các bộ phận nằm trong ngoặc đơn ( ) thì chúng luôn luôn đ−ợc thực hiện tr−ớc tiên. Chẳng hạn, biểu thức 1 + 2 * 3 sẽ cho kết quả là 7, trong khi (1 + 2) * 3 sẽ cho kết quả là 9. Chú ý rằng −3 2 sẽ cho kết quả là −9 chứ không phải 9. Khi có các phép toán cùng bậc −u tiên xuất hiện liên tiếp nhau trong biểu thức, chúng sẽ đ−ợc thực hiện theo thứ tự từ trái sang phải, ngoại trừ phép lấy lũy thừa. Do đó, biểu thức B/C*A đ−ợc thực hiện nh− (B/C)*A mà không phải nh− B/(C * A). Đối với các phép toán lũy thừa, thứ tự thực hiện là từ phải sang trái. Ví dụ, biểu thức A B C đ−ợc thực hiện theo nguyên tắc A (B C). 1.9.1 Phép chia với số nguyên Đối với những ng−ời mới lập trình bằng Fortran, đây quả là một vấn đề không đơn giản, bởi vì nhiều khi kết quả nhận đ−ợc của các biểu thức nằm ngoài dự đoán của họ. Vấn đề là ở chỗ, khi một đại l−ợng có kiểu số nguyên (hằng, biến hoặc biểu thức nguyên) chia cho một đại l−ợng có kiểu số nguyên khác, kết quả nhận đ−ợc cũng sẽ có kiểu số nguyên, do phần lẻ thập phân bị cắt bỏ. Ta hãy xét các ví dụ sau đây. 10 / 3 cho kết quả là 3 19 / 4 cho kết quả là 4 4 / 5 cho kết quả là 0 -8 / 3 cho kết quả là -2 3 * 10 / 3 cho kết quả là 10 10 / 3 * 3 cho kết quả là 9 Nh− vậy, khi chia hai đại l−ợng nguyên cho nhau, kết quả nhận đ−ợc là phần nguyên của th−ơng, còn phần d− bị cắt bỏ. 1.9.2 Biểu thức hỗn hợp Fortran 90 cho phép thực hiện phép tính với biểu thức chứa các toán hạng có kiểu khác nhau. Nguyên tắc chung là các kiểu dữ liệu “yếu hơn” hoặc “đơn giản hơn” buộc 31
  31. phải chuyển đổi sang kiểu dữ liệu “mạnh hơn”. Vì kiểu số nguyên là đơn giản nhất, cho nên trong biểu thức có các toán hạng nguyên và thực thì các toán hạng nguyên phải chuyển thành các toán hạng có kiểu thực. Tuy nhiên, quá trình chuyển đổi này chỉ thực hiện đối với từng phép tính mà không nhất thiết áp dụng cho cả biểu thức. Ví dụ: 10 / 3.0 cho kết quả là 3.33333 4. / 5 cho kết quả là 0.8 2 (-2) cho kết quả là 0, vì 2 (−2)=1/(2 2) = 1/4 Nh−ng biểu thức 3 / 2 / 3.0 sẽ cho kết quả bằng 0.333333 vì 3/2 đ−ợc tính tr−ớc, nhận giá trị nguyên bằng 1. 1.10 Lệnh gán. Gán hằng, gán biểu thức Lệnh gán là câu lệnh đ−ợc sử dụng phổ biến nhất trong lập trình. Cú pháp câu lệnh gán có dạng: vname = expr Trong đó vname là tên của biến hoặc hằng, expr là giá trị (hằng) hoặc biểu thức. Mục đích của câu lệnh gán là tính giá trị của biểu thức ở vế phải (nếu cần) và gán cho biến/hằng ở vế trái. Nh− vậy, dấu bằng (=) trong câu lệnh gán hoàn toàn không có nghĩa nh− dấu bằng trong toán học, mà nó đ−ợc hiểu là dấu gán, và nên đọc là vname đ−ợc gán bởi giá trị của expr. Ví dụ, câu lệnh X = A + B đ−ợc hiểu là nội dung của biến X đ−ợc gán bởi giá trị của tổng nội dung của biến A và nội dung của biến B. Khi thực hiện câu lệnh, máy sẽ lấy giá trị của A cộng với giá trị của B, kết quả nhận đ−ợc sau đó sẽ gán cho biến X. T−ơng tự, câu lệnh N = N + 1 hàm nghĩa là tăng giá trị của biến N lên một đơn vị. Đ−ơng nhiên trong toán học biểu thức này không có ý nghĩa. Tác động của quá trình thực hiện câu lệnh là lấy nội dung của biến N cộng với 1, đ−ợc bao nhiêu gán lại cho biến N. Nếu expr không cùng kiểu dữ với vname, nó đ−ợc chuyển đổi sang kiểu dữ liệu của vname tr−ớc khi gán. Có nghĩa là điều đó có thể dẫn đến sai số tính toán. Ví dụ, giả sử N là biến nguyên, còn X và Y là những biến thực thì: N = 10. / 3 (giá trị của N sẽ là 3) X = 10 / 3 (giá trị của X sẽ là 3.0) Y = 10 / 3. (giá trị của Y sẽ là 3.33333) 32
  32. Sự vô ý trong lập trình nhiều lúc cũng có thể dẫn đến kết quả sai không đáng có. Chẳng hạn, khi muốn tính trung bình cộng hai số (ví dụ điểm trung bình của hai môn học), nếu đặt tên biến các môn đó là M1 và M2 mà không khai báo chúng là biến thực (tức máy sẽ hiểu đó là hai biến nguyên theo qui tắc kiểu ẩn), thì điểm trung bình đ−ợc xác định bởi câu lệnh: TBinh = (M1 + M2) / 2 sẽ bị chặt cụt phần thập phân do vế phải là kết quả của phép chia hai số nguyên. Nếu tổng (M1+M2) không chia hết cho 2 thì kết quả nhận đ−ợc là sai. Nh−ng, nếu câu lệnh trên đ−ợc viết d−ới dạng: TBinh = (M1 + M2) / 2.0 thì kết quả lại hoàn toàn chính xác mặc dù M1 và M2 vẫn là những biến nguyên. Sau đây là một số ví dụ về câu lệnh gán. C = (A 2 + B 2) 0.5 / (2 * A) A = P * (1 + R / 100) N Câu lệnh thứ nhất có thể đ−ợc viết bằng cách khác khi sử dụng hàm th− viện SQRT (hàm lấy căn bậc hai) của Fortran nh− sau: C = SQRT ( A 2 + B 2 ) / (2 * A) Tuy nhiên, không đ−ợc viết câu lệnh d−ới dạng: C = (A 2 + B 2) (1/2) / (2 * A) Bởi vì (1/2) trong biểu thức lũy thừa sẽ nhận giá trị bằng 0 do phép chia hai số nguyên cho nhau. 1.11 Lệnh vào ra đơn giản Quá trình nhận thông tin vào và kết xuất thông tin ra của máy tính đ−ợc gọi là quá trình vào ra dữ liệu. Dạng vào ra dữ liệu đơn giản nhất trong Fortran là sử dụng các lệnh READ* và PRINT*, và đ−ợc gọi là vào ra trực tiếp. Các dạng vào ra dữ liệu phức tạp hơn sẽ đ−ợc đề cập đến trong những phần sau. Trong các mục tr−ớc ta đã gặp các câu lệnh với READ* và PRINT*, nh−ng ch−a giải thích gì về chúng. ở đây ta sẽ thấy rằng đó là những câu lệnh rất th−ờng dùng mà ta cần phải tìm hiểu ngay. 1.11.1 Lệnh vào dữ liệu Từ những ví dụ trên nhận thấy các biến đ−ợc gán giá trị bằng cách sử dụng câu lệnh gán, chẳng hạn nh− trong ch−ơng trình TinhTien: SoTien = 1000.0 LaiSuat = 0.09 33
  33. Cách làm này không linh hoạt, vì khi muốn chạy ch−ơng trình với các giá trị số tiền gốc hoặc lãi suất khác nhau, mỗi lần nh− vậy ta phải thay đổi trực tiếp các câu lệnh gán này trong ch−ơng trình, sau đó biên dịch lại rồi mới thực hiện ch−ơng trình. Thay cho cách này ta có thể sử dụng câu lệnh READ* nh− sau: READ*, SoTien, LaiSuat Trong tr−ờng hợp này, khi chạy ch−ơng trình, máy sẽ chờ ta gõ giá trị của các biến từ bàn phím. Các giá trị này có thể đ−ợc gõ trên cùng một dòng, phân cách nhau bởi các dấu cách, dấu phẩy hoặc trên các dòng khác nhau. Dạng tổng quát của lệnh READ* nh− sau: READ*, list Trong đó list là danh sách biến; nếu có nhiều hơn một biến thì chúng đ−ợc viết cách nhau bởi dấu phẩy. Khi vào dữ liệu với lệnh READ* cần chú ý một số điểm sau. − Mỗi dòng dữ liệu đ−ợc gõ liên tục (không dùng dấu ENTER xuống dòng) đ−ợc gọi là một bản ghi. Nếu dòng dữ liệu quá dài, không hiển thị đủ trên một dòng màn hình, nó sẽ đ−ợc tự động “cuộn” xuống dòng d−ới, nh−ng vẫn thuộc cùng một bản ghi. − Mỗi một lệnh READ khi nhận dữ liệu đòi hỏi một bản ghi mới. Khi nhập dữ liệu vào từ bàn phím, mỗi bản ghi đ−ợc phân tách nhau bởi dấu ENTER (nhấn phím ENTER). Do đó, câu lệnh: READ*, A, B, C sẽ đ−ợc thỏa mãn với một bản ghi chứa 3 giá trị: 3 4 5 Trong khi các câu lệnh: READ*, A READ*, B READ*, C đòi hỏi phải đ−a vào 3 bản ghi, mỗi bản ghi chứa 1 giá trị (tức là trong khi nhập dữ liệu sẽ dùng dấu ENTER xuống dòng sau khi gõ vào một giá trị): 3 4 5 − Khi gặp một lệnh READ mới, những dữ liệu ch−a đ−ợc đọc trên bản ghi hiện thời (nếu còn) sẽ bị bỏ qua, và một bản ghi mới khác sẽ đ−ợc tìm đến để nhận dữ liệu. − Nếu lệnh READ đòi hỏi nhiều dữ liệu hơn số dữ liệu chứa trên bản ghi hiện thời nó cũng sẽ tìm đến bản ghi mới tiếp theo để nhận tiếp dữ liệu. Do đó, nếu dữ liệu không đủ đáp ứng cho lệnh READ thì ch−ơng trình sẽ bị kết thúc với thông báo lỗi. 34
  34. Ví dụ, các câu lệnh READ*, A READ*, B, C READ*, D với các bản ghi dữ liệu đ−a vào là (ở đây mỗi dòng đ−ợc xem là một bản ghi): 1 2 3 4 7 8 9 10 sẽ có hiệu quả giống nh− các lệnh gán sau: A = 1 B = 4 C = 7 D = 9 Tức là các giá trị 2, 3 trên bản ghi thứ nhất, 8 trên bản ghi thứ ba và 10 trên bản ghi thứ t−, bị bỏ qua. 1.11.2 Đọc dữ liệu từ file TEXT Trên thực tế th−ờng xảy ra tình huống, ta đang muốn kiểm tra, chỉnh sửa ch−ơng trình, trong đó mỗi lần chạy, ch−ơng trình cần phải đọc vào nhiều số liệu. Chẳng hạn, khi viết một ch−ơng trình tính trung bình của 10 số, chắc chắn ta sẽ cảm thấy rất khó chịu nếu cứ phải nhập vào 10 số từ bàn phím (bằng lệnh READ*) mỗi khi thử lại ch−ơng trình. Đó là ch−a nói đến những ch−ơng trình đòi hỏi nhiều dữ liệu hơn, nh− tính điểm trung bình chung học tập cho một lớp sinh viên khoảng 50 ng−ời, 100 ng−ời, Để tránh phiền phức trong những tr−ờng hợp nh− vậy, Fortran cung cấp một ph−ơng thức vào dữ liệu khá đơn giản nh−ng rất hữu ích, là sử dụng file số liệu. ý t−ởng là ở chỗ, tr−ớc khi chạy ch−ơng trình, ta cần phải chuẩn bị số liệu và l−u chúng vào một file riêng biệt trên đĩa. File số liệu này có thể đ−ợc tạo ra bằng một trình soạn thảo bất kỳ và đ−ợc ghi lại d−ới dạng file TEXT (ASCII file) với một tên file nào đó, chẳng hạn SOLIEU.TXT. Khi chạy ch−ơng trình, máy sẽ tìm đến file này và nhận số liệu từ đó. Muốn vậy, thay cho câu lệnh READ*, ta sử dụng hai câu lệnh mới có chức năng tham chiếu đến file và đọc dữ liệu từ file. Để tiện trình bày, ta xét ví dụ đơn giản sau. Giả sử ta có file số liệu với tên là SOLIEU.TXT mà nội dung của nó chỉ gồm 3 số ở dòng đầu tiên của file: 3 4 5 Bây giờ ta hãy gõ ch−ơng trình sau đây vào máy và chạy thử: PROGRAM ThuFile REAL A, B, C OPEN(1, FILE = 'SOLIEU.TXT') ! Mở file 35
  35. READ(1, *) A, B, C ! Đọc số liệu từ file PRINT*, A, B, C END Câu lệnh OPEN kết nối số 1 với file SOLIEU.TXT trên đĩa. Số 1 này đ−ợc gọi là UNIT, mang hàm nghĩa chỉ thị số hiệu file (hay kênh vào/ra). Câu lệnh READ ở đây (khác với lệnh READ*) định h−ớng cho ch−ơng trình tìm và đọc số liệu trong file đ−ợc kết nối với UNIT 1. Thông th−ờng số UNIT nhận giá trị trong khoảng 1−9999. 1.11.3 Lệnh kết xuất dữ liệu Lệnh PRINT* là câu lệnh rất thuận tiện cho việc kết xuất thông tin khi l−ợng dữ liệu không lớn. Thông th−ờng nó đ−ợc sử dụng trong quá trình xây dựng, phát triển ch−ơng trình, hoặc đ−a ra những kết quả tính toán trung gian để theo dõi tiến trình làm việc của ch−ơng trình. Dạng tổng quát của nó nh− sau: PRINT*, list Trong đó list có thể là danh sách hằng, biến, biểu thức và xâu ký tự, đ−ợc viết cách nhau bởi dấu phẩy (,). Xâu ký tự phải đ−ợc đặt trong cặp dấu nháy đơn (‘ ’) hoặc dấu nháy kép (“ ”). Nếu list là danh sách rỗng thì lệnh này có dạng đơn giản là PRINT* và có ý nghĩa chèn thêm một dòng trống. Ví dụ: PRINT* PRINT*, "Can bac hai cua ", 2, ' la', SQRT(2.0) Sau đây là một số qui tắc chung của lệnh PRINT. − Mỗi câu lệnh PRINT* tạo ra một bản ghi mới. Nếu nội dung bản ghi quá dài nó sẽ đ−ợc “cuộn” xuống các dòng tiếp theo. − Đối với số thực, tùy theo độ lớn giá trị của số đ−ợc in mà chúng có thể đ−ợc biểu diễn d−ới dạng dấu phẩy tĩnh hoặc dấu phẩy động. Nếu muốn in ở dạng cầu kỳ, có qui cách, ta có thể sử dụng lệnh định dạng FORMAT. Ví dụ, để in số 123.4567 d−ới dạng dấu phẩy tĩnh trên 8 cột, với 2 chữ số sau dấu chấm thập phân, ta có thể viết: X = 123.4567 PRINT 10, X 10 FORMAT( F8.2 ) Lệnh định dạng FORMAT cho phép bố trí khuôn mẫu in theo qui cách đ−ợc chỉ ra ở phần trong dấu ngoặc đơn. Trong ví dụ trên, nếu muốn in giá trị của biến X kèm theo những chú thích hợp lý ta có thể đ−a thêm vào các hằng ký tự. Chẳng hạn, thay cho câu lệnh trên đây ta có thể viết: 10 FORMAT( “Gia tri bien X = ”, F8.2 ) Hằng ký tự phải đ−ợc đặt trong cặp dấu nháy đơn, hoặc dấu nháy kép. Ta sẽ đề cập chi tiết đến câu lệnh này trong các mục sau. 36
  36. Lệnh PRINT* cũng có thể đ−ợc dùng để in một thông báo (hằng ký tự) dài quá một dòng bằng cách sử dụng ký tự nối dòng. Ví dụ: PRINT*, 'Day la cau thong bao duoc & &viet bang lenh PRINT co noi dong' 1.11.4 Kết xuất ra máy in Nếu muốn kết xuất ra máy in, ta chỉ cần đặt tham số FILE=’PRN’ trong câu lệnh OPEN và kết hợp với việc sử dụng lệnh WRITE. Ví dụ: OPEN (2, FILE = 'prn' ) WRITE(2, *) 'In ra may in' PRINT*, 'In ra man hinh' Chú ý rằng lệnh WRITE trong tr−ờng hợp này phải gắn kết với số hiệu file UNIT trong lệnh OPEN. Lệnh này tổng quát hơn lệnh PRINT. Ta sẽ làm quen với câu lệnh này ở những nội dung sau. 1.12 Sử dụng hàm trong fortran Trên đây ta đã gặp tr−ờng hợp tính căn bậc hai của một số d−ơng bằng hàm th− viện SQRT của Fortran. Đó chỉ là một trong rất nhiều hàm có sẵn do trình biên dịch cung cấp. Hệ thống các hàm này (và cả những hàm do ng−ời dùng xây dựng bổ sung thêm) lập thành một th− viện các hàm trong (hay còn gọi là hàm th− viện), cho phép ta sử dụng chúng nh− những “hộp đen” mà không cần biết chúng đ−ợc xây dựng nh− thế nào. Mỗi một hàm nh− vậy thực hiện một chức năng tính toán khác nhau (nh− lấy căn bậc hai, tính cosine, ) và cho một giá trị kết quả. Các hàm này đ−ợc tham chiếu trực tiếp trong các biểu thức. Khi tính biểu thức, hàm sẽ đ−ợc thực hiện theo trình tự thuật toán đã xây dựng và giá trị tính đ−ợc của hàm sẽ thay thế vị trí tham chiếu đến hàm. Ví dụ, xét đoạn ch−ơng trình sau: REAL X, Y X = 16.0 Y = 5.6 + SQRT(X) PRINT*, X, Y END Trong ch−ơng trình này, để tính giá trị của Y, cần phải tính SQRT(X). Vì X = 16.0 nên hàm SQRT(X) = SQRT(16.0) sẽ cho kết quả là 16.0 = 4.0. Do đó, Y = 5.6 + 4.0 = 9.6. Mặc dù vậy ta hoàn toàn không biết cách tính căn bậc hai mà hàm SQRT thực hiện nh− thế nào. Và ta sử dụng hàm SQRT để tính căn bậc hai của một số X nào đó nh− là một sự thừa nhận tính đúng đắn, chính xác của nó. Fortran cung cấp cho ta một th− viện các hàm khá phong phú. Để tiện sử dụng khi trình bày ở các phần sau, trong bảng 1.6 nêu ra một số hàm thông dụng nhất. 37
  37. Khi sử dụng các hàm th− viện ta cần đặc biệt chú ý đến tính năng của chúng. Ví dụ, các hàm INT và NINT đ−ợc sử dụng để đổi số thực thành số nguyên, nh−ng hàm INT sẽ cắt bỏ phần thập phân trong khi hàm NINT làm tròn số thực đến số nguyên gần nhất: INT(5.3) là 5 NINT(5.3) là 5 INT(5.8) là 5 NINT(5.8) là 6 INT(−5.3) là −5 NINT(−5.3) là −5 INT(−5.8) là −5 NINT(−5.8) là −6 Bảng 1.6 Một số hàm th− viện th−ờng dùng của Fortran Tên hàm Kiểu dữ liệu của Kiểu dữ liệu và lời gọi hàm Chức năng của hàm đối số của kết quả INT (X) Chuyển số X thành số nguyên sau REAL INTEGER khi chặt cụt phần thập phân NINT (X) Làm tròn số X đến số nguyên gần REAL INTEGER nhất REAL (X) Chuyển số nguyên X thành số thực INTEGER REAL ABS (X) Tìm giá trị tuyệt đối của X REAL REAL IABS (X) Tìm giá trị tuyệt đối của X INTEGER INTEGER SQRT (X) Tính căn bậc hai của X REAL REAL EXP (X) Tính e X REAL REAL ALOG (X) Tính lnX (logarit tự nhiên) REAL REAL ALOG10 (X) Tính lgX (logarit thập phân) REAL REAL SIN (X) Tĩnh Sine của X REAL REAL COS (X) Tính Cosine của X REAL REAL TAN (X) Tính Tang của X REAL REAL MOD (X,Y) Tính phần d− của phép chia hai số INTEGER INTEGER nguyên X/Y MAX0(X1, ,XN) Tìm giá trị lớn nhất của d∙y số INTEGER INTEGER X1, ,XN MIN0(X1, ,XN) Tìm giá trị nhỏ nhất của d∙y số INTEGER INTEGER X1, ,XN AMAX1(X1, ,XN) Tìm giá trị lớn nhất của d∙y số REAL REAL X1, ,XN AMIN1(X1, ,XN) Tìm giá trị nhỏ nhất của d∙y số REAL REAL X1, ,XN Hàm REAL đ−ợc sử dụng để đổi một số nguyên thành một số thực. Nếu các biến TONG và N là những biến nguyên còn T_BINH là biến thực, khi đó hai câu lệnh sau đây có thể cho kết quả hoàn toàn khác nhau: T_BINH = TONG / N và T_BINH = REAL(TONG)/REAL(N) Những hàm trên đây chỉ đòi hỏi có một đối số, nh−ng nh− đã thấy trong bảng 1.6, có thể có những hàm đòi hỏi hai đối số hoặc nhiều hơn. Ví dụ, hàm MOD đòi hỏi hai đối số, trong khi các hàm MAX0, MIN0, AMAX1, AMIN1 lại có thể có số l−ợng đối số lớn hơn hoặc bằng hai. Ví dụ 1.5. Giả sử A, B, C là ba đỉnh của một tam giác. Ký hiệu AB, AC, BC là các cạnh của tam giác, ALFA là góc kẹp giữa hai cạnh AB và AC. Cho biết độ dài của các cạnh AB, AC và số đo bằng độ của góc ALFA, có thể tính độ dài của cạnh BC theo công thức: 38
  38. BC2 = AB2 + AC2 − 2.AB.AC.Cos(Alfa) Viết ch−ơng trình nhập vào độ dài các cạnh AB, AC và góc ALFA (độ) rồi tính độ dài của cạnh BC. Ta có ch−ơng trình sau: REAL AB, AC, BC, ALFA REAL PI PI = 4.0*ATAN (1.0) PRINT*,’Cho do dai cac canh AB, AC: ‘ READ*, AB,AC PRINT*,’Cho so do goc (do) giua AB va AC: ’ READ*, ALFA BC = SQRT (AB 2 + AC 2 − 2*COS(ALFA*PI/180.0) ) PRINT*,’Do dai canh BC = ‘, BC END Trong ch−ơng trình trên, hàm ATAN để tính Arctang. Vì Tang của góc π /4 bằng 1 nên Arctang của 1 bằng π /4. Một trong những hàm rất quan trọng đ−ợc sử dụng trong nhiều lĩnh vực là hàm ex, trong đó e là một hằng số, có giá trị bằng 2.718282 khi lấy tròn số đến sáu chữ số thập phân. Ví dụ, hàm mật độ xác suất của biến ngẫu nhiên tuân theo luật phân bố chuẩn chuẩn hóa có dạng: 1 1 − x 2 f (x) = e 2 2π Biểu thức −ớc l−ợng giá trị của hàm này viết bằng ngôn ngữ Fortran có thể có dạng: F = 1.0/SQRT(2.0*PI)*EXP(0.5*X*X) 39
  39. Bài tập ch−ơng 1 1.1 Hãy cho biết trong các tên biến d−ới đây những tên nào viết sai theo qui −ớc của Fortran, tại sao: (a) A2 (b) A.2 (c) 2A (d) 'A'ONE (e) AONE (f) X_1 (g) MiXedUp (h) Pay Day (i) U.S.S.R. (j) Pay_Day (k) min*2 (l) PRINT 1.2 Hãy xác định xem trong những hằng sau đây hằng nào viết đúng, hằng nào viết sai theo qui −ớc của Fortran, tại sao: (a) 9,87 (b) .0 (c) 25.82 (d) –356231 (e) 3.57*E2 (f) 3.57E2.1 (g) 3.57E+2 (h) 3,57E–2 1.3 Hãy viết các biểu thức sau đây d−ới ngôn ngữ Fortran: (a) ax 2 + bx + c =0; (b) ax 2 + bx + c >0; (c) ax 2 + bx + c <0; (d) ax 2 + bx + c ≠0; (e) ax 2 + bx + c ≥0; (f) ax 2 + bx + c ≤0 1.4 Tìm chỗ sai trong đoạn ch−ơng trình sau: INTEGER*1 A, N INTEGER*2 B, M REAL X LOGICAL L A = 12.0 N = 150 B = −54.4 M = 33456 L = .TRUE. 1.5 Hãy gõ đoạn ch−ơng trình sau vào máy, chạy tính thử và khảo sát những thông báo lỗi (ERROR) khi dịch ch−ơng trình rồi sửa lại cho đúng: PROGRAM Dread_ful REAL: A, B, X X:= 5 Y = 6,67 B = X \ Y PRINT* 'The answer is", B END. 1.6 Lập ch−ơng trình nhập vào hai số thực A và B, rồi tính tổng, hiệu, tích th−ơng của chúng. In kết quả lên màn hình với những dòng chú thích phù hợp. Hãy khảo sát điều gì sẽ xảy ra khi thực hiện phép chia cho số 0. 1.7 Hãy lập ch−ơng trình nhập vào hai số nguyên M và N, rồi tính tổng, hiệu, tích th−ơng của chúng. In kết quả lên màn hình với những dòng chú thích phù hợp. Chú ý theo dõi và cho biết tại sao với những cặp số M, N khác nhau lại có thể cho kết quả nh− nhau khi thực hiện phép chia hai số. 1.8 Cho tr−ớc giá trị của ba biến thực A=2, B=3, C=5 và hai biến nguyên I=2, J=3. Hãy cho biết giá trị của các biểu thức sau nếu chúng đ−ợc tính bằng ch−ơng trình Fortran: 40
  40. 1) A*B + C; 2) A*(B + C); 3) B/C*A; 4) B/(C * A); 5) A/I/ J; 6) I/J/A; 7) A*B I/A J * 2; 8) C + (B / A) 3 / B * 2.; 9) A B I; 10) −B A C; J / (I / J). R / Cp 1.9 Nhiệt độ thế vị θ đ−ợc xác định bởi công thức θ = T⎛1000 ⎞ , trong đó T (oC) ⎜ ⎟ ⎝ p ⎠ và p (mb) là nhiệt độ và áp suất ban đầu của phần tử khí, R/Cp≈0.288. Hãy lập ch−ơng trình nhập vào giá trị nhiệt độ và áp suất ban đầu của một phần tử khí và tính nhiệt độ thế vị của nó. 1.10 Giả sử có các khai báo sau: REAL P1, X, Y INTEGER MAXI, A, B, I PARAMETER (P1 = 3.14159, MAXI = 1000) Hãy tính giá trị của các câu lệnh hợp lệ d−ới đây, đồng thời chỉ ra những câu lệnh không hợp lệ, tại sao. Cho A=3, B=4 và X=−1.0 I = A * B I = (990 − MAXI) / A I = A*Y x = pi*y I = A/B X = A / B X = A * (A/ B) I = B / 0 I = A * (990 — MAXI) I = (MAXI — 990) / A X = A / Y I = PI*A x = pi/y I = B/A I = (MAXI — 990) * A L = A * 0 I = A * MAXI — 990) 1.11 Cho A, B, C và X là tên của bốn biến thực (REAL), I, J và K là tên của ba biến nguyên (INTEGER). Hãy sửa các câu lệnh d−ới đây cho phù hợp với qui tắc biểu diễn biểu thức số học bằng ngôn ngữ Fortran. 1) X = 4.0 A* C 2) A = AC 3) I = 2X—J 4) K = 3(1 ± J) 5) X = 5A/BC 6) I = 5J3 1.12 Viết ch−ơng trình xác định số lần đập của quả tim trong cả cuộc đời một con ng−ời. Ch−ơng trình cho phép tính với nhịp đập bất kỳ của quả tim (ví dụ 72 lần/phút) và với tuổi thọ bất kỳ của một ng−ời (ví dụ 75 tuổi). Lấy số ngày trong một năm bằng 365.25 ngày. 41
  41. 1.13 Thời gian bay (t − giây) và độ cao (h − mét) đạt đ−ợc của một viên đạn pháo đ−ợc xác định theo các công thức: S t = vcosθ gt 2 h = vt − 2 trong đó S (m) là khoảng cách từ nơi bắn đến mục tiêu; v (m/s) là vận tốc ban đầu của viên đạn; θ (radian) là góc nâng của nòng pháo; và g (m/s2) là gia tốc trọng tr−ờng. Cho g = 9.8 m/s2. Hãy viết ch−ơng trình nhập vào khoảng cách đến mục tiêu, góc nâng của nòng pháo và vận tốc ban đầu của viên đạn và tính thời gian bay và độ cao đạt đ−ợc của viên đạn. 1.14 Biệt thự của một gia đình là một hình chữ nhật có các kích th−ớc là XN và YN. Biệt thự đ−ợc xây dựng trên một khu đất cũng là hình chữ nhật có các cạnh song song với biệt thự và có các kích th−ớc XD và YD. Ngoài biệt thự, trong khu đất còn có một v−ờn hoa hình tròn bán kính RH. Khoảng trống còn lại của khu đất là cỏ. Hãy viết ch−ơng trình nhập vào những giá trị hợp lệ của các kích th−ớc của biệt thự, của khu đất và của v−ờn hoa và tính xem nếu một ng−ời cắt cỏ cắt đ−ợc 2 m2/s thì phải mất bao nhiêu thời gian để cắt hết cỏ trong khu đất đó. 1.15 Viết ch−ơng trình nhập vào các tử số và mẫu số của hai phân số rồi tính tổng, hiệu, tích, th−ơng của chúng. In kết quả d−ới dạng phân số và giá trị phần trăm của phân số kết quả. 1.16 Viết ch−ơng trình đọc vào giờ, phút, giây và đổi ra giờ biểu diễn d−ới dạng số thập phân (ví dụ XX giờ, YY phút, ZZ giây sẽ đ−ợc đổi thành HH.TTTT giờ) 1.17 Viết ch−ơng trình nhập giá trị ba điện trở của một mạch điện mắc song song và tính điện trở t−ơng đ−ơng của mạch theo công thức: 1 1 1 1 = + + Rtd R1 R2 R3 1.18 Bộ 3 số nguyên d−ơng m, n, p thỏa mãn điều kiện m2 + n2 = p2 đ−ợc gọi là bộ ba số Pitago (ví dụ, ba số 3, 4, 5 là một bộ số Pitago), vì ba số này thỏa mãn điều kiện là ba cạnh của một tam giác vuông, trong đó m và n là hai cạnh góc vuông, p là cạnh huyền. Cho hai số nguyên d−ơng x và y, với x > y, khi đó có thể tạo một bộ số Pitago theo công thức sau: m = x2 − y2 n = 2xy p = x2 + y2 Viết ch−ơng trình nhập vào hai số nguyên d−ơng và thành lập bộ số Pitago theo các công thức trên. 42
  42. 1.18 Tốc độ suy giảm nhiệt độ theo ph−ơng thẳng đứng (gradient thẳng đứng của nhiệt độ) trong lớp khí quyển d−ới cùng có thể lấy gần đúng bằng 0.6oC/100m. Viết ch−ơng trình xác định nhiệt độ khí quyển ở độ cao h (m) nào đó nếu biết rằng nhiệt độ ở mực n−ớc biển (h=0) là T (OC). 1.19 Hãy biểu thị d−ới dạng các câu lệnh của Fortran những nội dung sau: (a) Thêm 1 vào giá trị của biến I rồi l−u kết quả vào ô nhớ của biến I. (b) Lấy luỹ thừa 3 của I rồi cộng với J và l−u kết quả vào ô nhớ I. (c) Chia tổng của A và B cho tích của C và D rồi l−u vào ô nhớ của biến X. 1.20 Viết ch−ơng trình tính giá trị của biểu thức sau: 3 0.19238 A= ()x + sin b − c (cos3x + 0.20345), 15.172 + 5x trong đó: x nhập từ bàn phím; b=2x−31.769; c=lg(x4+5x)+ln(x2+5b) 1.21 Viết ch−ơng trình nhập vào toạ độ ba điểm A(x1,y1), B(x2,y2), C(x3,y3) rồi tính tích vô h−ớng của các vectơ AB, AC. 1.22 Sử dụng trình soạn thảo Fortran (hoặc một trình soạn thảo bất kỳ) tạo một file TEXT có tên là SOLIEU.TXT với nội dung của file nh− sau: 23 12.5 65.2 21 67 89 34 56 76 32 45.6 54.6 67.8 21.3 Sau đó viết ch−ơng trình đọc file số liệu theo các yêu cầu: Đọc các giá trị thứ nhất và thứ ba ở dòng 1 và gán cho các biến A, B; Đọc các giá trị thứ hai, thứ ba và thứ t− ở dòng 2 và gán cho các biến C, D, E; Đọc hai giá trị ở dòng 3 và giá trị thứ nhất ở dòng 4 và gán cho các biến X, Y, Z. In kết quả nhận đ−ợc của các biến A, B, C, D, E, X, Y, Z lên màn hình và so sánh với nội dung file số liệu. 43
  43. Ch−ơng 2. Các câu lệnh cơ bản của Fortran Trong ch−ơng tr−ớc chúng ta đã làm quen với một số câu lệnh của Fortran, nh− lệnh gán, các lệnh vào ra đơn giản với READ* và PRINT*, lệnh mở file OPEN để nhận dữ liệu từ file TEXT hoặc kết xuất thông tin ra máy in, lệnh định dạng FORMAT, Với những câu lệnh đó, ta đã có thể viết đ−ợc một số ch−ơng trình đơn giản. Ch−ơng này sẽ nghiên cứu những câu lệnh phức tạp hơn. 2.1 Lệnh chu trình (DO Loops) Khi viết ch−ơng trình, ta có thể bắt gặp tình huống, một hoặc nhiều câu lệnh nào đó phải thực hiện lặp lại nhiều lần giống nhau. Chẳng hạn, muốn in 10 số nguyên liên tiếp, mỗi lần in một số, ta phải dùng đến 10 câu lệnh in ra. Điều đó làm cho ta nhiều lúc cảm thấy bất tiện. Tuy nhiên, thay cho cách làm trên đây, Fortran hỗ trợ một cấu trúc câu lệnh khá đơn giản nh−ng rất hiệu quả. Đó là câu lệnh chu trình, hay chu trình lặp xác định. Cú pháp câu lệnh có thể có các dạng sau. Dạng 1: DO m bdk = TriDau, TriCuoi [, Buoc] Các_câu_lệnh m Câu_lệnh_kết_thúc Dạng 2: DO m bdk = TriDau, TriCuoi [, Buoc] Các_câu_lệnh m CONTINUE Dạng 3: DO bdk = TriDau, TriCuoi [, Buoc] Các_câu_lệnh END DO Trong đó: bdk, TriDau, TriCuoi, Buoc phải có cùng kiểu dữ liệu; m là nhãn của câu lệnh kết thúc chu trình, trong tr−ờng hợp không thể sử dụng câu lệnh kết thúc nh− vậy, có thể thay thế nó bằng câu lệnh m CONTINUE nh− ở dạng 2. Nếu TriDau TriCuoi thì Buoc phải là một số âm. Nếu Buoc=1 thì có thể bỏ qua Buoc. Cấu trúc dạng 1 và dạng 2 là của Fortran 77 và các phiên bản tr−ớc đó, nh−ng chúng vẫn t−ơng thích với Fortran 90. Mặc dù vậy, do một số đặc điểm mở rộng của câu lệnh chu trình trong Fortran 90 (mà ta sẽ đề cập ở các phần sau), hiện nay ng−ời ta ít sử dụng các cấu trúc đó. Tập Các_câu_lệnh nằm giữa DO và m Câu_lệnh_kết_thúc hoặc 44
  44. m CONTINUE hoặc ENDDO là những câu lệnh đ−ợc thực hiện lặp đi lặp lại. Số lần lặp lại đ−ợc xác định bởi: Số lần lặp = MAX { (TriCuoi − TriDau + Buoc) / Buoc, 0 } Tác động của lệnh chu trình đ−ợc mô tả trên hình 2.1. Có thể tóm tắt tác động này qua các b−ớc sau. 1) Bắt đầu chu trình bdk đ−ợc gán giá trị bằng TriDau. 2) Sau đó ch−ơng trình sẽ thực hiện biểu thức so sánh bdk =TriCuoi: a) Nếu biểu thức cho kết quả .TRUE. (đúng): a.1) Tiếp tục thực hiện Các_câu_lệnh, kể cả Câu_lệnh_kết_thúc, nằm trong chu trình rồi tăng hoặc giảm bdk một l−ợng bằng trị tuyệt đối của Buoc a.2) Quay về thực hiện b−ớc 2) b) Nếu biểu thức cho kết quả .FALSE. (sai) thì kết thúc chu trình a) Tr−ờng hợp TriDau =TriCuoi Hình 2.1 Sơ đồ khối mô tả tác động của lệnh chu trình DO Ta nhận thấy, tác động của chu trình là thực hiện lặp đi lặp lại Các_câu_lệnh, kể cả Câu_lệnh_kết_thúc. Mỗi lần nh− vậy giá trị của bdk sẽ thay đổi phù hợp với Buoc, còn TriDau và TriCuoi đ−ợc giữ nguyên cho đến khi vòng lặp kết thúc. Do đó, trong phạm vi vòng lặp, tức là trong Các_câu_lệnh và Câu_lệnh_kết_thúc, tuyệt đối không đ−ợc xuất hiện những câu lệnh làm thay đổi giá trị của bdk, TriDau và TriCuoi, nếu không sẽ dẫn đến lỗi không l−ờng tr−ớc đ−ợc. 45
  45. Ví dụ 2.1. Ch−ơng trình sau đây sẽ tính tổng các số nguyên liên tiếp từ N1 đến N2, trong đó N1 và N2 đ−ợc nhập vào từ bàn phím. INTEGER N1, N2, TONG, I PRINT '(A\)', ' CHO GIA TRI N1, N2 (N1 =N2, nếu không sẽ nhận đ−ợc kết quả bất ngờ (!?), vì khi đó số lần lặp bằng 0. Các câu lệnh DO I = N1,N2,1 TONG = TONG + I PRINT*, I ENDDO cũng có thể đ−ợc thay thế bởi các câu lệnh sau đây DO 100 I = N1, N2 TONG = TONG + I 100 PRINT*, I Trong tr−ờng hợp này, câu lệnh 100 PRINT*, I là câu lệnh kết thúc chu trình, và vì Buoc có giá trị bằng 1 nên ta đã bỏ qua nó. Ta cũng có thể dùng câu lệnh CONTINUE để kết thúc chu trình nh− sau: DO 100 I = N1, N2 TONG = TONG + I PRINT*, I 46
  46. 100 CONTINUE Lệnh CONTINUE ở đây có thể xem là “thừa”, tuy vậy trong nhiều tr−ờng hợp, để an toàn và rõ ràng hơn, ta có thể sử dụng những câu lệnh “thừa” kiểu này. Ví dụ 2.2. Ch−ơng trình tính căn bậc hai của số a theo ph−ơng pháp Newton có thể đ−ợc mô tả nh− sau: 1) Nhập vào số a 2) Khởi tạo x bằng 1 (gán giá trị cho x bằng 1) 3) Lặp lại 6 lần các b−ớc sau đây: a) Thay x bởi (x + a/x)/2 b) In giá trị của x 4) Kết thúc ch−ơng trình Mã nguồn ch−ơng trình nh− sau: PROGRAM Newton ! Tinh can bac hai bang pp Newton REAL A ! Số sẽ lấy căn bậc hai INTEGER I ! Biến đếm phép lặp REAL X ! Giá trị gần đúng của căn bậc hai của a WRITE( *,*) ' Cho so se lay can bac hai: ' READ*, A PRINT* X = 1 ! Khởi tạo giá trị ban đầu của x (??) DO I = 1, 6 X = (X + A / X) / 2 PRINT*, X ENDDO PRINT* PRINT*, 'Can bac 2 cua ‘,a,’ tinh theo F90 la:',& SQRT(A) END Khi chạy ch−ơng trình, trên màn hình sẽ xuất hiện 6 lần giá trị của X. Giá trị ở dòng thứ 6 đ−ợc xem là gần đúng của căn bậc hai của a tính bằng ph−ơng pháp lặp Newton, còn giá trị in ở dòng cuối cùng là căn bậc hai của a tính bằng hàm th− viện SQRT của Fortran. Giữa chúng có thể có sự khác nhau; khi a càng lớn thì sự khác nhau đó càng nhiều. Trong tr−ờng hợp này ta có thể tăng số lần lặp lại bằng cách thay số 6 ở dòng lệnh DO I = 1, 6 bằng một số lớn hơn và chạy lại ch−ơng trình. Việc so sánh kết quả nhận đ−ợc sau mỗi lần thay đổi dòng lệnh này sẽ giúp ta hiểu rõ hơn ý nghĩa của vòng lặp. Chú ý: Nói chung Fortran cho phép các biến bdk, TriDau, TriCuoi, Buoc nhận kiểu dữ liệu là số nguyên hoặc số thực. Tuy nhiên ta không nên dùng kiểu dữ liệu thực, do số thực đ−ợc biểu diễn ở dạng gần đúng, có thể gây nên những sai số không l−ờng tr−ớc đ−ợc. 47
  47. 2.2 Lệnh rẽ nhánh với IF Cấu trúc rẽ nhánh là kiểu cấu trúc rất phổ biến đối với các ngôn ngữ lập trình. Trong Fortran, cấu trúc rẽ nhánh đ−ợc cho khá đa dạng. Sau đây ta sẽ lần l−ợt xét từng tr−ờng hợp. 2.2.1 Dạng 1 IF (BThuc_Logic) Câu_lệnh Trong đó Câu_lệnh là một câu lệnh thực hiện nào đó và không thể là một trong các câu lệnh có cấu trúc khác, nh− IF, DO, BThuc_Logic là điều kiện rẽ nhánh. Tác động của câu lệnh IF là, nếu BThuc_Logic nhận giá trị .TRUE. (đúng) thì ch−ơng trình sẽ thực hiện Câu_lệnh ngay sau đó, ng−ợc lại, nếu BThuc_Logic nhận giá trị .FALSE. (sai) thì Câu_lệnh sẽ bị bỏ qua và ch−ơng trình tiếp tục với những câu lệnh khác sau IF. Sơ đồ khối mô tả tác động này đ−ợc cho trên hình 2.2. Ví dụ 2.3. Hãy đọc vào một số và cho biết đó là số d−ơng, số âm hay số 0. Ch−ơng trình có thể đ−ợc viết nh− sau. ! Vi du ve lenh re nhanh Hình 2.2 Cấu trúc IF dạng 1 REAL X PRINT '(A\)',' CHO MOT SO:' READ*, X IF (X > 0) PRINT *, ' DAY LA SO DUONG' IF (X < 0) PRINT *, ' DAY LA SO AM' IF (X == 0) PRINT *, ' DAY LA SO 0' END Nh− đã thấy, đối với cấu trúc này, khi BThuc_Logic nhận giá trị .TRUE. (đúng) thì chỉ có một câu lệnh sau đó đ−ợc thực hiện. 2.2.2 Dạng 2 IF (BThuc_Logic) THEN Các_câu_lệnh END IF Về nguyên tắc, tác động của câu lệnh này hoàn toàn giống với cấu trúc dạng 1 trên đây. Sự khác nhau giữa chúng chỉ là ở chỗ, trong cấu trúc dạng 1, khi điều kiện đ−ợc thỏa mãn (BThuc_Logic nhận giá trị .TRUE.) thì chỉ có một câu lệnh sau IF đ−ợc thực hiện, còn trong tr−ờng hợp này, nếu BThuc_Logic nhận giá trị .TRUE. thì có thể có nhiều câu lệnh nằm giữa IF THEN và END IF sẽ đ−ợc thực hiện (Các_câu_lệnh hàm nghĩa là có thể có nhiều câu lệnh). Ví dụ 2.4. Viết ch−ơng trình nhập vào hai số thực, nếu chúng đồng thời khác 0 thì tính tổng, hiệu, tích, th−ơng của chúng. 48
  48. REAL X, Y, TONG, HIEU, TICH, THUONG PRINT*, ‘ CHO 2 SO THUC:’ READ*, X, Y ! Đọc các số X, Y từ bàn phím IF (X /= 0.AND.Y /= 0) THEN ! X và Y đồng thời khác 0 TONG = X + Y HIEU = X − Y TICH = X * Y THUONG = X / Y PRINT*,’ TONG CUA ’,X,’ VA ‘,Y,’ LA:’,TONG PRINT*,’ HIEU CUA ’,X,’ VA ‘,Y,’ LA:’,HIEU PRINT*,’ TICH CUA ’,X,’ VA ‘,Y,’ LA:’,TICH PRINT*,’ THUONG CUA ’,X,’ VA ‘,Y,’ LA:’,& THUONG END IF IF (X == 0.OR.Y == 0) THEN ! Một trong hai số = 0 PRINT*,’ MOT TRONG HAI SO VUA NHAP = 0’ END IF END 2.2.3 Dạng 3 IF (BThuc_Logic) THEN Các_câu_lệnh_1 ELSE Các_câu_lệnh_2 END IF Khác với hai cấu trúc trên, trong cấu trúc này việc thực hiện ch−ơng trình có thể rẽ về một trong hai “nhánh”: Nếu BThuc_Logic nhận giá trị .TRUE. thì ch−ơng trình sẽ thực hiện Các_câu_lệnh_1, ng−ợc lại, ch−ơng trình sẽ thực hiện Các_câu_lệnh_2. Sơ đồ khối mô tả tác động của cấu trúc này đ−ợc cho trên hình 2.3 Ví dụ 2.5. Viết ch−ơng trình nhập vào từ bàn phím ba số thực. Nếu ba số đó thỏa mãn điều kiện là ba cạnh của một tam giác thì tính diện tích của tam giác. Ng−ợc lại thì đ−a ra thông báo “BA SO NAY KHONG PHAI LA 3 CANH CUA TAM GIAC”. PROGRAM TAM_GIAC REAL A,B,C ! Ba số sẽ nhập vào Hình 2.3 Cấu trúc IF dạng 3 REAL P,S ! Nửa chu vi và Diện tích LOGICAL L1 LOGICAL L2 PRINT*, ' CHO 3 SO THUC:' READ*, A,B,C L1 = A>0.AND.B>0.AND.C>0 ! Ba số cùng D−ơng L2 = A+B>C.AND.B+C>A.AND.C+A>B ! Ba số phải thỏa mãn bất đẳng thức tam giác 49
  49. IF (L1.AND.L2) THEN ! Thỏa mãn điều kiện Tam giác P = (A+B+C)/2 S = SQRT(P*(P-A)*(P-B)*(P-C)) PRINT*,' DIEN TICH TAM GIAC = ',S ELSE ! Không thỏa mãn điều kiện Tam giác PRINT*,"BA SO NAY KHONG PHAI LA 3 CANH & &CUA TAM GIAC" END IF END Trong ch−ơng trình này ta đã sử dụng hai biến lôgic L1, L2 để xác định ba số nhập vào có thỏa mãn điều kiện là ba cạnh của một tam giác hay không. Cách dùng các biến kiểu này rất có ích, vì trong những tr−ờng hợp phức tạp nó sẽ giúp ta gỡ rối ch−ơng trình đ−ợc nhanh chóng và chính xác. Hơn nữa, khi viết nh− vậy ch−ơng trình trông sáng sủa hơn. 2.2.4 Dạng 4 IF (BThuc_Logic_1) THEN Các_câu_lệnh_1 ELSE IF (BThuc_Logic_2) THEN Các_câu_lệnh_2 ELSE IF (BThuc_Logic_3) THEN Các_câu_lệnh_3 ELSE Các_câu_lệnh_n END IF Cấu trúc này đ−ợc gọi là cấu trúc khối IF (Block IF). Tác động của cấu trúc này đ−ợc mô tả trên hình 2.4. Tr−ớc hết, ch−ơng trình sẽ kiểm tra BThuc_Logic_1. Nếu BThuc_Logic_1 nhận giá trị .TRUE. thì Các_câu_lệnh_1 sẽ đ−ợc thực hiện; nếu BThuc_Logic_1 nhận giá trị .FALSE. thì ch−ơng trình sẽ kiểm tra đến BThuc_Logic_2. Nếu BThuc_Logic_2 nhận giá trị .TRUE. thì Các_câu_lệnh_2 sẽ đ−ợc thực hiện; nếu BThuc_Logic_2 nhận giá trị .FALSE. thì ch−ơng trình sẽ kiểm tra BThuc_Logic_3, Quá trình cứ tiếp diễn nh− vậy cho đến khi nếu tất cả các BThuc_Logic đều nhận giá trị .FALSE. thì ch−ơng trình sẽ thực hiện Các_câu_lệnh_n. Nếu Các_câu_lệnh_* ở giai đoạn nào đó của quá trình đã đ−ợc thực hiện, ch−ơng trình sẽ thoát khỏi cấu trúc IF và chuyển điều khiển đến những câu lệnh ngay sau END IF, ngoại trừ tr−ờng hợp trong Các_câu_lệnh_* có lệnh chuyển điều khiển GOTO đến một vị trí khác trong ch−ơng trình. 50
  50. Hình 2.4 Cấu trúc IF dạng 4 Ví dụ 2.6. Viết ch−ơng trình nhập điểm trung bình chung học tập (TBCHT) của một sinh viên và cho biết sinh viên đó đ−ợc xếp loại học tập nh− thế nào, nếu tiêu chuẩn xếp loại đ−ợc qui định nh− sau: Loại xuất sắc nếu TBCHT >=9; Loại giỏi nếu 9 10) THEN PRINT*, ‘ DIEM KHONG HOP LE’ STOP ! Dừng ch−ơng trình nếu điểm không hợp lệ ELSE IF (DIEM >= 9) THEN PRINT*,' LOAI XUAT SAC' ELSE IF (DIEM >= 8) THEN PRINT*,' LOAI GIOI' ELSE IF (DIEM >= 7) THEN PRINT*,' LOAI KHA' ELSE IF (DIEM >= 5) THEN PRINT*,' LOAI TRUNG BINH' ELSE PRINT*,' LOAI YEU' END IF END Ch−ơng trình trên đây có thể viết bằng cách khác nh− sau. PROGRAM XEPLOAI_2 INTEGER DIEM WRITE (*,'(A\)') ' CHO DIEM TBCHT: ' READ*, DIEM IF (DIEM < 0.OR.DIEM < 10) THEN PRINT*, ‘ DIEM KHONG HOP LE’ STOP 51
  51. END IF IF (DIEM >= 9) PRINT*,' LOAI XUAT SAC' IF (DIEM >= 8.AND.DIEM = 7.AND.DIEM = 5.AND.DIEM 10). Câu lệnh WRITE hàm chứa trong đó định dạng FORMAT (‘(A\)’) có tác dụng giữ cho con trỏ màn hình không nhảy xuống dòng d−ới mà nằm ngay sau hằng ký tự ‘CHO DIEM TBCHT: ‘. Rõ ràng, nếu sử dụng cấu trúc khối IF nh− ở ch−ơng trình XEPLOAI_1, các biểu thức lôgic sẽ gọn gàng hơn, và ch−ơng trình trông sáng sủa hơn so với ch−ơng trình XEPLOAI_2. 2.2.5 Lệnh nhảy vô điều kiện GOTO Đây là một trong những câu lệnh đ−ợc sử dụng khá phổ biến đối với Fortran 77 và các phiên bản tr−ớc. Khi lập trình với Fortran 90 lệnh GOTO ít đ−ợc sử dụng do cái gọi là “sự phá vỡ cấu trúc” của nó. Mặc dù vậy, câu lệnh này giúp ng−ời lập trình cảm thấy nhẹ nhàng khi gặp phải những tình huống khó xử. Cú pháp câu lệnh GOTO có dạng sau: GOTO m Trong đó m là nhãn của một câu lệnh nào đó sẽ đ−ợc chuyển điều khiển tới trong ch−ơng trình. Khi gặp lệnh GOTO, ngay lập tức ch−ơng trình sẽ chuyển điều khiển tới câu lệnh có nhãn m. Nếu trong ch−ơng trình không có câu lệnh nào có nhãn m thì lỗi sẽ xuất hiện. Hơn nữa, câu lệnh sẽ đ−ợc chuyển điều khiển tới (câu lệnh có nhãn m) không đ−ợc phép nằm trong vòng kiểm soát của lệnh chu trình DO và cấu trúc rẽ nhánh IF. Chẳng hạn, những tr−ờng hợp sau đây là không đ−ợc phép: GOTO 123 DO I = 1, N 123 X = X + Y END DO Hoặc: GOTO 456 IF (L1.AND.L2) THEN 52
  52. 456 A = B * C END IF Nh−ng có thể dùng lệnh GOTO để thoát khỏi một chu trình lặp hoặc một cấu trúc rẽ nhánh nào đó, chẳng hạn: DO I = 1, N GOTO 123 END DO 123 X = X + Y Hoặc IF (L1.AND.L2) THEN GOTO 456 END IF 456 A = B * C Sau đây là một ví dụ minh họa tác động của lệnh GOTO. Cho giá trị của các biến lôgic L1 và L2. Nếu L1=.TRUE. thì gán I=1, J=2; nếu L1=.FALSE. còn L2=.TRUE. thì gán I=2, J=3; nếu cả L1 và L2 đều nhận giá trị .FALSE. thì gán I=3, J=4. Khi đó, đoạn ch−ơng trình: IF (L1) THEN I = 1 J = 2 ELSE IF (L2) THEN I = 2 J = 3 ELSE I = 3 J = 4 END IF có thể đ−ợc thay thế bởi đoạn ch−ơng trình sau nếu sử dụng lệnh GOTO IF (.NOT.L1) GOTO 10 I = 1 J = 2 GOTO 30 10 IF (.NOT.L2) GOTO 20 I = 2 J = 3 GOTO 30 20 I = 3 J = 4 30 CONTINUE 53
  53. 2.2.6 Lệnh IF số học IF (BThuc_SoHoc) m1, m2, m3 Trong đó BThuc_SoHoc là một biểu thức số học, có thể có kiểu nguyên hoặc thực; m1, m2, m3 là nhãn của các câu lệnh có trong ch−ơng trình. Ng−ời ta gọi cấu trúc IF này là IF số học, vì quyết định rẽ nhánh phụ thuộc vào dấu của BThuc_SoHoc. Tác động của cấu trúc này đ−ợc mô tả trên hình 2.5. Tr−ớc hết ch−ơng trình sẽ tính giá trị của BThuc_SoHoc. Nếu BThuc_SoHoc nhận giá trị âm, ch−ơng trình sẽ chuyển điều khiển tới câu lệnh có nhãn m1; nếu BThuc_SoHoc Hình 2.5 Cấu trúc IF số học nhận giá trị bằng 0, ch−ơng trình sẽ chuyển điều khiển tới câu lệnh có nhãn m2; nếu BThuc_SoHoc nhận giá trị d−ơng, điều khiển sẽ đ−ợc chuyển tới câu lệnh có nhãn m3. Hai trong ba nhãn m1, m2, m3 có thể trùng nhau, có nghĩa là hai nhánh của điều khiển có thể chuyển đến cùng một câu lệnh. Tuy nhiên các câu lệnh có nhãn m1, m2, m3 không đ−ợc phép nằm trong vòng kiểm soát của lệnh chu trình DO và cấu trúc rẽ nhánh khác. Cũng nh− lệnh GOTO, lệnh IF số học cũng ít đ−ợc sử dụng khi lập trình với Fortran 90. Ví dụ 2.7. Nhập vào một số nguyên và xác định xem số đó nhỏ hơn, lớn hơn hay bằng 50. Ta có ch−ơng trình sau. INTEGER N PRINT*, ' CHO MOT SO NGUYEN ' READ*, N IF (N−50) 10, 20, 30 10 PRINT*,' SO NAY NHO HON 50' GOTO 40 20 PRINT*,' SO NAY BANG 50' GOTO 40 30 PRINT*,' SO NAY LON HON 50' 40 CONTINUE END Nếu ta chỉ quan tâm đến việc số nhập vào có lớn hơn 50 hay không, thì cấu trúc rẽ nhánh chỉ cần chuyển điều khiển đến hai nhánh. Khi đó ch−ơng trình đ−ợc viết lại thành: INTEGER N PRINT*, ' CHO MOT SO NGUYEN ' READ*, N IF (N−50) 10, 10, 30 10 PRINT*,' SO NAY NHO HON HOAC BANG 50' GOTO 40 54
  54. 30 PRINT*,' SO NAY LON HON 50' 40 CONTINUE END Các cấu trúc IF dạng 2, 3, 4 và IF số học cũng không thể là câu lệnh kết thúc của lệnh chu trình DO. Ví dụ 2.8. Giả sử ta cần liệt kê tất cả các số nguyên chia hết cho 13 trong phạm vi từ N1 đến N2 với N1 và N2 đ−ợc nhập từ bàn phím. Khi đó ch−ơng trình có thể viết nh− sau nếu sử dụng lệnh chu trình DO dạng 2 hoặc dạng 3 mà không thể sử dụng dạng 1: Program Cach_1 ! Dung Chu trinh DO dang 2 write(*,'(A\)')' Cho hai so nguyen N1 va N2: ' read*, N1, N2 if (N1 > N2 .OR. N2 N2 .OR. N2 < 13) then Print*, ' So lieu khong hop le & & hoac khong co so nao chia het cho 13' Stop end if Do i=N1,N2 if (mod(i,13)==0) then print*, i end if enddo End 2.3 Kết hợp DO và IF Nh− đã thấy ở ví dụ 2.8, chu trình DO có thể bao hàm cả cấu trúc rẽ nhánh IF và ng−ợc lại. Nghĩa là chu trình DO có thể kiểm soát toàn bộ cấu trúc IF, hoặc trong cấu trúc IF có chứa trọn vẹn chu trình DO. Nhất thiết chúng không đ−ợc phép giao nhau. Cú pháp tổng quát của các cấu trúc này nh− sau. 55
  55. Dạng 1: Cấu trúc IF nằm trong chu trình DO: DO bdk = TriDau, TriCuoi, Buoc IF (BThuc_Logic) THEN END IF END DO Dạng 2: Chu trình DO nằm trong cấu trúc IF: IF (BThuc_Logic) THEN DO bdk = TriDau, TriCuoi, Buoc END DO END IF Ví du 2.9. Ch−ơng trình sau đây sẽ kết thúc sau 20 lần lặp, mặc dù số lần lặp đ−ợc qui định bởi lệnh chu trình là 10000: PROGRAM IFinDO ! C.trúc IF nằm trong C.trình DO Do i=1,10000 If (mod(i,2)==0) write(*,*) i If (i == 20) then print*,' Ket thuc sau lan lap thu ',i stop End If Enddo END Ví dụ 2.10. Đọc vào hai số nguyên M và N. Nếu M ≤ N thì in ra lần l−ợt các số từ M đến N, ng−ợc lại thì in thông báo M > N. PROGRAM DOinIF ! C.trình DO nằm trong C.trúc IF Print*,' Cho hai so nguyen: ' Read*, M, N if (M N' End If END 2.4 Rẽ nhánh với cấu trúc SELECT CASE Một trong những ph−ơng pháp hữu hiệu để chuyển điều khiển trong ch−ơng trình là sử dụng cấu trúc rẽ nhánh SELECT CASE. Dạng tổng quát của cấu trúc này nh− sau. SELECT CASE (BThuc_Chon) 56