You are on page 1of 153

LA VĂN HIỂN

GIỚI THIỆU

NGÔN NGỮ LẬP TRÌNH


Fortran 90
Tài liệu lập trình căn bản
dành cho sinh viên :
 Khối khoa học kỹ thuật .
 Khối khoa học tự nhiên .

Tà i liệ u được trình bà y dưới hình thức tự họ c .
Bao gò m cá c nọ i dung cơ bả n nhá t củ a Fortran 90 .
Thực hà nh lạ p trình ở cuó i mõ i chương .
Kết hợp phần mềm GNUPLOT để vễ đò thị .

2018
LỜI NÓI ĐẦU
Tài liệu “ Giới thiệu NGÔN NGỮ LẬP TRÌNH Fortran 90 “ được biên soạn nhằm giúp
các bạn sinh viên các khối, ngành khoa học kỹ thuật, khoa học tự nhiên quan tâm đến ngôn ngữ
Fortran nắm được phương pháp căn bản để viết văn bản chương trình và chạy các chương trình
tính toán bằng Fortran 90, phục vụ yêu cầu học tập , thực hiện các bài tập lớn ,luận văn tốt nghiệp.

Fortran có lịch sử phát triển lâu đời qua nhiều thế hệ máy tính với các phiên bản khác nhau
, đến nay vẫn là ngôn ngữ có nhiều ưu thế trong lĩnh vực tính toán số . Fortran được xem là ngôn
ngữ lập trình rất phố biến đối với các sinh viên, kỹ sư, khoa học gia thuộc các lĩnh vực khoa học ,
kỹ thuật tại các nước Âu – Mỹ. Việc học tập Fortran còn giúp các bạn sinh viên có điều kiện học
tập ở nước ngoài , đọc hiểu các chương trình viết bằng ngôn ngữ Fortran được trình bày trong các
sách , tài liệu , bài báo nước ngoài .

Hai phiên bản Fortran được sử dụng phổ biến nhất hiện nay là FORTRAN 77 và Fortran 90
, trong đó phiên bản Fortran 90 được xem là một phiên bản quan trọng trong lịch sử phát triển
Fortran . Fortran 90 có rất nhiều điểm mới so với các phiên bản trước như văn bản chương trình
được phép viết theo kiểu “ định dạng tự do “ ( free format ) khác với kiểu “ định dạng cố định theo
cột “ ( fixed format ) , các cấu trúc điều khiển , xử lý mảng , xử lý vào / ra , chương trình con , cấp
phát động …tạo nhiều thuận lợi hơn cho người lập trình .Từ kiến thức Fortran 90 , các bạn có thể
dễ dàng tiếp cận các phiên bản tiếp theo như Fortran 95 , Fortran 2003, Fortran 2008 .

Tài liệu này sử dụng các trình biên dịch miễn phí như G77/GFortran của GNU Compiler
Collection (GCC) và G95 thông qua phần mềm Force 2.0 là một môi trường phát triển tích hợp (
IDE ) gọn nhẹ dùng để viết văn bản chương trình , tiến hành biên dịch , liên kết và chạy thử chương
trình . Tài liệu cũng giới thiệu thêm trình biên dịch Fortran g95 và Silverfrost FTN95 Personal
Edition để bạn đọc tham khảo , chạy kiểm chứng chương trình .

Phần phụ lục giới thiệu tổng quát phần mềm GNUPLOT dùng để vẽ các đồ thị 2D, 3D lấy
dữ liệu từ kết quả tính toán của Fortran và một chương trình mẫu trong bộ LINPACK dùng để giải
hệ phương trình tuyến tính là các ví dụ về các tiện ích miễn phí từ các tổ chức phát triển Fortran
cung cấp cho người sử dụng . Các bạn sinh viên cần tham khảo , sử dụng các chương trình tiện ích
rất phong phú đã được lập trình sẵn từ các thư viện Fortran trên Internet , phục vụ cho việc học tập,
nghiên cứu .

Mặc dù đã cố gắng rà soát kỹ các nội dung trong tài liệu cũng như chạy kiểm tra các chương
trình ví dụ nhưng khó tránh khỏi có những sai sót hoặc những điểm trình bày chưa phù hợp . Tác
giả rất mong và xin cám ơn sự đóng góp của các bạn đọc , các bạn sinh viên gần xa để tài liệu
ngày càng hoàn thiện hơn .

Tháng 05 năm 2018

La Văn Hiển
Email : hienbk58@yahoo.com
Cập nhật : 16/06/2019.

1
CHƯƠNG 1 : GIỚI THIỆU CHUNG VỀ NGÔN NGỮ LẬP TRÌNH FORTRAN
1.1 Sơ lược về lịch sử ngôn ngữ lập trình Fortran .
1.1.1 Nguồn gốc .
Fortran ( hay FORTRAN ) được viết tắt từ cụm từ Mathematical FORmula TRANslating
System ( hệ thống dịch công thức toán học ) , là ngôn ngữ lập trình cấp cao đầu tiên , kiểu biên
dịch ( compiler ) , do John Backus ( 1924 – 2007 ) cùng một nhóm kỹ sư điện toán thuộc tập đoàn
công nghiệp máy tính IBM ( International Business Machines ) thiết kế và phát triển từ giữa những
năm 50 của thế kỷ XX . Ban đầu tên ngôn ngữ được viết là FORTRAN , nhưng xu hướng hiện
nay được viết chuẩn hóa thành Fortran ( chỉ viết hoa kí tự đầu ) .
1.1.2 Phạm vi ứng dụng .
Ngôn ngữ lập trình Fortran hiện nay có đầy đủ tính năng của một ngôn ngữ lập trình cấp
cao hiện đại, hỗ trợ nhiều kiểu lập trình , có khả năng ứng dụng trên nhiều lĩnh vực khác nhau ,
tuy nhiên Fortran thích hợp hơn cho các ứng dụng tính toán số trong lĩnh vực khoa học kỹ thuật ,
khoa học tự nhiên và được xem là ngôn ngữ lập trình rất phổ biến đối với các sinh viên, kỹ sư,
khoa học gia thuộc các lĩnh vực nêu trên tại các nước Âu Mỹ .
1.1.3 Các phiên bản của ngôn ngữ Fortran .
FORTRAN (1957) , FORTRAN II ( 1958) , FORTRAN III (1958) , FORTRAN IV (1961)
, FORTRAN 66 (1966) được xem là các phiên bản cũ , lỗi thời .
Các phiên bản được sử dụng phổ biến hiện nay là FORTRAN 77 , Fortran 90 và Fortran
95 . Phiên bản Fortran 90 , được xem là cột mốc quan trọng trong lịch sử phát triển ngôn ngữ
Fortran , có rất nhiều điểm mới so với FORTRAN 77 như văn bản chương trình hay tập tin nguồn
( source code ) được phép viết theo kiểu “ định dạng tự do “ ( free format ) , khác với kiểu “ định
dạng cố định theo cột “ ( fixed format ) của các phiên bản trước ; các cấu trúc điều khiển , xử lý
mảng , xử lý vào/ra , cấp phát động , chương trình con , đơn vị chương trình module tạo nhiều
thuận lợi hơn cho người lập trình . Phiên bản Fortran 95 bổ sung một số ít các nội dung mới vào
Fortran 90 và bỏ đi một vài điểm lỗi thời vẫn còn giữ trong Fortran 90 . Hai phiên bản sau cùng là
Fortran 2003 ( hỗ trợ lập trình hướng đối tượng ) và Fortran 2008.
Một số công ty, tổ chức phát triển ngôn ngữ Fortran đã cho ra đời nhiều biến thể khác nhau
từ Fortran chuẩn ( Standard Fortran ) bằng cách thêm vào một số chức năng mở rộng . Các phiên
bản Fortran chuẩn được ANSI ( American National Standards Institute - Viện tiêu chuẩn quốc gia
Hoa Kỳ ) và ISO ( International Organization for Standardization - Tổ chức quốc tế về tiêu chuẩn
hóa ) xem xét và công bố . Chương trình viết bằng Fortran chuẩn đảm bảo tính khả chuyển (
portable ) , chạy ổn định trên các hệ máy tính khác nhau có cài đặt trình biên dịch Fortran .
Phần trình bày trong tài liệu này tập trung vào phiên bản Fortran 90 . Phần thực hành minh
họa các chương trình Fortran 90 , chúng ta sử dụng phần mềm miễn phí Force 2.0 ,version 2.0.9p
, được download tại trang web :
http://force.lepsch.com/2009/05/downloads.html
Tên tập tin download : Force209G95Setup.exe , kích thước 3,55 MB . Phần mềm Force là
một môi trường phát triển tích hợp IDE ( Integrated Development Environment ) gọn nhẹ , có
chức năng biên dịch và chạy các chương trình viết bằng FORTRAN 77 hoặc Fortran 90 . Force sử
dụng trình biên dịch miễn phí G77/GFortran của GNU Compiler Collection (GCC) và G95.
Khi cài đặt xong , chúng ta mở file Force2.exe , sẽ thấy hiển thị cửa sổ môi trường phát
triển tích hợp IDE của Force 2.0. Trong môi trường này , chúng ta sẽ soạn tập tin nguồn ( văn bản
chương trình dạng text ) , tiến hành biên dịch , liên kết ( lệnh compile ) và nếu không có lỗi sẽ tiến
hành chạy thử ( lệnh run ) chương trình .
Ngoài ra , chúng ta có thể chọn các trình biên dịch Fortran khác như sau để chạy kiểm tra
cùng một chương trình nguồn :
2
 Sử dụng môi trường IDE của SilverFrost qua chương trình FTN90 Personal Edition (
chương trình miễn phí phục vụ học tập , chạy test ) với file có tên ftn95_personal.exe ( kích
thước 70, 996 MB ) , được download tại trang web :
https://www.silverfrost.com/32/ftn95/ftn95_personal_edition.aspx
Khi download về máy tính xong , chúng ta chạy file ftn95_personal.exe để cài đặt chương
trình vào một thư mục tùy chọn , ví dụ D :\FTN95 . Sau đó chúng ta chạy file plato.exe để mở
môi trường IDE Plato . Từ đây , có thể soạn thảo , biên dịch , liên kết và chạy các chương trình
Fortran .
 Để có thể biên dịch trực tiếp tại cửa sổ Command Prompt của Windows , chúng ta có thể
vào trang web The Fortran Company có địa chỉ như sau để download về trình biên dịch g95 qua
một file cài đặt có tên g95-Mingw_201210.exe ( 5381 KB ) :
http://www.fortran.com/the-fortran-company-homepage/whats-new/g95-windows-
download/

Phần hướng dẫn sơ lược sử dụng các trình biên dịch này sẽ được trình bày ở cuối chương.

1.2 Bộ kí tự của Fortran . Văn bản chương trình Fortran .


1.2.1 Bộ kí tự của Fortran .
Bộ kí tự của Fortran bao gồm :

a) Chữ viết thường : từ a đến z ; ( 26 chữ cái tiếng Anh )


b) Chữ viết hoa : từ A đến Z ;
c) Chữ số : 0 đến 9 ; ( 10 chữ số )
d) Kí tự nối chân : _ ; ( underscore )
e) Các kí tự khác : = + - * /( ) < > ,.;:‘“!&%$?
f) Kí tự trống ( blank , space ) .
Các đơn vị văn bản chương trình như từ khóa ( keyword ) , tên biến , tên hằng , tên chương
trình con , xâu kí tự , biểu tượng …sử dụng các kí tự trong bộ kí tự Fortran .
Kí hiệu các phép toán số học cộng, trừ, nhân, chia được viết : + - * / ; riêng phép lũy thừa
sử dụng hai dấu * liên tiếp ( ví dụ : 2**3 = 23 = 8 ) .
1.2.2 Soạn thảo văn bản chương trình ( tập tin nguồn dạng *.f90 )
Để soạn một văn bản chương trình Fortran 90 , có thể dùng trình soạn thảo Notepad hoặc
soạn thảo ngay trong môi trường Force . Tập tin nguồn phải được lưu ( save as ) dưới dạng có phần
đuôi là *.f90 , ví dụ hinhtron.f90 , để trình biên dịch có thể nhận dạng được loại tập tin và phiên
bản Fortran phù hợp ( Tập tin văn bản soạn theo định dạng cố định có phần đuôi là *.f , được giới
thiệu ở cuối chương ; tập tin văn bản của Fortran 95 có phần đuôi là *.f95 ) .
Fortran không phân biệt chữ hoa với chữ thường trong văn bản chương trình ( case
insensative ) trừ việc thể hiện nội dung trong xâu kí tự ( string of characters ) . Trong các văn bản
chương trình trước đây , người lập trình thường viết các dòng lệnh bằng chữ IN HOA nhưng xu
hướng hiện nay là viết văn bản bằng chữ thường hoặc kết hợp cả hai .

1.3 Phương thức chung tạo tập tin thi hành (executable file) từ văn bản chương trình .
a) Trong môi trường DOS hoặc Windows , các ngôn ngữ lập trình cấp cao ( như Fortran, C,
Pascal,…) tạo tập tin thi hành có đuôi *.exe theo hai bước :
Bước 1 : Biên dịch ( compiling ) tập tin nguồn ( source code , file dạng text do người lập
trình soạn thảo ) sang tập tin đối tượng ( object code ) dạng nhị phân ( binary code ) .

3
Bước 2 : Liên kết ( linking ) một hoặc vài tập tin đối tượng và các tập tin thư viện có liên
quan để tạo thành tập tin thi hành được ( executable file ) .

Tập tin thi hành *.exe sau đó có thể chạy độc lập bên ngoài môi trường IDE.
b) Sơ đồ minh họa :

Tập tin nguồn Tập tin đối tượng Tập tin thi hành
( Source code ) ( Object code ) ( Executable code )
Biên dịch Liên kết
( Compiling ) ( Linking )
File1.f90 File1.obj

File2.f90 File2.obj Program.exe

Thư viện
Libraries

Hình 1.1 : Các bước biên dịch và liên kết .

1.4 Một ví dụ về chương trình Fortran .


Để có một cái nhìn tổng quát về việc soạn thảo tập tin nguồn của Fortran và cách thức biên
dịch , liên kết và chạy chương trình trong môi trường Force , chúng ta xem qua một ví dụ đơn giản
dưới đây .
1.4.1 Văn bản chương trình tính chu vi và diện tích hình tròn .
Trong cửa sổ Force , vào menu File , chọn New . Khung cửa sổ soạn thảo hiện ra và con trỏ
xuất hiện ở dòng đầu tiên. Chúng ta vào nội dung văn bản chương trình như dưới đây và lưu tập
tin dưới tên hinhtron.f90 ( cần tạo trước một thư mục chứa các tập tin văn bản Fortran để tiện việc
thực hành ,ví dụ thư mục có tên D :\work ) . Văn bản chương trình được xem là một tập các chỉ
thị được sắp xếp theo một thứ tự từ trên xuống dưới và được viết theo các quy tắc rất chặt chẽ của
ngôn ngữ Fortran nhằm ra lệnh cho máy tính thực hiện một nhiệm vụ xác định nào đó .

Phần đánh số dòng trong cửa sổ soạn thảo là tự động nhằm giúp người lập trình dễ dàng
tham chiếu đến các dòng lệnh . Khi chúng ta soạn thảo tập tin nguồn , các từ khóa trong văn bản ,
các dòng ghi chú và một số đơn vị cú pháp trong văn bản sẽ tự động có màu thích hợp giúp chúng
ta dễ phân biệt các đối tượng khác nhau trong văn bản . Trong văn bản ví dụ dưới đây , các từ khóa
( keyword ) của Fortran được in đậm .

program HinhTron
implicit none
! phan khai bao
real :: r,chuvi,dientich ! khai bao bien
real,parameter :: pi = 3.14159 ! khai bao hang
! phan tinh toan
print*,'Cho biet ban kinh r :' ; read*,r
chuvi = 2.0*r*pi
dientich = r*r*pi
4
! ket qua
print*,'Chu vi : ',chuvi
print*,'Dien tich : ',dientich
read* ! tam dung man hinh de xem ket qua
end program HinhTron

Hình 1.2 : Soạn thảo tập tin nguồn trong cửa sổ Force .

1.4.2 Biên dịch, liên kết và chạy chương trình hinhtron.f90 .


Bước biên dịch,liên kết : chọn menu Run , chọn mục Compile (phím tắt Ctrl – F9) . Khi
biên dịch , liên kết xong , nếu không có lỗi cửa sổ Process hiển thị như sau :

Hình 1.3 : Cửa sổ Process thông báo việc biên dịch hoàn tất , không có lỗi .

5
Trường hợp xảy ra lỗi cú pháp ( syntax error ) sẽ có thông báo thích hợp tại cửa sổ phía
dưới vùng soạn thảo để hướng dẫn người lập trình chỉnh sửa lại tập tin nguồn , sau đó chạy lại trình
biên dịch . Ví dụ dòng thứ bảy của văn bản trên , chúng ta viết read , r thay vì viết đúng là read*,
r . Cửa sổ Process sẽ báo lỗi và có thông báo chỉ ra vị trí ( dòng ) bị lỗi .

In file D:\work\hinhtron.f90:7
print*,'Cho biet ban kinh r :' ; read,r
Error: Syntax error in READ statement at (1)

Hình 1.4 : Cửa sổ Process thông báo lỗi và vị trí có lỗi cú pháp .

Bước chạy thử tập tin thi hành : chọn menu Run, mục Run ( phím tắt F9). Máy tính sẽ tạm
thời thoát khỏi môi trường Force và cho chạy tập tin hinhtron.exe trên một cửa sổ màu đen riêng .
Tại đây , con trỏ chương trình sẽ dừng lại sau câu Cho biet ban kinh r : và đợi chúng ta nhập một
giá trị trực tiếp từ bàn phím ( ví dụ 3.0 ) và nhấn phím Enter để xác nhận . Các kết quả tính toán
được hiển thị trên màn hình . Sau khi xem xong kết quả , chúng ta nhấn phím Enter để thoát khỏi
cửa sổ chạy chương trình và trở về môi trường IDE của Force .

Hình 1.5 : Kết quả chạy tập tin hinhtron.exe

Để chạy file *.exe trực tiếp trong thư mục có lưu file nguồn *.f90 như ví dụ trên , trên thanh
menu chọn Options , chọn Environment Options , chọn tiếp thẻ Run , chọn Run with  EXE
(direct ) . Trường hợp chọn Console , chương trình sẽ được chạy trên cửa sổ cmd ( command
prompt ) của Windows .

Hình 1.6 : Chọn cửa sổ chạy chương trình đã được biên dịch và liên kết .

Trường hợp có lỗi khi chạy chương trình ( run time error ) hoặc kết quả không đúng với kết
quả mà chúng ta đã biết trước ( chạy test chương trình ) thì cần quay lại văn bản chương trình để
6
rà soát lại thuật giải , các biểu thức tính toán , nội dung các câu lệnh để chỉnh sửa , sau đó thực hiện
lại việc biên dịch, liên kết và chạy chương trình như trên .
Để lưu kết quả của cửa sổ màu đen này vào file Word hoặc Notepad ,chúng ta để con trỏ
lên thanh tiêu đề hinhtron.exe và bấm phím phải con chuột , khi xuất hiện menu ,chọn Edit , chọn
Select All , nội dung chọn được chiếu sáng , bấm Enter ( tương đương lệnh Copy) để đưa nội dung
này vào clipboard sau đó dán vào file Word đã mở sẵn ( trong Word bấm phím phải chuột , chọn
Paste hoặc dùng tổ hợp phím Ctrl – V ) .

1.5 Giải thích sơ lược văn bản chương trình hinhtron.f90


Chương trình Fortran ví dụ nêu trên chỉ có một đơn vị chương trình là chương trình chính
( main program ) . Chương trình Fortran tổng quát thường gồm một chương trình chính và nhiều
đơn vị chương trình khác có liên quan ( sẽ được trình bày ở các phần sau ) .

1.5.1 Cấu trúc tổng quát chương trình chính .

program tên_chương_trình
implicit none
! < các câu lệnh không thực thi , khai báo các đối tượng [ biến ,hằng, mảng,…] sử dụng
trong chương trình , luôn luôn phải đặt trước các câu lệnh thực thi .>

! <các câu lệnh thực thi : nhập/xuất dữ liệu , tính toán , so sánh , gọi chương trình con …>
end program tên_chương_trình ! có thể viết đơn giản là end

Các từ program , end program, implicit none thuộc bộ từ khóa ( keyword ) của Fortran.

Một số quy định về soạn thảo văn bản chương trình Fortran 90 :
 Văn bản chương trình gồm các dòng text , mỗi dòng có không quá 132 kí tự .
 Nội dung văn bản bên phải sau dấu chấm than ! là phần chú thích , dùng để giải thích
các câu lệnh hoặc các phần khác nhau của chương trình nhằm giúp người đọc văn bản hiểu rõ các
câu lệnh . Trình biên dịch sẽ bỏ qua các chú thích này.
 Trình biên dịch bỏ qua các dòng trống ( dòng trắng ) trong văn bản chương trình .
 Thông thường một câu lệnh được đặt trên một dòng văn bản riêng biệt , nếu viết hai hoặc
nhiều câu lệnh trên cùng một dòng thì phải dùng dấu chấm phẩy ; để phân cách các câu lệnh . Ví
dụ : print*,'Cho biet ban kinh r :' ; read*, r ! dòng có 2 câu lệnh .
 Câu lệnh dài nếu cần phải viết trên nhiều dòng liên tiếp thì phải dùng dấu nối & đặt ở cuối
dòng và có thể phải đặt ở đầu dòng tiếp theo ( trường hợp xuống dòng ở giữa xâu kí tự thì bắt buộc
phải đặt dấu nối & ở đầu dòng tiếp theo ) . Ví dụ :
integer :: dai , rong , &
chuvi , dien tich
print*, ‘ Tri so chu vi cua &
& duong tron la : ‘ , chuvi ! bắt buộc phải có dấu & ở đầu dòng tiếp theo.
dientich = r*r &
*pi ! không cần dấu & ở đầu dòng , nếu có cũng không có lỗi .
Số dòng tiếp theo cho phép : 39 dòng . ( Thay đổi theo trình biên dịch ) .
 Các khoảng trống có thể chèn vào giữa các đơn vị văn bản như từ khóa, tên biến , tên hằng
, các biểu tượng ( giúp cho chương trình dễ đọc ) …mà không ảnh hưởng đến kết quả ( trừ trường
hợp xuất hiện trong xâu kí tự ) . Tuy nhiên không được chèn khoảng trống vào giữa các kí tự của
một đơn vị văn bản ( từ khóa, tên ) hoặc biểu tượng . Ví dụ :
7
Viết real :: r,chuvi,dientich giống như real :: r, chuvi, dientich ; tuy nhiên lưu ý dấu
:: không được viết tách rời ra thành : : . Không được viết re al thay cho real . Không được viết
> = thay cho >= ( kí hiệu so sánh lớn hơn hay bằng ) .
 Nội dung của một xâu kí tự được đặt giữa hai dấu nháy đơn ‘ string ‘ , hoặc hai dấu nháy
kép “ string “ có giá trị như nhau .Phải dùng thống nhất một kiểu dấu nháy giữa một xâu kí tự . Để
thể hiện It’s a book , chúng ta viết ‘It’’s a book.’ (đặt hai dấu nháy đơn trong ‘string’) , để thể hiện
Say “Hello !” , viết ‘Say “Hello !” ‘ hoặc “ Say “”Hello !”” “ .
1.5.2 Câu lệnh implicit none .
Câu lệnh implicit none là câu lệnh tùy chọn ( optional ) , được đặt ngay sau từ khóa program
, buộc người lập trình phải khai báo rõ ràng ( explicitly ) tất cả các biến , hằng ,mảng … tham gia
vào chương trình cùng kiểu dữ liệu tương ứng ( số nguyên, số thực , số phức , kí tự , logic …) .
Fortran 90 vẫn chấp nhận quy tắc ngầm định về kiểu biến số học như các phiên bản trước ,
nghĩa là có thể không cần phải khai báo biến trong văn bản chương trình , mà chỉ căn cứ vào kí tự
đầu của tên biến để biết biến thuộc loại dữ liệu nào : cụ thể những biến có tên bắt đầu bằng các chữ
I,J,K,L,M,N thuộc loại số nguyên mặc định ( integer ) và có tên bắt đầu bằng các chữ khác thuộc
loại số thực mặc định ( real ) .
Tuy nhiên , việc khai báo đầy đủ các đối tượng cùng kiểu dữ liệu ngay từ đầu chương trình
là một thói quen lập trình tốt , tránh được nhiều sai sót khó phát hiện khi biên dịch chương trình .
Trong tài liệu này , chúng ta sử dụng câu lệnh implicit none trong văn bản chương trình .
1.5.3 Khai báo biến ( variable) .
Biến dùng để lưu trữ dữ liệu , kết quả tính toán trong bộ nhớ máy tính .Khai báo biến gồm
khai báo tên biến ( variable name ) và kiểu dữ liệu ( data type ) của biến . Fortran quy định tên biến
là xâu kí tự từ 1 đến 31 kí tự , chỉ gồm chữ viết, chữ số và kí tự nối chân , nhưng tên biến phải bắt
đầu bằng chữ viết ( letter ) . Tên biến không được có khoảng trống chen giữa hoặc kí tự đặc biệt .
Ví dụ về tên biến đúng : ChuVi, chu_vi, dientich, NhanVien_01, x, y, z
Ví dụ về tên biến đặt sai : 1x , dien tich , chu-vi
Fortran không phân biệt chữ hoa với chữ thường trong tên biến . Các tên biến : DienTich ,
dientich , Dientich là như nhau . Quy tắc đặt tên biến còn được áp dụng cho việc đặt tên ( định
danh , identifier ) cho các đối tượng khác như hằng , mảng, tên chương trình ...
Fortran không cấm việc dùng tên các từ khóa ( keyword) để đặt tên ( identifier ) cho các đối
tượng biến, hằng , mảng …như một số ngôn ngữ lập trình khác , nhưng chúng ta nên tránh việc
làm này vì dễ gây ra các nhầm lẫn .
Câu lệnh real :: r ,chuvi ,dientich , khai báo 3 biến kiểu số thực ( từ khóa real ) có tên là
r, chuvi, dientich ; giữa real và dãy tên biến là dấu :: viết liền nhau . Trong trường hợp đơn giản
này dấu :: có thể bỏ mà không ảnh hưởng đến kết quả ( real r,chuvi,dientich ) . Lưu ý các tên
biến phải được phân cách bằng dấu phẩy .
1.5.4 Khai báo hằng (named constant ) .
Câu lệnh real , parameter :: pi = 3.14159 , khai báo pi là một hằng kiểu số thực có giá trị
được gán là 3,14159 . Ở đây thuộc tính parameter là một từ khóa báo cho biết biến pi được khai
báo là một hằng . Hằng có giá trị không đổi trong suốt chương trình . Lưu ý dấu :: trong trường
hợp này bắt buộc phải có . Việc khai báo hằng giúp cho chương trình dễ đọc , dễ hiểu câu lệnh và
dễ chỉnh sửa giá trị của hằng theo yêu cầu của bài toán .
1.5.5 Viết dữ liệu ra màn hình ( thiết bị xuất chuẩn ) .
Câu lệnh print *, <đối tượng xuất ra màn hình . > dùng để xuất ( viết ) ra màn hình nội
dung xâu kí tự , giá trị biến , giá trị biểu thức . Dấu * cho biết các đối tượng được viết theo định
dạng ( format ) tự động ( mặc định ) , phụ thuộc vào cấu trúc máy tính . Các đối tượng xuất khác
nhau được phân cách bởi dấu phẩy và được viết ra trên một dòng màn hình .

8
Câu lệnh print* không kèm đối tượng xuất dùng để tạo một dòng trống (blank line) trên
màn hình .
Câu lệnh print tổng quát : print format , < list > , trong đó format là phần định dạng cho
các đối tượng xuất , list là danh sách các đối tượng xuất ra màn hình .
Câu lệnh write (*,*) < đối tượng xuất ra màn hình . > có ý nghĩa tương đương lệnh print*,
<đối tượng xuất .> . Dấu * đầu tiên cho biết kênh xuất là màn hình ,dấu * thứ hai quy định kiểu
định dạng là tự động.
Ví dụ : write (*,*) ‘Cho biet ban kinh r : ‘  print*, ’Cho biet ban kinh r : ‘

Ghi chú : lệnh print chỉ dùng để xuất ra màn hình , trong khi lệnh write có nhiều lựa chọn
hơn như dùng để xuất ra màn hình , tập tin ...
1.5.6 Nhập dữ liệu từ bàn phím ( thiết bị nhập chuẩn ) .
Câu lệnh read *, < danh sách tên biến . > dùng để nhập giá trị trực tiếp từ bàn phím và gán
cho các biến theo thứ tự trong danh sách . Khi chương trình thực hiện đến câu lệnh read*, r thì
máy tính sẽ dừng lại chờ chúng ta nhập một giá trị là bán kính r từ bàn phím , ví dụ nhập số 3.0 ,
xong nhấn Enter để xác nhận . Biến r sẽ lấy giá trị 3.0 trong các biểu thức tính toán tiếp theo .
Câu lệnh read (*,*) < tên các biến > có ý nghĩa tương tự read*,< tên các biến> .
Dấu * đầu tiên trong read (*,*) cho biết kênh nhập dữ liệu là bàn phím , dấu * thứ hai cho
biết kiểu định dạng dữ liệu là kiểu tự động . Ví dụ : read (*,*) r  read*, r
Nếu x,y,z là ba biến kiểu số thực thì khi gặp câu lệnh read*, x,y,z , chúng ta có thể nhập
giá trị cho từng biến , sau mỗi giá trị nhập phải nhấn Enter ; hoặc có thể nhập cùng lúc ba giá trị
vào trên cùng một hàng nhưng lúc này mỗi số phải được viết cách nhau bằng dấu phẩy hoặc cách
nhau một hay vài khoảng trống , xong nhấn Enter để kết thúc việc nhập liệu cho cả ba số .

Lệnh read* không có đối số làm tạm dừng màn hình chờ chúng ta xem kết quả , xong nhấn
phím Enter để chương trình tiếp tục hoặc kết thúc . Nếu không có câu lệnh này thì khi chương
trình được thực thi xong , máy tính lập tức quay trở lại môi trường IDE Force làm chúng ta không
kịp xem kết quả . Tuy nhiên một số trình biên dịch như Plato lại không cần câu lệnh này ; khi
chương trình chạy xong , xuất hiện câu “ Press return to close window … “ hoặc “ Press any key
to continue “ , lúc này sau khi xem kết quả xong , chúng ta nhấn phím Enter hoặc phím bất kỳ để
đóng cửa sổ thực thi chương trình , trở về môi trường soạn thảo .
Hai câu lệnh như print*, “ Cho biet gia tri cua x va y : “ ; read *, x,y được dùng kết hợp
với nhau để hướng dẫn việc nhập số liệu cho các biến x và y .
Lưu ý : khi sử dụng các lệnh print* hoặc read* , phải có dấu phẩy giữa lệnh và các đối tượng
xuất , trong khi dùng lệnh write (*,*) và read (*,*) thì không có .

1.5.7 Câu lệnh gán .


Các câu lệnh gán trong chương trình hinhtron.f90 :
chuvi = 2.0*r*pi ! tính chu vi
dientich = r*r*pi ! tính diện tích
Biểu thức số học bên phải dấu = ( dấu dành cho phép gán ) được tính toán với các giá trị
biến, hằng đã biết , sau đó đem gán giá trị này cho biến bên trái . Trong phép gán , cần có sự kiểm
soát tính tương thích về kiểu dữ liệu giữa hai vế và cần nắm vững quy tắc chuyển đổi mặc định
của Fortran về kiểu dữ liệu ( sẽ được trình bày trong các chương sau ) . Dấu phép gán = không
phải là dấu bằng theo nghĩa toán học mà phải hiểu là phép thay thế giá trị được tính từ vế phải cho
nội dung của biến bên vế trái . Lưu ý cách viết số thực 2.0 hoặc 2. thay vì viết số 2 là số nguyên .

9
( Nếu viết 2 thì kết quả vẫn tương tự , lúc này Fortran sẽ tự chuyển đổi số nguyên thành số thực
trước khi tính toán biểu thức , chúng ta sẽ tìm hiểu thêm vấn đề này ở chương 3 ) .
Trong chương trình trên , do chúng ta sử dụng lệnh xuất theo kiểu định dạng (format) tự
động cho dữ liệu kiểu real ( số thực độ chính xác đơn, thể hiện 6 hoặc 7 chữ số thập phân có nghĩa
) nên các chữ số thập phân xuất hiện thêm ở vị trí cuối có thể không thể hiện đúng kết quả * ( giá
trị diện tích đúng ở ví dụ là 28.27431 ) . Tuy nhiên nếu sử dụng lệnh print với kiểu định dạng (
format ) phù hợp ( sẽ được trình bày ở các chương sau ) , chúng ta sẽ chủ động và khắc phục được
vấn đề này . Khi thay các dòng lệnh xuất có định dạng ( dòng 11, 12 ) , được kết quả như sau :

Cho biet ban kinh r :


3.0
Chu vi : 18.84954 ! viết lại dòng 11 : print ‘(a,f10.5)’, ‘Chu vi : ‘, chuvi
Dien tich : 28.27431 ! viết lại dòng 12 : print ‘(a,f10.5)’, ‘Dien tich : ‘, Dien tich

Ghi chú : + Xâu kí tự định dạng ‘(a,f10.5)’ sau từ khóa print dùng để sắp xếp viết ra một xâu kí tự
( kí hiệu định dạng là chữ a ) và một số thực được viết dưới dạng dấu chấm tĩnh , chiếm 10 ô ( kí tự ) màn
hình , trong đó phần lẻ thập phân chiếm 5 ô ( kí hiệu định dạng là f10.5 ) . Số được viết căn lề phải .

2 8 . 2 7 4 3 1

+ * Sự chuyển đổi tự động giữa hệ thập phân ( người dùng ) và hệ nhị phân ( máy tính ) gây ra sai số do
việc cắt cụt phần đuôi của số thực ( truncation error ) .

1.6 Thực hành .


1.6.1 Phân tích câu lệnh .
Để làm quen với Fortran , bạn đọc tự phân tích từng dòng lệnh của chương trình Fortran
chunhat.f90 như sau và chép vào cửa sổ soạn thảo của Force , cần quan sát các màu sắc do môi
trường Force hiển thị để phân biệt các đối tượng khác nhau của văn bản chương trình :

program ChuNhat
! Chuong trinh tinh dien tich hinh chu nhat biet hai canh x,y
implicit none
real :: x,y,dientich
print *,” Tinh dien tich hinh chu nhat “
print *
print ‘(a,$)’, ” Vao gia tri canh x : “ ; read *, x
print ‘(a,$)’, ” Vao gia tri canh y : “ ; read *, y
dientich = x*y ! Tinh dien tich hinh chu nhat
print *
print *,” Dien tich hinh chu nhat co &
& gia tri la : “, dientich
read*
end program ChuNhat ! Co the viet don gian la end

Ghi chú :

10
a.Trong câu lệnh print ‘(a,$)’, ” Vao gia tri canh x : “ , xâu kí tự ‘(a,$)’ là phần định dạng
cho đối tượng xuất ra gồm một xâu kí tự ( kí hiệu định dạng là chữ a ) và sau đó giữ con trỏ không
xuống dòng ( kí hiệu điều khiển là $ ) ,và đợi nhập một giá trị trực tiếp từ lệnh read*, x tiếp theo.
b.Trong trường hợp đơn giản chỉ có chương trình chính ( main program ) , chỉ thị kết thúc
chương trình có thể viết đơn giản là end .
c. Mỗi câu lệnh trong Fortran đều bắt đầu bằng một từ khóa , ngoại trừ câu lệnh gán hoặc
câu lệnh bắt đầu bằng một nhãn ( label ) . Cuối một câu lệnh không có dấu chấm phẩy .
d. Bên trái phép gán, chỉ được phép viết duy nhất một tên biến .

1.6.2 Thực hành biên dịch , liên kết và chạy chương trình trong môi trường Force.
Sử dụng các lệnh trong menu Run để biên dịch, liên kết ( phím tắt Ctrl – F9 ) và chạy
chương trình ( phím tắt F9) .

1.7 Sử dụng trình biên dịch Fortran G95 ( phần tham khảo ) .
1.7.1 Cài đặt .
+ Chạy file g95-Mingw_201210.exe để cài đặt G95. Khi cửa sổ g95_Mingw_Installer
Setup : Installation Folder hiện ra , chúng ta chọn thư mục để lưu chương trình g95 trong ô
Destination Folder , ví dụ C : \ g95 hoặc E :\g95 . Dung lượng yêu cầu của G95 : 16,1 MB . Bấm
nút Install để cài đặt .
+ Trong quá trình cài đặt , chúng ta bấm nút Yes hay OK theo đề nghị của chương trình cài
đặt ( đặt các biến PATH , cài đặt các tiện ích, thư viện …).
+ Khi cài đặt thành công , thư mục g95 sẽ có 3 thư mục con là bin , doc, lib và file uninstall-
g95 . Chương trình chính g95 nằm trong thư mục bin .
1.7.2 Sử dụng G95.
+ Sau khi soạn xong tập tin nguồn chunhat.f90 ( sử dụng Notepad) và lưu tại thư mục
d:\work . Để biên dịch và liên kết tập tin này thành tập tin thi hành có tên chunhat và cũng lưu tại
d:\work , thực hiện như sau :

Hình 1.7 : Các cửa sổ xuất hiện khi cài đặt chương trình G95
11
 Vào cửa sổ lệnh của windows ( command prompt window ) , đánh dòng lệnh như sau :
c:\> g95 –o d:\work\chunhat d:\work\chunhat.f90 , xong nhấn Enter .
g95 : tên chương trình biên dịch .
-o d:\work\chunhat : chỉ định tên file kết quả ( output file , executable file ) gồm cả đường dẫn .
d:\work\chunhat.f90 : tên file nguồn gồm cả đường dẫn.

 Nếu biên dịch và liên kết thành công , chúng ta có file chunhat.exe và thể chạy test
chương trình ngay trong của sổ lệnh ( chạy file output )  c:\> d:\work\chunhat <Enter> .

+ Mẫu các câu lệnh dùng để biên dịch , liên kết :
 Lệnh g95 –c file.f90 : biên dịch file.f90 thành tập tin đối tượng file.o ; có thể biên dịch
nhiều file nguồn cùng một lúc . ( Chữ c : compile ) .
Lệnh g95 –o file file.f90 : biên dịch và liên kết file.f90 thành tập tin thi hành có tên file
Lệnh g95 –o file file01.f90 file02.f90 file03.f90 : biên dịch và liên kết cùng lúc nhiều
file nguồn thành một tập tin thi hành có tên file . ( Chữ o : output )

Hình 1.8 : Biên dịch , liên kết và chạy chương trình trong cửa sổ lệnh của windows.

1.8 Trình biên dịch của Silverfrost FTN95 Personal Edition ( phần tham khảo ) .
Khi chạy file plato.exe xong , cửa sổ môi trường Plato sẽ hiện ra và chúng ta chọn menu
File , chọn tiếp New , cửa sổ New File hiện ra như sau :

Hình 1.9 : Cửa sổ New File .

Trong phần Type , chúng ta chọn Free format Fortran file xong nhấn nút Open . Khung
soạn thảo hiện ra và chúng ta có thể soạn văn bản chương trình , ví dụ chương trình tính thể tích
hình trụ như dưới đây và lưu tập tin có tên hinhtru.f90 . Để biên dịch và liên kết , chúng ta vào
menu Build , chọn Build .
12
Hình 1.10 : Cửa sổ IDE của Plato và vị trí menu Build.
Nếu không có lỗi , trong cửa sổ output phía dưới có nội dung sau :
Compiling and linking file: hinhtru.f90
Creating executable: C:\Users\Administrator\Documents\hinhtru.EXE
Compilation and linking completed.

Sau đó chúng ta chạy chương trình này : chọn menu Build , lệnh Start Run ( Ctrl – F5) .
Một thông báo hiện ra vài giây cho biết về các giới hạn khi sử dụng phiên bản personal
edition của phần mềm này trước khi chương trình chạy thực sự .

Hình 1.11 : Kết quả chạy chương trình hinhtru.f90

Kết quả sẽ hiển thị trên một màn hình màu đen riêng . Sau khi xem xong , nhấn Enter để
đóng cửa sổ này và quay trở về môi trường Plato . Chúng ta cũng có thể copy vào clipboard toàn
bộ nội dung trên cửa sổ màu đen này , cách thực hiện tương tự như trong phần mềm Force .

Trong menu Build còn có lệnh Compile dùng để biên dịch tập tin nguồn thành tập tin đối
tượng .Tất cả các hướng dẫn chi tiết sử dụng phần mềm Plato được trình bày trong menu Help .

1.9 Văn bản chương trình Fortran 90 viết theo kiểu định dạng cố định ( tham khảo ) .
Chúng ta xem ví dụ sau đây về cách viết văn bản chương trình Fortran 90 tính chu vi ,
diện tích hình tròn theo kiểu định dạng cố định ( fixed format ) .

13
Hình 1.12 : Văn bản chương trình circle.f viết theo định dạng cố định .

 Lợi ích : sử dụng , khai thác các văn bản chương trình FORTRAN 77 đã có sẵn , sau đó sử dụng
trình biên dịch Fortran 90 để biên dịch và chạy các chương trình này .
 Quy tắc viết văn bản theo định dạng cố định :
+ Mỗi dòng văn bản chứa 72 kí tự ( từ cột 1 đến cột 72 , được đánh số thứ tự từ trái qua phải ) ,
từ cột 73 trở đi trình biên dịch sẽ bỏ qua .
+ Phân chia các khu vực theo cột như sau :
Khu vực viết câu lệnh : từ cột số 7 đến cột 72 .
Đối với câu lệnh dài cần viết trên nhiều dòng thì mỗi dòng tiếp theo cần đặt dấu * hoặc dấu
& tại cột số 6.
Khu vực đặt nhãn ( label , tối đa 5 chữ số ) cho câu lệnh ( áp dụng cho câu lệnh được gắn
nhãn ) : từ cột số 1 đến cột số 5 .
Dòng có ghi chữ C tại cột số 1 là dòng ghi chú hoặc dòng để trống .
+ Ngoài ra đối với Fortran 90 , khi viết văn bản theo định dạng cố định , được phép sử dụng hai
nội dung bổ sung như sau :
Cho phép viết nhiều lệnh trên cùng một dòng văn bản , dùng dấu chấm phẩy ; để phân cách
câu lệnh .
Cho phép viết nội dung ghi chú sau dấu chấm than !

Lưu ý : văn bản chương trình viết theo định dạng cố định phải được lưu với phần đuôi tập
tin là *.f , ví dụ circle.f , thay vì *.f90 được áp dụng cho văn bản viết theo định dạng tự do như
trình bày ở các phần trên .

14
Hình 1.13 : Kết quả chạy chương trình circle.exe

*****

15
CHƯƠNG 2 : CÁC KIỂU DỮ LIỆU CỦA FORTRAN

2.1 Các kiểu dữ liệu của Fortran .


2.1.1 Khái niệm .
Mỗi đối tượng như biến (variable ) , hằng có tên (named constant) gọi tắt là hằng , mảng
(array) …trong một đơn vị chương trình Fortran đều phải có một tên riêng hay định danh (
identifier) và gắn liền với một kiểu dữ liệu ( data type ) nào đó như số nguyên, số thực , số phức ,
logic, kí tự . Fortran cung cấp năm kiểu dữ liệu đã được định nghĩa sẵn ( built – in ) như sau :
 Integer : kiểu số nguyên .
 Real : kiểu số thực .
 Complex : kiểu số phức , gồm phần thực và phần ảo là các số thực .
 Logical : kiểu logic , chỉ có hai giá trị là đúng ( .true.) hoặc sai ( .false. )
 Character : kiểu kí tự ( gồm một hoặc nhiều kí tự ) .

Các kiểu integer , real , complex thuộc nhóm dữ liệu số ( numeric ) ; các kiểu logical ,
character thuộc nhóm dữ liệu non-numeric .
Đối với mỗi kiểu dữ liệu trên , chúng ta sẽ tìm hiểu cách khai báo biến, hằng , miền giá trị
( range ) cùng kích thước trong bộ nhớ của kiểu dữ liệu , các phép tính có liên quan và kết quả trả
về .
Trên cơ sở các kiểu dữ liệu do Fortran cung cấp , chúng ta còn có thể xây dựng kiểu dữ liệu
do người dùng tự định nghĩa (user defined type ) . Kiểu dữ liệu hỗn hợp này ( tương tự như kiểu
bản ghi trong một số ngôn ngữ lập trình ) bao gồm nhiều thành phần , mỗi thành phần có tên và có
kiểu dữ liệu riêng .
2.1.2 Khai báo biến và hằng .
a. Mẫu chỉ thị khai báo biến :
Tên_kiểu_dữ_liệu :: tên_biến1 , tên_biến2, tên_biến3 ( dấu :: có thể không có ).
b. Mẫu chỉ thị khai báo biến và gán luôn giá trị ban đầu :
Tên_kiểu_dữ_liệu :: tên_biến1 , tên_biến2 = giá trị ( biến2 được gán giá trị
ban đầu . Dấu :: bắt buộc phải có ) .
c. Mẫu chỉ thị khai báo hằng ( named constant ) :
Tên_kiểu_dữ_liệu , parameter :: tên_hằng = giá trị ( dấu :: phải có ) .
Khi khai báo hằng phải dùng từ khóa parameter đi sau tên kiểu dữ liệu .
Ghi chú : + Trong chỉ thị khai báo biến , nếu có gán giá trị ban đầu hay có khai báo thuộc
tính như parameter , dấu :: bắt buộc phải có . Dấu :: luôn luôn phải viết liền nhau .
+ Một biến chưa được gán giá trị ban đầu sẽ có một giá trị không xác định khi chương trình
bắt đầu được thực thi ( unassigned variable ) .

2.2 Dữ liệu kiểu số nguyên ( integer ) .


2.2.1 Tổng quát về kiểu số nguyên .
Kiểu số nguyên dùng thể hiện các số nguyên tương đối trong toán học , bao gồm số nguyên
âm , số nguyên dương . Phạm vi ( range) hay miền giá trị hẹp hay rộng của từng kiểu số nguyên
tùy thuộc vào việc khai báo thêm tham số phụ có liên quan đến kích thước ( tính bằng byte ) của
kiểu dữ liệu trong bộ nhớ .
Kiểu số nguyên mặc định ( default ) được khai báo bằng từ khóa integer , chiếm 4 byte
trong bộ nhớ , và có phạm vi biểu diễn :
-2 147 483 648  n  2 147 483 647

Ví dụ về cách viết giá trị trực tiếp ( literal constant ) kiểu số nguyên integer :
16
-345 0 +1975 2018
Số nguyên dương có thể không cần có dấu + ở trước .
Ngoài cách viết số nguyên theo hệ thập phân thông thường , Fortran còn cho phép viết số
nguyên theo các hệ 2, 8, 16 , sử dụng các kí tự ở đầu là b , o , z như ví dụ sau :
b’1011’ (hệ nhị phân ) o’234’ (hệ bát phân) z’23af’ (hệ thập lục phân)
(Tương ứng với các số 11 , 156 , 9135 trong hệ thập phân ) .
2.2.2 Phân loại và khai báo các kiểu số nguyên :
Các kiểu số nguyên có kích thước 1,2,4,8 byte , có phạm vi thể hiện (range) như sau :

Kiểu số nguyên Phạm vi biểu diễn


1 byte -128..127
2 byte -32 768 ..32 767
4 byte -2 147 483 648 .. 2 147 483 647 ( tương tự kiểu integer mặc định )
8 byte -9 223 372 036 854 775 808 .. 9 223 372 036 854 775 807
Bảng 2.1 : Các kiểu số nguyên và phạm vi biểu diễn.

Trong ngôn ngữ Fortran 90, việc khai báo kiểu số nguyên có chỉ định rõ miền giá trị ( range
) được thực hiện thông qua tham số kind là một số nguyên .Đối với một số trình biên dịch số kind
được gán trực tiếp bằng với số byte ( kích thước kiểu dữ liệu trong bộ nhớ ) và được khai báo như
sau :
integer (kind = 1) :: k ! khai báo biến k là số nguyên 1 byte .
integer (kind = 2) :: m ! khai báo biến m là số nguyên 2 byte .
integer (kind = 4) :: n ! khai báo biến n là số nguyên 4 byte .
integer (kind = 8) :: n8 ! khai báo biến n8 là số nguyên 8 byte .

Lưu ý : trong trường hợp này , để viết giá trị trực tiếp của số nguyên 2 byte , ví dụ 213 ,
chúng ta viết như sau : 213_2 ( số nguyên , dấu nối chân , số kind ) . Nếu viết 213 thì đây là số
nguyên kiểu integer mặc định ( 4 byte ) . Để kiểm tra , chúng ta dùng hàm kind (213) và kind
(213_2) sẽ thấy kết quả trả về lần lượt là 4 và 2 ( print *, kind(213) , kind(213_2) ) .
Chúng ta có thể khai báo số kind thông qua một hằng ik , sau đó gán kind = ik như sau :
integer, parameter :: ik = 2 ! khai báo hằng nguyên ik có giá trị bằng 2 .
integer (kind = ik ) :: m,n = 15_ik ! khai báo hai biến nguyên m,n có số kind
! là ik = 2 , biến n được gán giá trị đầu là 15_2
! phạm vi biểu diễn : -32768 .. 32767

Khai báo số kind thông qua một hằng có ưu điểm là dễ chỉnh sửa phạm vi ( miền giá trị )
của biến cho phù hợp với yêu cầu của bài toán ( chỉ cần thay đổi giá trị hằng ik ) .

Tuy nhiên không bắt buộc tất cả các trình biên dịch Fortran đều quy định số kind bằng với
số byte như trên . Một số trình biên dịch quy định số kind riêng cho từng kiểu số nguyên (chúng ta
cần tham khảo sổ tay của trình biên dịch cụ thể hoặc chạy test , dùng hàm kind ( ) , để in ra số
kind của các kiểu dữ liệu ) .Trong trường hợp này , chương trình nguồn viết bằng Fortran có thể
không tương thích khi chạy trên các trình biên dịch khác nhau khi chúng ta gán số kind bằng một
giá trị cụ thể như trên .
Để giải quyết tính khả chuyển ( portable ) về phạm vi thể hiện đối với dữ liệu kiểu số
nguyên giữa các trình biên dịch khác nhau ( văn bản chương trình tương thích với các trình biên
dịch khác nhau ) , Fortran cung cấp hàm đã được định nghĩa sẵn selected_int_kind ( r ) dùng để
cung cấp số kind cho kiểu số nguyên có phạm vi thay đổi từ – 10r đến +10r do người lập trình yêu
17
cầu . Ví dụ kiểu số nguyên có phạm vi thay đổi từ -104 đến +104 ( -10 000  n  10 000 ) được
khai báo như sau để lấy số kind phù hợp :

interger ,parameter :: ikind = selected_int_kind (4) ! lấy số kind qua hằng ikind.
integer (kind = ikind ) :: m,n ! khai báo biến nguyên có số kind = ikind .

Lưu ý : nếu miền giá trị của biến do người lập trình yêu cầu không được đáp ứng , hàm
selected_int_kind ( ) sẽ trả về giá trị – 1.
2.2.3 Ví dụ .
Để làm rõ các nội dung trên, chúng ta xem qua chương trình testint.f90 , chạy trên phần
mềm Force 2.0 , như sau ( Trong Force , số kind được gán bằng số byte ) :

program testint
implicit none
integer :: n ! khai bao so nguyen mac dinh
integer (kind = 2) :: n2 ! khai bao so nguyen 2 byte
integer (kind = 4 ):: n4 ! so nguyen 4 byte
integer (kind = 8 ):: n8 ! so nguyen 8 byte
! Lay so kind kieu so nguyen theo mien gia tri yeu cau
integer, parameter :: ikind = selected_int_kind (4) ! pham vi -104.. +104 .
integer (kind = ikind) :: m
! Phan thuc thi
! in ra cac gia tri lon nhat cua tung kieu so nguyen
print*,’Max so nguyen mac dinh :’, huge(n)
print*,’Max so nguyen 2 byte :’, huge(n2)
print*,’Max so nguyen 4 byte :’, huge(n4)
print*,’Max so nguyen 8 byte :’, huge(n8)
print*,’So kind cua m : ‘, ikind
print*,’Max cua kieu so nguyen cua m : ‘, huge(m)
! Phep chia hai so nguyen
print*,’Phep chia 14/4 = ‘ , 14/4
read* ! Tam ngung de xem ket qua
end program testint

Kết quả :
Max so nguyen mac dinh : 2147483647
Max so nguyen 2 byte : 32767
Max so nguyen 4 byte : 2147483647
Max so nguyen 8 byte : 9223372036854775807
So kind cua m : 2
Max cua kieu so nguyen cua m : 32767
Phep chia 14/4 = 3

Qua chương trình nêu trên ,chúng ta nhận xét :


1.Hàm huge (n) trả về số nguyên lớn nhất của kiểu dữ liệu của đối số n .
2.Số kind của biến m ( khai báo phạm vi thể hiện từ -104 đến 104 ) bằng 2 , có kiểu đồng
nhất với kiểu số nguyên integer 2 byte .

18
3. Phép chia hai số nguyên 14/4 = 3 cho kết quả là một số nguyên là phần nguyên của phép
chia . ( Lưu ý : 1/3 cho kết quả là số 0 ) . Đây là một đặc điểm cần chú ý khi chia hai số nguyên
trong Fortran .Nếu muốn lấy phần dư ( dư số ) của phép chia hai số nguyên m/n , chúng ta dùng
hàm mod (m,n) , ví dụ mod (14,4)  2
4. Hàm kind (m) trả về số kind của m . Ví dụ : kind (15_2) → 2 , kind (15)  4

Chúng ta xem chương trình sau dùng để in ra số kind của kiểu số nguyên mặc định và số
nguyên có phạm vi thể hiện từ -104 đến 104 , chương trình này chạy trên phần mềm FTN95 của
Silverfrost ( đã giới thiệu tại chương 1 ) .
program testint_kind
implicit none
integer :: n = 12 , k ! so nguyen mac dinh
k = selected_int_kind(4) ! so nguyen co pham vi tu -104 den 104
print *,'So kind cua kieu integer : ', kind(n)
print *,'So kind cua kieu integer -10**4 den 10**4 : ', k
end
Kết quả :
So kind cua kieu integer : 3
So kind cua kieu integer -10**4 den 10**4 : 2

Nhận xét : số kind của kiểu số nguyên mặc định trong FTN95 là 3 , khác với số kind ( bằng
4 ) của phần mềm Force 2.0 ở trên .
Lưu ý : số kind của phần mềm FTN95 đối với số nguyên 1,2,4,8 byte lần lượt là 1,2,3,4 .

Khi tính toán với các số nguyên , cần kiểm soát giá trị thể hiện lớn nhất và nhỏ nhất của
kiểu dữ liệu , nếu kết quả tính toán ( trung gian hoặc cuối cùng ) vượt ra ngoài miền này , kết quả
trả về là một giá trị không phù hợp.
Ví dụ : trong phạm vi số nguyên 2 byte ( giá trị max = 32767 ), nếu m = 15000 và n = 20000
thì kết quả tổng k = m + n sẽ có giá trị trả về là -30536 thay vì 35000 ( do kết quả vượt quá giá trị
max của kiểu dữ liệu ) .

2.3 Dữ liệu kiểu số thực ( real ) .


2.3.1 Tổng quát về kiểu số thực .
Kiểu số thực dùng thể hiện các số thực âm, số thực dương thuộc tập hợp các số thực R trong
toán học . Hai kiểu số thực thông dụng được các trình biên dịch hỗ trợ là số thực có độ chính xác
đơn ( simple precision ) và số thực có độ chính xác kép ( double precision ) .
2.3.2 Kiểu số thực có độ chính xác đơn .
Đây là kiểu số thực mặc định , được khai báo với từ khóa real , chiếm 4 byte trong bộ nhớ
.
Miền giá trị về giá trị tuyệt đối :
1.2 E-38 ( 1.2 10-38 )  |x|  3.4 E+38 ( 3.4 10+38 ) ( giá trị gần đúng )
Số chữ số thập phân có nghĩa : 6 hoặc 7 chữ số .

Cách viết giá trị trực tiếp ( literal constant ) số thực kiểu real :
a) Viết dưới dạng thông thường (dạng dấu chấm tĩnh ) gồm phần dấu ( dấu + có thể bỏ ) ,
phần nguyên , dấu chấm ( thay cho dấu phẩy theo kiểu viết số Việt Nam , bắt buộc phải có ) , phần
lẻ thập phân .

19
Ví dụ :
-123.34 -10. 0. .35 4.0 +152.18
b) Viết dưới dạng dấu chấm động , hay dạng bậc (lũy thừa cơ số 10) , dùng kí tự E hay e .
Dạng mExx  m*10xx , trong đó m là phần định trị (gồm dấu  , phần nguyên , dấu chấm, phần
lẻ thập phân ) , phần mũ xx gồm phần dấu và một số nguyên có hai chữ số nằm trong miền giá
trị cho phép . Nếu phần định trị 0.1  m < 1. thì số thực được gọi là chuẩn hóa .
Ví dụ : 2.25E+04  2.25e+4  0.225e5  225e+02  22500.
-1.5E-01  -15e-02  - 0.15
-123.45  -0.12345E+3 (dạng chuẩn hóa )

Khi cần sử dụng số thực có độ chính xác cao hơn ( số chữ số thập phân thể hiện lớn hơn 7 )
, chúng ta sử dụng kiểu số thực có độ chính xác kép hoặc độ chính xác gấp bốn .

2.3.3 Kiểu số thực có độ chính xác kép .


Số thực có độ chính xác kép được khai báo với từ khóa double precision , chiếm 8 byte
trong bộ nhớ hoặc thông dụng hơn ( từ Fortran 90 trở về sau ) là khai báo thông qua số kind phù
hợp với trình biên dịch , ví dụ real ( kind = 8 ) . Trong trường hợp này , số kind được gán bằng
số byte ( kích thước của kiểu số thực trong bộ nhớ ) .
Miền giá trị về giá trị tuyệt đối :
2.2 D-308 ( 2.2 10-308 )  |x|  1.8 D+308 ( 1.8 10+308 ) ( giá trị gần đúng )
Số chữ số thập phân có nghĩa : 15 hoặc 16 chữ số .

Cách viết giá trị trực tiếp số thực kiểu double precision :
Chỉ được viết dưới dạng bậc ( lũy thừa cơ số 10 ) và dùng kí tự D hay d thay cho kí tự E
hay e trong cách viết cho kiểu số thực độ chính xác đơn . Ví dụ :
4.25D+04  4.25d4  425d+02 ( 42500.)
-2.5D-01  -25d-02 ( - 0.25 )
-36.25  -0.3625D+2 ( dạng chuẩn hóa )
Số thực độ chính xác kép 3.14159  3.14159d0 ( do 100 = 1 ) .

Tuy nhiên , từ Fortran 90 trở về sau , giá trị trực tiếp kiểu số thực chính xác kép thường
được viết dưới dạng kèm theo số kind phù hợp như trình bày dưới đây .
Ghi chú : Do sự chuyển đổi giữa hệ thập phân ( người sử dụng ) và hệ nhị phân ( máy tính
) nên dữ liệu kiểu số thực chỉ thể hiện gần đúng số thực ( do sai số cắt cụt : truncation error ) . Độ
chính xác của kiểu số thực ( số chữ số thập phân có nghĩa được thể hiện ) sẽ tùy thuộc vào kích
thước khai báo của kiểu số thực được sử dụng trong bộ nhớ .
2.3.4 Khai báo kiểu số thực có kèm tham số kind .
Từ Fortran 90 trở về sau , để chỉ rõ độ chính xác ( precision ) và miền giá trị ( range ) của
một kiểu số thực , một số trình biên dịch dùng tham số kind , là một số nguyên , và gán số kind
này bằng với số byte , cách khai báo như sau :
real (kind = 4) :: x ! khai báo số thực độ chính xác đơn , giống kiểu real mặc định.
real (kind = 8) :: y ! khai báo số thực độ chính xác kép (double precision ).
real (kind = 16) :: z ! số thực độ chính xác gấp bốn ( quadruple precision) .

Lúc này , các giá trị trực tiếp đối với số thực , ví dụ số thực độ chính xác kép được viết có
kèm theo phần đuôi nhận dạng gồm dấu nối chân (underscore) và số kind (là số 8 ) . Ví dụ : theo
khai báo như trên , số thực độ chính xác kép 2.45 được viết : 2.45_8 ( giá trị_số kind ) . Nếu viết

20
2.45 thì đây là số thực độ chính xác đơn ( kiểu mặc định ) . Chúng ta có thể dùng hàm kind (2.45)
và kind (2.45_8) để in ra số kind tương ứng với kiểu số thực .

Để có thể dễ dàng chuyển đổi kiểu số thực trong chương trình , chúng ta khai báo số kind
thông qua một hằng nguyên như sau :
integer , parameter :: r = 8 ! khai báo hằng nguyên r = 8
real ( kind = r ) :: x, y = 2._r ! khai báo số thực có kind = r , độ chính xác kép.
( Biến y = 2._r được gán luôn giá trị ban đầu với số kind = r ) .

Tuy nhiên không phải tất cả các trình biên dịch Fortran đều quy định số kind bằng với số
byte như trên . Chúng ta cần tham khảo sổ tay của trình biên dịch hoặc chạy một chương trình test
, sử dụng hàm kind ( ) , để biết số kind tương ứng với từng kiểu số thực . Ví dụ chúng ta cho chạy
dòng lệnh : print*, kind (1.), kind(1.d0) để tìm ra số kind của kiểu số thực chính xác đơn và chính
xác kép .
Việc gán số kind bằng một giá trị cụ thể có thể phù hợp với trình biên dịch này nhưng lại
không phù hợp với trình biên dịch khác . Ví dụ đối với trình biên dịch FTN95 của Silverfrost , số
kind của kiểu số thực chính xác đơn là 1 , kiểu chính xác kép là 2 .
Để đảm bảo tính khả chuyển ( portable ) của văn bản chương trình Fortran giữa các trình
biên dịch khác nhau , Fortran cho phép lấy số kind ( là một số nguyên) của kiểu dữ liệu số thực
qua hàm định nghĩa sẵn selected_real_kind (p,r) trong đó đối số p (precision) quy định độ chính
xác ( số chữ số thập phân có nghĩa ) và đối số r (range) quy định miền giá trị của kiểu dữ liệu nằm
trong khoảng (-10r , -10-r )  (10 -r , 10r ) . Ví dụ :

+ Đối với kiểu số thực có độ chính xác đơn , khai báo như sau :
integer , parameter :: rs = selected_real_kind (6,37) ! lấy số kind qua hằng rs
! p = 6 và r = 37 , phạm vi ± (10-37 đến 1037)
real (kind = rs) :: x, y = 2._rs ! khai báo số thực có kind = rs , chính xác đơn .
Hoặc chúng ta chỉ cần khai báo số chữ số thập có nghĩa p trong hàm selected_real_kind :
integer , parameter :: r6 = selected_real_kind (6) ! lấy số kind qua hằng r6
real (kind = r6) :: x, y = 2._r6 ! số thực có 6 chữ số thập phân có nghĩa , tương đương
! số thực chính xác đơn .
+ Đối với số thực có độ chính xác kép :
integer , parameter :: rd = selected_real_kind (15,307)
real (kind = rd) :: x, y = 2.34_rd ! khai báo số thực có kind = rd ,chính xác kép.
Hoặc chúng ta chỉ cần khai báo tham số đầu tiên p , xác định số chữ số thập có nghĩa :
integer , parameter :: r15 = selected_real_kind (15) ! lấy số kind số thực có số chữ số
! thập phân có nghĩa là 15
real (kind = r15) :: x, y = 2.34_r15 ! khai báo số thực có kind = r15 ,chính xác kép.

+ Đối với số thực độ chính xác gấp bốn ( quadruple precision ) :
integer , parameter :: rq = selected_real_kind (33,4931)
real (kind = rq) :: x, y = 3.4567_rq
Hoặc chỉ khai báo tham số p = 33 :
integer , parameter :: r33 = selected_real_kind (33)
real (kind = r33) :: x, y = 3.4567_r33

21
Một cách tổng quát, nếu trong chương trình , chúng ta cần hai biến số thực x,y có độ
chính xác là p = 12 chữ số có nghĩa và phạm vi thể hiện ( r = 50) từ ± (10 -50 đến 1050 ),
lúc này khai báo như sau :
integer , parameter :: rc = selected_real_kind (12,50) ! lấy số kind qua hằng rc
real (kind = rc) :: x, y=3.14_rc ! khai báo số thực có kind = rc .
print*,’So kind r :’, rc ! in ra số kind .
print*,kind (x) !  rc
Trong trường hợp cụ thể này ( p = 12, r = 50) , số kind sẽ có giá trị bằng 8 ( nếu sử dụng
phần mềm Force ) , đồng nhất với kiểu số thực có độ chính xác kép .
Hàm selected_real_kind ( ) sẽ trả về giá trị -1 nếu yêu cầu về độ chính xác của kiểu số thực
không được trình biên dịch hỗ trợ , trả về giá trị -2 nếu phạm vi thể hiện không được hỗ trợ .
Chúng ta cần nắm vững việc sử dụng hai hàm selected_int_kind ( ) và selected_real_kind (
) để lấy số kind phù hợp với kiểu dữ liệu ( độ chính xác , phạm vi biểu diễn ) theo yêu cầu của bài
toán cần giải quyết .
2.3.5 Ví dụ .
+ Chúng ta xem chương trình realtest.f90 về phép nhân hai số thực có độ chính xác đơn và
chính xác kép như sau :
program realtest
implicit none
real :: u,v,w ! khai bao so thuc chinh xac don ( mac dinh )
real (kind = 8) :: x,y,z ! khai bao so thuc chinh xac kep
u = 2.34567 ; v = 3.45678
print*, u,v
x = 2.34567_8 ! gia tri truc tiep so thuc chinh xac kep
y = 3.45678_8 ! viet co so kind di kem
print*,x,y
w = u*v ; z = x*y
! Ket qua phep nhan hai so thuc
print*,’Ket qua nhan so thuc chinh xac don :’
print’(e20.12)’,w
print*,’Ket qua nhan so thuc chinh xac kep :’
print’(d20.12)’,z
read*
end program realtest
Kết quả :
2.34567 3.45678
2.34567 3.45678
Ket qua nhan so thuc chinh xac don :
0.810846519470E+01 ( chính xác chỉ đến 8 số thập phân : 0.81084651 )
Ket qua nhan so thuc chinh xac kep :
0.810846514260D+01 ( chính xác )
Kết quả trên cho thấy chúng ta cần chọn kiểu số thực phù hợp ( tương ứng với kích thước
kiểu dữ liệu , khai báo qua số kind ) khi có yêu cầu tính toán với độ chính xác cao .
Ghi chú : Trong lệnh print , kí hiệu định dạng ‘(d20.12)’ quy định số thực độ chính xác kép
được thể hiện ( viết ra ) theo dạng bậc trong 20 ô (kí tự ) trong đó phần lẻ thập phân ( thuộc phần
định trị ) chiếm 12 ô. Tương tự cho kí hiệu ‘(e20.12)’ áp dụng cho số thực độ chính xác đơn .
+ Chúng ta xem tiếp chương trình realtest2.f90 , lấy số kind qua hàm selected_real_kind (
p,q) như sau :
22
program realtest2
implicit none
integer,parameter :: rs = selected_real_kind(6,37) ! lay so kind do chinh xac don
integer,parameter :: rd = selected_real_kind(15,307) ! so kind do chinh xac kep
integer,parameter :: rc = selected_real_kind(15,50) ! so kind theo yeu cau
real (kind = rs) :: x ! khai bao so thuc chinh xac don
real (kind = rd) :: y ! so thuc chinh xac kep
real (kind = rc) :: z ! so thuc co do chinh xac va mien gia tri theo yeu cau
! In so kind rs,rd,rc
print*,’So kind : ‘
print*,rs,rd,rc
! In mien gia tri Min Max cua kieu du lieu .
print*,’Mien gia tri min max :’
print’(2e20.8)’, tiny(x), huge(x)
print’(2e20.8)’, tiny(y), huge(y)
print’(2e20.8)’, tiny(z), huge(z)
read*
end program realtest2
Kết quả :
So kind :
488
Mien gia tri min max :
0.11754944E-37 0.34028235E+39 ! kiểu số thực chính xác đơn.
0.22250739-307 0.17976931+309 ! kiểu số thực chính xác kép.
0.22250739-307 0.17976931+309 ! kiểu số thực theo yêu cầu.

Trong chương trình trên chúng ta có sử dụng hàm tiny(x) và huge(x) để lấy giá trị nhỏ nhất
và lớn nhất của kiểu dữ liệu của x ( về giá trị tuyệt đối ) .
Lệnh print ‘(2e20.8)’ có phần định dạng kiểu bậc cho 2 số thực thể hiện ra màn hình theo
kiểu chính xác đơn , mỗi số thực chiếm độ rộng 20 ô trong đó phần lẻ thập phân chiếm 8 ô .
Chúng ta thấy số kind của kiểu dữ liệu số thực lấy qua hàm selected_real_kind (15,50) với
yêu cầu 15 chữ số có nghĩa và miền giá trị ( về trị tuyệt đối ) thể hiện từ 10-50 đến 1050 là 8, bằng
với số kind của kiểu số thực có độ chính xác kép .

Khi tính toán với kiểu dữ liệu số thực cần chú ý đến miền giá trị thể hiện của kiểu dữ liệu ,
nếu kết quả tính toán ( trung gian hoặc sau cùng ) vượt quá giới hạn trên ( về trị số tuyệt đối ) sẽ
có lỗi overflow ( tràn số ) , kết quả trả về là Inf ( Infinity ) , hoặc nhỏ hơn giới hạn dưới ( underflow)
kết quả trả về là số 0. Hình vẽ dưới đây thể hiện trục số thực và các miền giá trị của kiểu dữ liệu
số thực (các đoạn vẽ nét đậm ) .

-Inf 0 min max +Inf

Hình 2.1 : Trục số thực và miền giá trị kiểu dữ liệu số thực .

23
2.4 Dữ liệu kiểu số phức ( complex ) .
2.4.1 Tổng quát về kiểu số phức .
Một dữ liệu kiểu số phức bao gồm hai số thực là phần thực và phần ảo . Các số thực trong
thành phần tạo nên số phức thông thường có kiểu mặc định real ( độ chính xác đơn ) hoặc real
(kind = 8 ) tương ứng với số thực độ chính xác kép .
Cách viết giá trị trực tiếp kiểu số phức :
(2.5 , 3.e+1) thể hiện số phức 2.5 + 30.i ; (0. , 1.) thể hiện số i , đơn vị ảo i2 = -1

Lưu ý : giá trị trực tiếp phần thực và phần ảo được viết trong dấu ngoặc đơn và phân cách
bởi dấu phẩy :
z = ( phần_thực , phần_ảo ) .
2.4.2 Khai báo kiểu số phức.
Kiểu số phức được khai báo với từ khóa complex , đây là kiểu số phức mặc định có phần
thực và phần ảo là các số thực kiểu real mặc định .
Ví dụ : khai báo biến, hằng kiểu số phức có thành phần thực và ảo có kiểu số thực độ chính
xác kép :
integer, parameter :: r = 8 ! khai báo hằng nguyên r = 8
complex ( kind = r ) :: z ! khai báo biến z kiểu số phức có số kind r = 8
! tương ứng với phần thực , phần ảo là số thực có độ chính xác kép.
complex , parameter :: i = (0._r,1._r) ! khai báo hằng số phức i , đơn vị ảo.
z = (1.2_r, 0.5_r)*i ! kết quả (-0.5 , 1.2 )

Chúng ta xem chương trình sau đây về kiểu số phức mặc định :
program testcom
implicit none
integer,parameter :: r = 4 ! khai bao hang r
complex( kind = r) :: z ! khai bao bien phuc z co so kind = 4
! phan thuc, phan ao la so thuc kieu real mac dinh.
complex, parameter :: i = (0._r,1._r) ! khai bao hang so phuc i , don vi ao.
z = (1.2_r,0.5_r)*i ! nhan hai so phuc
print*,’So phuc z : ‘ , z
print*,’Phan thuc : ‘,real(z) ! ham lay phan thuc
print*,’Phan ao : ‘,aimag(z) ! ham lay phan ao
print*,’So lien hop : ‘,conjg(z) ! ham lay so phuc lien hop
print ‘(a,f8.4)’,’Modun : ‘,abs(z) ! ham lay modun so phuc
read*
end program testcom

Kết quả :
So phuc z : (-0.5,1.2)
Phan thuc : -0.5
Phan ao : 1.2
So lien hop : (-0.5,-1.2)
Modun : 1.3000

Nếu muốn chuyển chương trình trên sang làm việc với số phức có kind = 8 , chúng ta chỉ
cần thay đổi giá trị của hằng r = 8 ở câu lệnh khai báo hằng .

24
Các hàm chuẩn của Fortran trong chương trình trên :
Hàm real(z) trả về phần thực của số phức z .
Hàm aimag(z) trả về phần ảo của số phức z .
Hàm conjg(z) trả về số phức liên hợp z = a – ib của số phức z = a + ib .
Hàm abs(z) trả về mô-đun ( suất ) của số phức z .
Kí hiệu f8.4 là kiểu định dạng cho số thực với dấu chấm tĩnh có độ dài là 8 ô trong đó phần
lẻ thập phân chiếm 4 ô. Số được căn lề phải .

+ Số phức z còn được gán thông qua hàm cmplx (x,y ) , trong đó x,y là các số thực kiểu real
mặc định ( x,y có thể là các biểu thức số học ) :
complex :: z ! khai báo số phức z kiểu mặc định .
real :: x = 1. , y = 3.
z = cmplx(x,y) ! z = (1.,3.)
z = cmplx(x/2.,y/3.) ! z = (0.5,1.)
+ Trường hợp x,y là các số thực có độ chính xác kép , chúng ta sử dụng hàm dcmplx (x,y)
để tạo số phức có kind = 8  z = dcmplx (x,y) ! khai báo complex (kind = 8) :: z

2.5 Dữ liệu kiểu logic ( logical ) .


Biến thuộc kiểu logic được khai báo với từ khóa logical , chiếm 4 byte trong bộ nhớ ( kiểu
logical mặc định ) , và miền giá trị chỉ có hai giá trị là .false. ( sai ) và .true. ( đúng ) . Lưu ý cách
viết các giá trị false hoặc true phải có dấu chấm đứng trước và sau .
Cách khai báo biến kiểu logical :
logical :: test = .false. ! khai báo biến test kiểu logical , được gán giá trị ban đầu .false.

Cách khai báo hằng kiểu logical :


logical , parameter :: test = .true. ! hằng test có giá trị .true.

Chúng ta xem chương trình testlogic.f90 như sau :


program testlogic
implicit none
logical :: test1 = .false. , test2 = .true. ! khai bao hai bien logical mac dinh
logical (kind = 1) :: test ! khai bao bien logical kieu 1 byte
print*,test1,’ ‘,test2
test = .true.
print*,test
print*,’So kind cua test1 : ‘, kind(test1)
print*,’So kind cua test : ‘, kind(test)
read*
end program testlogic
Kết quả :
F T
T
So kind cua test1 : 4
So kind cua test : 1
Nhận xét : khi xuất ra màn hình , giá trị .true. được thể hiện bằng chữ T và giá trị .false.
được thể hiện bằng chữ F .
Kiểu dữ liệu logical còn có thể khai báo thêm tham số kind như kiểu logical ( kind = 1)
chiếm 1 byte trong bộ nhớ như trong ví dụ trên .
25
Biến logical thường dùng để lưu kết quả trả về của một phép so sánh hai biểu thức số học ,
hai biểu thức kí tự hoặc kết quả tính toán của các biểu thức logic sử dụng các phép toán logic ( đại
số Boolean ) .

2.6 Dữ liệu kiểu kí tự ( character ) .


2.6.1 Tổng quát về kiểu kí tự .
Biến thuộc kiểu kí tự thể hiện hoặc một kí tự đơn ( single character ) như “A” hoặc một xâu
kí tự ( string of characters ) như “ Hello Viet Nam ! “. Xâu rỗng không có kí tự nào trong cặp dấu
nháy.
Giá trị trực tiếp của xâu kí tự được đặt trong cặp dấu nháy đơn ‘xxx’ hoặc cặp dấu nháy kép
“xxx” có giá trị như nhau. Chiều dài xâu kí tự ( string length ) là số kí tự giữa hai dấu nháy kể cả
khoảng trống ( kí tự trống ) . Xâu rỗng có chiều dài bằng 0 . Chữ hoa, chữ thường trong xâu kí tự
được phân biệt khi xuất ra màn hình hoặc tập tin kết quả.
Lưu ý khi cần thể hiện câu It’s a book :
Nếu dùng dấu nháy đơn : ‘It’’s a book’ . ! có hai dấu ‘ liền nhau sau chữ t .
Nếu dùng dấu nháy kép : “It’s a book” . ! viết bình thường .
Để xuất câu Say “ Hello! ” , chúng ta viết ‘ Say “ Hello! ” ‘ hoặc “ Say ““ Hello! ”” “
Biến , hằng , giá trị trực tiếp kiểu kí tự thường được sử dụng để chứa nội dung phần văn bản
dạng text xuất hiện trong chương trình như thông báo việc nhập , xuất các biến , thông báo nội
dung các phần thực thi chương trình , xâu định dạng (format) kết quả hiển thị , biến lựa chọn dạng
kí tự …
2.6.2 Khai báo kiểu kí tự .
Biến kiểu kí tự được khai báo với từ khóa character có kèm theo tham số len = n , xác
định chiều dài ( cố định ) của biến ( n : số nguyên > 0 , số kí tự tối đa được phép thể hiện kể cả
các kí tự trống của biến , mỗi kí tự chiếm 1 byte trong bộ nhớ ,được đánh số từ 1 đến n , trái qua
phải) . Nếu tham số len = 1 thì không cần phải đưa vào khai báo ( tương ứng với biến kí tự đơn ) .
Trong Fortran , biến kiểu kí tự có chiều dài cố định theo chiều dài đã khai báo qua tham số
len . Nếu nội dung của biến có chiều dài ngắn hơn chiều dài đã khai báo thì sẽ có các khoảng trống
chèn bên phải nội dung của biến cho đủ chiều dài khi viết ra giá trị của biến ; ngược lại nội dung
của biến sẽ bị cắt cụt cho bằng với chiều dài của biến . Chúng ta xem qua 02 chương trình sau về
kiểu kí tự :

program testchar
implicit none
! khai bao bien kieu ki tu
character (len = 8) :: city1,city2 ! chieu dai bien 8 ki tu
character yes_no ! the hien chi mot ki tu don ( len = 1)
city1 = ‘Da Lat’ ! them cac khoang trong ben phai khi viet ra
city2 = ‘Nha Trang’ ! bi cat cut
yes_no = ‘Yes’ ! chi lay ki tu dau Y
print*,’--’//city1//’--‘
print*,’--’//trim(city1)//’--‘
print*,’--’//city2//’--‘
print*,’--’//yes_no//’--‘
print*,len(trim(city1))
print*,’It’’s a book’ Hình 2.2 : Kết quả chương trình testchar.
read*
end program testchar
26
Trong chương trình testchar :
1. Kí hiệu // là phép nối hai xâu kí tự ; ví dụ : “abc”//”de” trở thành “abcde”
2. Hàm trim( string) trả về xâu kí tự đã bị cắt bỏ các khoảng trống ở cuối xâu .
3. Hàm len( bien) trả về chiều dài của biến ( chiều dài của biến khi khai báo).
Lệnh len(trim(bien)) trả về chiều dài của biến đã cắt bỏ các khoảng trống ở cuối .
4. Xâu ‘Da Lat’ khi viết ra màn hình có thêm hai kí tự trống bên phải.
5. Xâu ‘Nha Trang’ bị cắt cụt chữ ‘g’. ( do thiếu chỗ thể hiện ) .

program testchar2
implicit none
character(len = 12) :: dao = “Phu Quoc” , so
print*,’--’//dao//’--‘
print*,len(dao) ! chieu dai bien ki tu dao
print*,’--’//trim(dao)//’--‘ ! bien dao bi cat khoang trong ben phai
print*,len(trim(dao)) ! chieu dai bien bi cat cut khoang trong ben phai
print*,’--’//adjustl(dao)//’--‘ ! dua bien ki tu dao ve sat ben trai
print*,’--’//adjustr(dao)//’--‘ ! dua ve sat ben phai
print*,dao (1:3) ! in 3 ki tu dau
print*,dao(5 : ) ! in tu ki tu thu 5 den cuoi
print*,index(dao,”Quoc”) ! vi tri xau “Quoc”
! trong xau dao .
write (so,*) 36 ! bien ki tu so lay gia tri ‘36’
print*,”So : “//so
read*
end program testchar2
Hình 2.3 : Kết quả chương trình testchar2
Trong chương trình testchar2 :
1.Hàm adjustl( ) đặt biến kí tự về sát bên trái , hàm adjustr( ) đặt biến về sát bên phải .
2.Cách viết các xâu kí tự con , trích từ biến xâu kí tự dao :
dao(1:3) thể hiện 3 kí từ đầu của biến dao  dao( : 3)
dao(5: ) thể hiện từ kí tự thứ 5 đến cuối .
dao (2:2) thể hiện đúng kí tự thứ 2
3. Hàm index (dao,”Quoc”) cho biết vị trí xâu con “Quoc” trong biến dao . Hàm index
(string,substring) trả về số 0 nếu xâu con substring không được tìm thấy trong xâu string .
4. Câu lệnh write (so,*) 36 chuyển đổi số nguyên 36 thành xâu kí tự ‘36’ và gán cho biến
kí tự so .
Một số ghi chú về kiểu kí tự :
a.Chúng ta xem khai báo về hằng kiểu kí tự như sau :
character (len = *), parameter :: ThuDo = “Ha Noi”
Khi đặt len = * , chiều dài của hằng kí tự sẽ được để tự do , trình biên dịch sẽ tự xác định
theo giá trị được gán cụ thể ở cuối câu lệnh . Hàm len(ThuDo) lúc này sẽ có giá trị 6 . Lưu ý , khi
khai báo biến kí tự trong chương trình chính , chúng ta không được khai báo len = * .

b. Khi trích một phần từ xâu kí tự có tên là xaukt , chúng ta dùng cách viết xaukt(m : n)
với m, n là hai ( chỉ ) số nguyên xác định vị trí đầu và cuối , để chỉ xâu kí tự con lấy từ vị trí thứ m
đến vị trí thứ n trong xâu xaukt , kể cả các khoảng trống ( tính từ trái qua phải ) . Nếu m không có
thì xem m = 1 , nếu n không có thì xem n = len(xaukt) .
27
2.7 Dữ liệu kiểu hỗn hợp .
Từ các kiểu dữ liệu cơ sở như đã trình bày ở trên, chúng ta có thể định nghĩa ( xây dựng )
kiểu dữ liệu hỗn hợp gồm nhiều thành phần . Mỗi thành phần có tên và có kiểu dữ liệu riêng . (
Tương tự kiểu bản ghi gồm các trường trong một số ngôn ngữ lập trình ) . Chúng ta xem qua ví dụ
sau :

program main
implicit none
! khai bao kieu du lieu moi co ten la HoSo gom 03 truong ( 03 thanh phan ).
type HoSo
character (len = 20) :: HoTen
integer :: NamSinh
real :: HeSo ! he so luong
end type
! ket thuc khai bao kieu du lieu moi co ten HoSo
type (HoSo) :: nv1 ! khai bao bien nv1 co kieu HoSo
nv1 = HoSo(“Nguyen Van A”,1976 ,5.75) ! gan toan bo du lieu mot lan
nv1%NamSinh = 1976 ! gan rieng truong NamSinh
print*, nv1%HoTen ! viet ra man hinh truong HoTen
print*, nv1%NamSinh ; print*, nv1%HeSo
read*
end program main

Kết quả :
Nguyen Van A
1976
5.75
Lưu ý : để truy xuất một trường , chúng ta viết : tên_biến%tên_trường

2.8 Một số nội dung bổ sung .


2.8.1 Cách khai báo kiểu dữ liệu theo phiên bản FORTRAN 77 ( tham khảo ) .
Do FORTRAN 77 được xem là subset ( một phần ) của Fortran 90 nên các trình biên dịch
Fortran 90 vẫn cho phép sử dụng cách khai báo kiểu dữ liệu theo dạng : tên_kiểu*số_byte . Số
byte thể hiện kích thước của kiểu dữ liệu chiếm trong bộ nhớ . Tuy nhiên , từ sau Fortran 90 , đây
là cách khai báo kiểu dữ liệu không theo chuẩn ( non standard ) mà nên sử dụng kiểu khai báo qua
tham số kind như trình bày ở các phần trên .
 Khai báo kiểu số nguyên :
integer*1 :: m , n = 25_1 ! số nguyên 1 byte , phạm vi -128..127
integer*2 :: m , n = 40_2 ! số nguyên 2 byte , phạm vi -32768..32767
integer*4 :: m , n = 123 ! số nguyên 4 byte , giống kiểu integer mặc định .
 Khai báo kiểu số thực :
real*4 :: x , y = 2.345 ! số thực 4 byte , giống kiểu real mặc định .
real*8 :: x , y = 6.78_8 ! số thực 8 byte , giống kiểu double precision .
real*16 :: x.y = 3.567_16 ! số thực 16 byte , giống kiểu quadruple precision .
 Khai báo kiểu số phức :
complex*8 :: z = (1.5 , 3.6) ! giống kiểu complex mặc định có số kind = 4
! phần thực , phần ảo có kiểu real mặc định.
28
complex*16 :: z = (0._8 , 1._8) ! giống kiểu complex có số kind = 8 .
! phần thực , phần ảo có kiểu double precision.
complex*32 :: z = (0._16 , 1._16) ! giống kiểu complex có số kind = 16 .
! phần thực , phần ảo có kiểu quadruple precision .
 Khai báo kiểu logical :
logical*4 :: test ! kiểu logical 4 byte , giống kiểu logical mặc định .
logical*1 :: test ! kiểu logical 1 byte , giống logical ( kind = 1) .

 Khai báo kiểu kí tự :


character*n :: ten1,ten2 ! xâu kí tự có chiều dài là n , tương đương character ( len = n)
! hoặc character (n) . Mỗi kí tự chiếm 1 byte .
character :: ten ! kiểu kí tự đơn , chiều dài len = 1 .

2.8.2 Một số hàm truy vấn dữ liệu kiểu số thực, số nguyên .
Dưới đây là một số hàm dùng để tra cứu thông tin của kiểu dữ liệu số cũng như lấy số kind
của kiểu dữ liệu số nguyên, số thực theo các yêu cầu của người lập trình ( đã trình bày chi tiết ở
các phần trên ) . Đối số x là số thực .

Bảng 2.2 : Một số hàm truy vấn dữ liệu kiểu số học.

Tên hàm Ý nghĩa

epsilon (x) * Trả về số dương nhỏ nhất khi cộng vào 1 sẽ thành một số lớn hơn 1.

precision (x) Trả về một số nguyên p thể hiện độ chính xác của kiểu dữ liệu : số chữ số
thập phân có nghĩa.
range (x) Trả về một số nguyên r thể hiện phạm vi biểu diễn của kiểu dữ liệu :
-10r  x  10r
tiny (x) Số dương nhỏ nhất của kiểu dữ liệu.

huge (x) Số dương lớn nhất của kiểu dữ liệu.

kind ( ) Lấy số kind ( là số nguyên ) của kiểu dữ liệu số , kiểu logical .
kind (1.)  trả về số kind của số thực chính xác đơn .
kind (1.d0)  trả về số kind của số thực chính xác kép .
selected_int_kind ( r ) Lấy số kind của kiểu dữ liệu số nguyên có phạm vi từ -10r đén +10r

selected_real_kind (p,r) Lấy số kind của kiểu dữ liệu số thực có độ chính xác là p ( số chữ số thập
p,r : nguyên dương . phân có nghĩa ) và có phạm vi thể hiện ( về trị tuyệt đối ) từ 10-r đén 10r.
Ít nhất một trong hai tham số p,r phải được đưa vào danh sách đối số .

* Ghi chú về số epsilon : Bạn đọc có thể in ra các giá trị epsilon cho kiểu số thực chính xác đơn ,
chính xác kép như sau :
print *, epsilon (1.)  0.119209 e -06 ; print*,epsilon (1.d0)  0.222045e-15 ( chỉ lấy 6 số lẻ
thập phân ) . Ta có biểu thức logical (1. + epsilon (1.) > 1.) cho giá trị .true.
Trong lập trình , chúng ta thường thay biểu thức so sánh bằng nhau giữa số thực x với số 0. ( x ==
0.) bằng biểu thức abs(x) < epsilon (1.) , trong đó abs ( ) là hàm lấy giá trị tuyệt đối .

29
2.8.3 Mẫu đầy đủ khai báo biến có kiểu số ( nguyên, thực, phức ) , kiểu logical hoặc
kiểu xâu kí tự ( character ) .
 Mẫu khai báo biến có kiểu số hay kiểu logical :
<Kiểu_dữ_liệu> [, <thuộc_tính>] [::] <danh_sách_biến> [ = <giá_trị_đầu> ]

Ghi chú : + Nội dung trong cặp [ ] có thể không có .


+ Các thuộc tính ( attributes ) đi sau tên kiểu dữ liệu có thể là : parameter , save, intent, pointer,
target, allocatable, dimension , public , private , external, intrinsic , optional . Ngoài thuộc tính
parameter đã biết , các thuộc tính còn lại chúng ta sẽ học dần ở các phần sau .
+ Khi có khai báo thuộc tính hay có gán giá trị ban đầu cho biến , dấu :: bắt buộc phải có .
+ Các biến trong danh sách được viết cách nhau dấu phẩy .
 Mẫu khai báo biến thuộc kiểu xâu kí tự :
character [(len = <chiều_dài_xâu>)] [, <thuộc_tính>] [::] <danh_sách_biến> &
[ = <giá_trị_đầu> ]

2.9 Thực hành .


a. Bạn đọc tự viết chương trình cocbtct.f90 để tính trọng lượng một đoạn cọc bê tông cốt
thép có chiều dài cd = 12 m , mặt cắt hình vành khăn có đường kính ngoài dn = 0m50 , đường
kính trong dt = 0m32 . Trọng lượng riêng của bê tông cốt thép  = 2500 kgf/m3 . Các số liệu cd,dn,dt
được nhập từ bàn phím .Số pi = 3.14159 và trọng lượng riêng của bê tông cốt thép được khai báo
như hằng số .
b. Phần đáp án đề nghị :
program cocbtct
implicit none
real :: dn,dt,cd,P !Khai bao 04 bien so thuc mac dinh.
real , parameter :: pi = 3.14159 , betong = 2500. ! Khai bao 02 hang .
print*,’Cho biet duong kinh ngoai : ‘ ; read*,dn
print*,’Cho biet duong kinh trong : ‘ ; read*,dt
print*,’Cho biet chieu dai coc : ‘ ; read*,cd
P = (pi/4.)*(dn**2 – dt**2) * cd * betong
print*,’Trong luong doan coc P (kgf) : ‘
print ‘(f8.2)’ , P
read*
end program cocbtct

dt
dt
dn
dt cắt cọc BTCT.
Hình 2.4 : Mặt

Hình 2.5 : Kết quả tính toán bài thực hành .

*****

30
CHƯƠNG 3 : PHÉP TOÁN VÀ BIỂU THỨC

3.1 Mở đầu .
Trong các chương trước , chúng ta đã làm quen với phát biểu gán theo dạng :

Tên_biến = Biểu thức

Trước tiên , biểu thức bên vế phải được tính theo quy tắc do Fortran quy định và trả về một
giá trị thuộc một kiểu dữ liệu nào đó , sau đó giá trị này được gán cho biến bên vế trái .
Các loại biểu thức trong Fortran :
 Biểu thức số học : kết quả trả về là một số nguyên , số thực hoặc số phức .
 Biểu thức logic : kết quả trả về là một giá trị kiểu logical , là .true. hoặc .false.
 Biểu thức kí tự : kết quả trả về là một kí tự đơn hoặc một xâu kí tự .
Để thực hiện việc tính một biểu thức , Fortran cung cấp các phép toán số học ( arithmetic
operators) , phép toán so sánh hay quan hệ ( relational operators ) , phép toán logic ( logical
operators ) , phép nối xâu kí tự ( concatenation operator ) .

3.2 Các phép toán số học và biểu thức số học .
3.2.1 Các phép toán số học .
Phép toán số học xuất hiện trong biểu thức số học , thực hiện các phép tính ( operator ) với
các toán hạng ( operand ) có thể là các giá trị trực tiếp , biến, hằng, phần tử mảng ,giá trị trả về của
hàm …. Trong biểu thức số học có thể có các biểu thức số học khác được đặt trong cặp dấu ngoặc
( ) và các hàm . Ví dụ về biểu thức số học :
(-b + sqrt (b**2 – 4.*a*c)) /(2.*a) ; b*b + c*c – 2.*b*c*cos (gocA)
Trong biểu thức ví dụ trên có hàm lấy căn bậc hai sqrt ( ) , hàm cosin cos( ) . Các hàm này
được Fortran cung cấp để sử dụng ngay gọi là intrinsic functions ( danh sách các hàm thông dụng
được liệt kê ở mục 3.6 ) .
 Phép toán hai ngôi :
Phép cộng ( kí hiệu + ) , phép trừ ( - ) , phép nhân ( * ) , phép chia ( / ) .
Lưu ý : 1) Trong phép chia a/b , yêu cầu mẫu số b  0 . Phép chia hai số nguyên m/n cho kết
quả là một số nguyên là phần nguyên của phép chia , ví dụ : 5/3 cho kết quả là 1 ; 1/3 cho kết quả
là số 0 , trong khi phép chia hai số thực 1. /3. cho kết quả là 0.3333 .
2) Hai toán hạng a,b trong phép toán hai ngôi , như a + b , có thể có kiểu hay số kind khác
nhau , lúc này Fortran sẽ áp dụng việc chuyển đổi theo quy tắc sao cho hai toán hạng có cùng kiểu
dữ liệu và có cùng số kind trước khi thực hiện phép tính .
Phép lũy thừa ( kí hiệu ** ) .
Ví dụ : 23 được viết trong Fortran 2**3  8 . Lưu ý với số mũ nguyên âm : 2**(-3) =
1/(2**3) = 1/8 = 0 , do vậy cần viết đúng là 2.**(-3) = 1/8. = 0.12500 . Fortran cho phép viết a**n
với a là một số thực và n là số nguyên dương hoặc âm . Trường hợp a**r , với r là số thực thì số
thực a phải thỏa điều kiện a > 0 , lúc này a**r được tính qua công thức exp(r*ln(a)) có sử dụng
hàm logarit Nêpe và hàm mũ ex ( theo công thức ar = erLna , với a > 0 ) .
 Phép toán một ngôi :
Phép lấy số đối ( kí hiệu - ) , ví dụ : - x . Kí hiệu + cũng có thể xem là phép toán một ngôi .
3.2.2 Quy tắc ưu tiên của các phép toán số học .
Khi tính một biểu thức số học trong đó có các phép toán khác nhau , Fortran quy định mức
độ ưu tiên của các phép toán theo thứ tự từ cao đến thấp như sau :

31
Phép lũy thừa ( ** ) : có mức ưu tiên cao nhất .
Phép nhân ( * ) , phép chia ( / ) : có mức ưu tiên như nhau .

Phép toán một ngôi + và - : có mức ưu tiên như nhau .
Phép toán hai ngôi phép cộng ( + ) , phép trừ ( - ) : có mức ưu tiên như nhau .

Trường hợp trong biểu thức số học có chứa những biểu thức số học khác được đặt trong
cặp dấu ngoặc ( ) , thì giá trị của những biểu thức này được ưu tiên tính trước ( cặp dấu ngoặc
trong cùng nhất sẽ được tính trước ) để trở thành một giá trị đơn . Do vậy , trong trường hợp nghi
ngờ , chúng ta cần dùng thêm các cặp dấu ngoặc để biểu thức trở nên dễ đọc , dễ kiểm soát việc
tính toán . Lưu ý trong một biểu thức , số dấu ngoặc mở phải bằng số dấu ngoặc đóng .
Nếu biểu thức có chứa hàm thì giá trị trả về của hàm cũng được tính trước để trở thành một
toán hạng trong biểu thức .
Đối với các phép toán có mức ưu tiên như nhau như phép nhân , phép chia hoặc phép cộng
,phép trừ , việc tính các phép toán này được thực hiện từ trái qua phải . Riêng phép lũy thừa lại
thực hiện từ phải qua trái .
Ví dụ : 5*4/10  (5*4) /10  2

2**3**2  2** (3**2)  2**9  512


bc / d
Biểu thức : a + b**c / d/ e  a + ( ( (b**c)/d ) / e )  a 
e
z
xy u
Biểu thức : x**y**z*u/v + w  ((x**(y**z) *u)/v) + w  w
v
3.2.3 Quy tắc chuyển đổi kiểu dữ liệu trong biểu thức số học và trong phép gán .
a) Trong phép toán hai ngôi ( + - * / ) , hai toán hạng có thể có kiểu dữ liệu hay số kind khác
nhau . Lúc này trình biên dịch sẽ áp dụng quy tắc chuyển đổi ngầm định của Fortran để hai toán
hạng trở thành đồng nhất về kiểu và số kind trước khi thực hiện phép tính .
Sự chuyển đổi tuân theo quy tắc thứ bậc như sau ( chiều mũi tên là chiều chuyển đổi ) :
Kiểu số nguyên  Kiểu số thực  Kiểu số phức .
Kiểu số thực chính xác đơn  Kiểu số thực chính xác kép .
Như vậy , tùy vào trường hợp cụ thể của biểu thức số học :
+ Các số nguyên sẽ được chuyển đổi thành số thực hoặc số phức .
+ Các số thực được chuyển đổi thành số phức .
+ Các số thực hay số phức được chuyển đổi thành số cùng kiểu nhưng có số kind cao hơn (
dữ liệu có kích thước lớn hơn ) .
Ví dụ : integer*real  real 4*2.0  8.0
real*integer  real 3.0*2  6.0
complex*<integer hoặc real >  complex (2., 1.) *2.  (4. , 2. )
real*real(kind=8)  real (kind=8) 2.*3._8  6. , kind (2.*3._8) = 8
Ghi chú : chúng ta cũng có thể chủ động dùng các hàm chuyển đổi kiểu dữ liệu số , như
hàm real ( ) , để đảm bảo các toán hạng có kiểu mong muốn trước khi tính toán , ví dụ : phép tính
2/5  0 nhưng real (2)/real(5)  0.4 hoặc chỉ cần real(2)/5  0.4
b) Trong một câu lệnh gán , Fortran chấp nhận kiểu của biến bên vế trái có thể khác kiểu
với kiểu của biểu thức bên phải . Biểu thức số học bên phải được tính trước và trả về một giá trị (
kết quả của biểu thức ) có kiểu dữ liệu và số kind phù hợp với các quy tắc của Fortran như đã nêu

32
ở trên . Giá trị này được chuyển đổi theo kiểu dữ liệu và số kind của biến bên trái và sau đó
gán cho biến này . Quy tắc gán này không tuân theo thứ bậc của kiểu dữ liệu số học .
Ví dụ : biến_integer = biểu thức có kiểu real
Giá trị biểu thức bên vế phải ( kiểu số thực ) sẽ bị cắt phần thập phân , sau đó gán cho biến
số nguyên bên vế trái.
Ví dụ : x = 123456789*2 ! print*x  2.4691358e+8 ( biến real x chỉ thể hiện gần đúng
số nguyên 246913578 của vế phải và được làm tròn chữ số cuối 8).

Chúng ta xem chương trình ví dụ sau :


1. program testconver
2. implicit none
3. integer :: m = 5 , n = 2 , p ! biến nguyên .
4. real (kind = 4) :: x ,y ! biến số thực chính xác đơn.
5. real (kind = 8) :: u = 1.25_8 , v ! biến số thực chính xác kép.
6. x = 2.5 + n ! số thực + số nguyên
7. y = x + m/n ! phép chia ưu tiên tính trước
8. print*,x,y
9. print*,kind (x +m/n) ! hiển thị số kind của biểu thức.
10. p = x + m/n ! số thực được gán cho số nguyên bên trái .
11. v = x + u ! số chính xác đơn + số chính xác kép
12. print*,p,v
13. print*,kind(x + u) ! hiển thị số kind của biểu thức.
14. read*
15. end program testconver

Giải thích các quy tắc chuyển đổi của Fortran về kiểu dữ liệu :
+ Dòng số 6 : x = 2.5 + n . Biến nguyên n = 2 được chuyển đổi thành số thực 2. , biểu thức
bên phải trở thành phép cộng hai số thực và trả về số thực 4.5 gán cho biến x .
+ Dòng số 7 : y = x + m/n . Phép chia hai số nguyên m/n ( 5/2 ) , cho kết quả là 2, được
thực hiện trước do phép chia ưu tiên hơn phép cộng . Sau đó số nguyên 2 được chuyển đổi thành
số thực 2. và cộng vào x được kết quả là số thực 6.5 và được gán cho biến y .
+ Dòng số 9 , hàm kind ( x + m/n ) trả về số kind = 4 , chứng tỏ biểu thức trong ngoặc có
kiểu số thực chính xác đơn .
+ Dòng số 10 : p = x + m/n . Kiểu dữ liệu của biến vế trái ( số nguyên ) khác với kiểu dữ
liệu của biểu thức vế phải ( số thực ) . Lúc này kiểu của biểu thức bị ép chuyển đổi theo kiểu của
biến vế trái và sau đó gán cho biến . Biến nguyên p sẽ nhận giá trị là 6 ( sau khi cắt bỏ phần thập
phân của số thực 6.5 , là kết quả của biểu thức bên vế phải ) .
+ Dòng số 13, hàm kind ( x + u ) trả về giá trị kind = 8 , chứng tỏ biểu thức trong ngoặc có
kiểu số thực chính xác kép . Biến số thực chính xác đơn x được chuyển đổi thành số thực chính
xác kép trước khi phép toán + được thực hiện .

c) Quy tắc chuyển đổi kiểu ngầm định như trên có thể làm sai lệch kết quả của chương trình
nếu chúng ta không cẩn thận .Trong thực hành , đối với câu lệnh gán biểu thức số học cho biến ,
chúng ta cần tạo thói quen kiểm soát kỹ kiểu dữ liệu , số kind của từng toán hạng trong biểu thức ,
trình tự thực hiện các phép tính ( dùng thêm các dấu ngoặc khi cần ) , kiểu và số kind của biểu
thức và kiểu , số kind của biến được gán bên vế trái . Trong trường hợp cần sự an toàn , chúng ta
có thể chủ động dùng các hàm chuyển đổi kiểu như real ( ) : đổi số thành số thực mặc định , dble
( ) : đổi số thành số thực có độ chính xác kép .
33
3.3 Biểu thức logic .Các phép toán so sánh ( quan hệ ) . Các phép toán logic .
Biểu thức logic được hình thành từ các toán hạng có kiểu logical ( chỉ có hai giá trị là .true.
hoặc .false. ) . Các toán hạng có thể là biến , hằng , phần tử mảng , giá trị của hàm kiểu logical ,
biểu thức so sánh và có thể được kết hợp với nhau bằng các phép toán logic . Kết quả trả về của
một biểu thức logic là .true. hoặc .false .
3.3.1 Các phép toán so sánh .
Fortran cung cấp sáu phép toán so sánh dùng để so sánh giá trị ( nguyên, thực ) của hai biểu
thức số học với nhau , kết quả trả về của phép so sánh là một giá trị kiểu logical ( .true. hoặc .false.
) . Cách viết phép toán so sánh dạng chữ của FORTRAN 77 , ví dụ .gt. ( greater than ) , vẫn được
sử dụng trong Fortran 90 .

Kí hiệu Kí hiệu theo FORTRAN 77 Ý nghĩa


> .gt. Lớn hơn
>= .ge. Lớn hơn hay bằng
< .lt. Nhỏ hơn
<= .le. Nhỏ hơn hay bằng
== .eq. Bằng
/= .ne. Không bằng ( Khác với )
Bảng 3.1 : Các phép toán so sánh .

Lưu ý : các kí hiệu như <= phải viết liền nhau ( không được viết tách ra < = ). Phép so
sánh bằng nhau phải dùng hai dấu bằng liên tiếp == , trong khi dấu = được sử dụng cho phép gán
. Kiểu dữ liệu số nguyên, số thực ở hai vế có thể khác nhau , Fortran sẽ chuyển đổi thành cùng kiểu
với kiểu dữ liệu có thứ bậc cao hơn trước khi tiến hành so sánh .
Ví dụ : 5.6 <= 3 sẽ có kết quả là .false. ; ( 2**3 +2 ) == 10. sẽ có kết quả là .true.
Đối với số phức , chỉ được sử dụng phép so sánh bằng nhau == và khác nhau /= .
Trong Fortran , xét về mức độ ưu tiên tính toán, các phép toán so sánh có mức ưu tiên như
nhau và được thực hiện sau phép toán số học nhưng trước các phép toán logic .
3.3.2 Các phép toán logic .
Fortran có bốn phép toán logic hai ngôi dùng để kết hợp hai biểu thức logic với nhau và
một phép toán logic một ngôi ( phép phủ định ) tương tự như phép tính đại số Boolean . Kí hiệu
các phép toán logic như sau :

Kí hiệu Ý nghĩa
.not. Phép phủ định .
.and. Phép giao .
.or. Phép hội.
.eqv. Phép so sánh tương đương .
.neqv. Phép so sánh không tương đương . Phép loại
trừ.
Bảng 3.2 : Các phép toán logic .

Cho p, q là hai biểu thức logic , các biểu thức .not.p ( phủ định của p ) , p .and. q ( p và q )
, p .or. q ( p hay q ) , p .eqv. q ( p tương đương q ) , p .neqv. q ( p không tương đương q ; p hoặc
q ) có bảng thực trị ( true table ) như sau :

34
p q .not. p p .and. q p .or. q p .eqv. q p .neqv. q
t t f t t t f
t f f f t f t
f t t f t f t
f f t f f t f
Kí hiệu : t  .true. ; f  .false.
Bảng 3.3 : Bảng thực trị các phép toán logic .
Quy tắc về phép toán logic :
a) .not. p cho giá trị sai khi p đúng và cho giá trị đúng khi p sai .
b) p .and. q chỉ đúng khi p , q cùng đúng , các trường hợp còn lại đều sai.
c) p .or. q chỉ sai khi p , q cùng sai , các trường hợp còn lại đều đúng .
d) p .eqv. q chỉ đúng khi p , q có cùng giá trị ( cùng đúng hoặc cùng sai ) .
e) p .neqv. q chỉ đúng khi p , q có giá trị khác nhau ( p đúng , q sai hay p sai, q đúng ).
Khi muốn viết phép toán phủ định của một biểu thức logic , ví dụ p .and. q , chúng ta phải
sử dụng cặp dấu ngoặc bao lấy biểu thức logic này và viết .not. ( p .and. q ) do phép toán .not. có
mức ưu tiên cao hơn phép .and.
Ví dụ về biểu thức logic : a, b, c là ba số thực > 0 và test là biến logical . Biểu thức logic
trong câu lệnh gán như sau dùng để kiểm tra xem ba số này có thể tạo thành ba cạnh của một tam
giác hay không ? :
test = (a+b > c) .and. (b+c > a) .and. (a+c > b)

3.4 Biểu thức kí tự . Phép nối xâu kí tự . Phép so sánh .


Biểu thức kí tự được hình thành từ các giá trị trực tiếp , biến, hằng , phần tử mảng , giá trị
hàm …có kiểu kí tự ( character ) . Khi viết giá trị trực tiếp một kí tự đơn hay xâu kí tự , chúng ta
dùng cặp dấu nháy đơn hoặc cặp dấu nháy kép bao lấy nội dung cần thể hiện .
Ví dụ : ‘H’ ; “ Nhap cac he so a,b,c : “ .
Lưu ý : Fortran có phân biệt chữ hoa với chữ thường trong xâu kí tự .

+ Phép toán nối hai xâu kí tự liền nhau được kí hiệu là // .
Ví dụ : “Tên : “ // “Nguyen Van A “ sẽ cho kết quả là xâu “ Tên : Nguyen Van A “
‘123’//’456’//’789’  ‘123456789’
+ Hàm repeat (string ,n) sẽ trả về xâu string được viết lặp lại n lần nối tiếp nhau . Ví dụ :
print*, repeat (‘*’, 20) sẽ viết ra màn hình hai mươi dấu * liền nhau .
+ Bảng mã ASCII ( American Standard Code for Information Interchange ) gồm 128 kí tự
( chữ viết, chữ số, kí tự đặc biệt ,kí tự điều khiển …) được sắp xếp theo nhóm và đánh số thứ tự
từ số 0 đến số 127. Khi so sánh hai xâu kí tự có chiều dài khác nhau , xâu ngắn sẽ được chèn thêm
vào các khoảng trống bên phải sao cho hai xâu có chiều dài bằng nhau .Việc so sánh hai xâu kí tự
bằng các phép toán so sánh ( xem 3.3.1 ) được thực hiện bằng cách xét từng cặp kí tự lần lượt từ
trái qua phải và tuân theo thứ tự được quy định trong bảng ASCII . Trường hợp một xâu là xâu kí
tự con của xâu kí tự còn lại thì xâu con được xem là nhỏ hơn .
Ví dụ : biểu thức so sánh ‘Fortran’ < ‘Fortran90’ sẽ cho giá trị là .true. do “Fortran’ là xâu
kí tự con của ‘Fortran90’ .
‘Pascal’ < ‘Fortran’ sẽ cho giá trị là .false. do kí tự ‘P’ có mã ( số thứ tự ) là 80 và kí tự ‘F’
có mã là 70 .
‘Fortran’ > ‘ForTran’ sẽ cho giá trị là .true. do kí tự ‘t’ có mã là 116 và kí tự ‘T’ có mã là
84 .

35
+ Để chuyển đổi một số nguyên ( số thứ tự trong bảng ASCII ) thành kí tự tương ứng chúng
ta dùng hàm : achar (integer) . Ví dụ : achar (70)  ‘F’ .
+ Ngược lại , để lấy số thứ tự của một kí tự trong bảng ASCII chúng ta dùng hàm : iachar
(character) . Ví dụ : iachar (‘P’)  80 .

3.5 Bảng tổng hợp các phép toán và mức độ ưu tiên trong Fortran .
Các phép toán trong Fortran và mức độ ưu tiên khi thực hiện tính toán trong một biểu thức
được tóm tắt trong bảng sau ( được thể hiện từ mức ưu tiên cao đến thấp ) . Khi các phép toán số
học , logic có mức ưu tiên như nhau thì các phép tính được thực hiện từ trái qua phải , trừ phép lũy
thừa và phép .not. được thực hiện từ phải qua trái .

Kiểu Phép toán


Số học ** Lũy thừa
* và / Nhân , chia
+ và - Cộng , trừ ( phép toán một ngôi )
+ và - Cộng , trừ ( phép toán hai ngôi )

Xâu kí tự // Phép nối xâu kí tự


So sánh == , /= , < , <= , > , >=
Logical .not.
.and.
.or.
.eqv. và .neqv.

Bảng 3.4 : Các phép toán trong Fortran và mức độ ưu tiên .

3.6 Một số hàm toán học thông dụng có sẵn trong Fortran ( intrinsic functions ) .
Để có thể viết các chương trình Fortran trong đó có sử dụng các hàm toán học thông dụng ,
chúng ta sử dụng các hàm chuẩn đã được định nghĩa sẵn trong Fortran như sau :

Tên hàm Ý nghĩa Số đối số Kiểu của đối số Kiểu kết quả

abs Hàm lấy giá trị tuyệt đối . 1 I I


R/D R/D
Lấy môđun (suất ) số phức. C R
sqrt Hàm lấy căn bậc 2 1 R/D R/D
y x ,x0
sin Hàm y = sin (x) 1 R/D R/D
-1.0  y  1.0 Đơn vị : radian
cos Hàm y = cos (x) 1 R/D R/D
-1.0  y  1.0 Đơn vị : radian
tan Hàm y = tan (x) 1 R/D R/D
Đơn vị : radian
asin Hàm y = arcsin (x) với 1 R/D R/D
-1.0  x  1.0 Đơn vị : radian
-/2  y  /2

36
acos Hàm y = arccos (x) với 1 R/D R/D
-1.0  x  1.0 Đơn vị : radian
0.  y  
atan Hàm y = arctan (x) với 1 R/D R/D
-/2 < y < /2 Đơn vị : radian

atan2 Hàm  = arctan2 (y,x) với 2 2R/2D R/D


- <    , tan  = y/x Đơn vị : radian
  (Ox, OM ) , M (x,y)

log Hàm y = log (x) 1 R/D R/D


x > 0 ( logarit tự nhiên )
log10 Hàm y = log10 (x) 1 R/D R/D
x > 0 ( logarit thập phân )
exp Hàm mũ y = ex > 0. 1 R/D R/D
( e = 2.71828…)
sinh Hàm sin hyperbolic 1 R/D R/D
(ex – e-x) /2
cosh Hàm cos hyperbolic 1 R/D R/D
(ex + e-x) /2
tanh Hàm tan hyperbolic 1 R/D R/D
sinh/cosh
max Trả về giá trị lớn nhất của n n2 I I
số . R/D R/D
min Trả về giá trị nhỏ nhất của n n2 I I
số . R/D R/D
mod Trả về phần dư của phép 2 2I I
chia hai số nguyên hoặc hai mod(6,4) → 2
số thực . 2R/2D R/D
mod(x,y )  x –int(x/y)*y mod(6.8,3.1) → 0.6
nint Làm tròn đến số nguyên gần 1 R/D I
nhất . nint (2.45) → 2
nint (3.5) → 4
ceiling Làm tròn đến số nguyên gần 1 R/D R/D
nhất , lớn hơn hay bằng đối ceiling (2.45) → 3.
số ( hướng về dương vô cực ceiling (-3.5) → -3.
) . Kết quả là số thực.
floor Làm tròn đến số nguyên gần 1 R/D R/D
nhất ,nhỏ hơn hay bằng đối floor (2.45) → 2.
số (hướng về âm vô cực) floor (-1.2) → -2.
.Kết quả là số thực.
int Đổi số thực thành số nguyên 1 R/D I
( cắt phần lẻ thập phân ).
aint Cắt bỏ phần lẻ thập phân của 1 R/D R/D
số thực.
dble Đổi số thành số thực có độ 1 I/R D
chính xác kép.
Lấy phần thực của số phức. 1 C D
real Đổi số thành số thực chính 1 I/D R
xác đơn .
Lấy phần thực của số phức . 1 C R
37
cmplx Tạo số phức mặc định 2 2I/2R C
dcmplx Tạo số phức có thành phần là 2 2D C
số thực chính xác kép .
aimag Lấy phần ảo của số phức. 1 C R

conjg Lấy số phức liên hợp. 1 C C


sign y = sign(x1,x2) 2 2I/2R/2D I/R/D
y = | x1| nếu x2  0.
y = -|x1| nếu x2 < 0.
Ghi chú về kiểu dữ liệu : I : integer ; R : real ; D : double precision ; C : complex .

Bảng 3.5 : Các hàm toán học thông dụng trong Fortran .

Ghi chú : Trong Fortran 90, nhiều hàm có tính chất generic , nghĩa là một tên hàm ( generic
name ), ví dụ như hàm lấy giá trị tuyệt đối abs ( ) , sẽ tương ứng với các chương trình con khác
nhau ,tùy thuộc vào kiểu của đối số đầu vào mà trả về một giá trị có kiểu phù hợp .
Trong các phiên bản trước , Fortran có các hàm lấy giá trị tuyệt đối có tên khác nhau ( gọi
là specific name ) như iabs (integer ) dùng cho đối số là số nguyên , abs (real) dùng cho số thực ,
dabs(real*8) dùng cho số thực chính xác kép , cabs (complex*8) dùng cho đối số là số phức , trả
về một số thực real là mô đun ( suất ) của số phức .

3.7 Thực hành .


1. Chương trình sau tính các cạnh , chu vi , diện tích và các góc của tam giác vuông OAB .
Các điểm có tọa độ như sau : O (0;0) , A (3;0) ,B(3;4) . Đặt a = OA , b = OB , ab = AB , p = nửa
chu vi , s = diện tích , góc O = góc AOB , góc B = góc OBA .

program tamgiac
implicit none
real :: xa = 3.,ya = 0.,xb = 3., yb =4. ! Toa do diem A , diem B .
real :: a,b,ab,p,s,gocO,gocB
real,parameter :: pi = 3.14159
a = sqrt(xa**2 + ya**2) ! sử dụng công thức tính khoảng cách d  x 2  y 2
b = sqrt(xb**2 + yb**2)
ab = sqrt((xb - xa)**2 + (yb - ya)**2)
print*," Ba canh tam giac : ",a," ",b," ",ab
p = (a + b + ab) /2. ! nua chu vi
print'(a,f8.2)'," Chu vi : ", 2.*p
s = sqrt(p*(p-a)*(p-b)*(p-ab)) ! công thức s  p( p  a)( p  b)( p  ab)
print'(a,f8.2)'," Dien tich : " , s Y
gocO = asin(ab/b)*180./pi ! sử dụng hàm y = asin(x) B (3,4)
gocB = asin(a/b)*180./pi
print'(a,f8.4)'," Goc O (do) : " , gocO
print'(a,f8.4)'," Goc B (do) : " , gocB
read*
A(3,0)
end program tamgiac
X
O (0,0)

38
Kết quả :
Ba canh tam giac : 3. 5. 4.
Chu vi : 12.00
Dien tich : 6.00
Goc O (do) : 53.1301
Goc B (do) : 36.8699

Bạn đọc biên dịch , chạy chương trình trên và xem kết quả . Sau đó phát triển thành
một chương trình cho phép nhập trực tiếp tọa độ 3 điểm A,B,C của một tam giác ABC bất
kỳ , tính các cạnh, các góc , chu vi , diện tích, các đường cao , các trung tuyến , bán kính
đường tròn ngoại tiếp , đường tròn nội tiếp của tam giác .

2. Viết chương trình nhập 5 số thực a,b,c,d,e là điểm số của 5 môn học ( thang điểm 10 )
từ bàn phím .Xuất ra tổng điểm , điểm trung bình , điểm số lớn nhất , điểm số nhỏ nhất.
Áp dụng cho dãy điểm số : 6.5 5.5 7.3 7.8 9.2

program diemso
implicit none
real :: a,b,c,d,e,S
print '(a,$)',"Vao gia tri 5 diem so : "
read*,a,b,c,d,e
S = a+b+c+d+e
print*,"Tong diem : ", S
print '(a,f7.2)'," Diem trung binh :", S/5
print*, "Diem so lon nhat : ",max(a,b,c,d,e)
print*, "Diem so nho nhat : ",min(a,b,c,d,e)
read*
end program diemso

Kết quả :
Vao gia tri 5 diem so : 6.5 5.5 7.3 7.8 9.2
Tong diem : 36.3
Diem trung binh : 7.26
Diem so lon nhat : 9.2
Diem so nho nhat : 5.5

Lưu ý : 1) Khi nhập liệu cho một dãy số trên cùng một hàng , các số phải cách nhau
bằng khoảng trắng hoặc dùng dấu phẩy . Sau khi kiểm tra xong , chúng ta nhấn phím Enter.
2) Giá trị max, min của một dãy số chúng ta dùng hàm max ( danh sách ) , min ( danh
sách ) .
3) Biểu thức S/5 có dạng hỗn hợp real/integer sẽ trả về kiểu real .

3. Chương trình lấy ngày giờ của hệ thống . Trong chương trình này , có câu lệnh :
call date_and_time (dateinfo, timeinfo) , dùng để gọi chương trình Fortran đã có sẵn có tên
date_and_time với hai đối là dateinfo , kiểu character (len = 8), và timeinfo , kiểu character
(len 10) . Các xâu kí tự này chứa kết quả trả về của hệ thống dưới dạng yyyymmdd ( năm-
tháng-ngày) và hhmmss.sss (giờ-phút-giây) . Sau đó chúng ta tách từ các xâu này các thông
tin chi tiết .

39
Bạn đọc phân tích và chạy chương trình sau :

program test_ngay_gio
implicit none
character (len = 8) :: dateinfo ! Xâu chứa thông tin năm, tháng,ngày
character (len = 10) ::timeinfo ! Xâu chứa thông tin giờ,phút,giây
character (len=4) :: year
character (len=2) :: month,day,hour,minute
character (len=6) :: second
call date_and_time (dateinfo,timeinfo) ! Gọi chương trình của Fortran
! Ngay, thang, nam
year = dateinfo(1:4) ! Lấy 4 kí tự đầu tiên cho năm
month = dateinfo(5:6) ! Lấy kí tự thứ 5,6 cho tháng
day = dateinfo(7:8) ! Lấy kí tự thứ 7,8 cho ngày
! Gio,phut,giay
hour = timeinfo (1:2)
minute = timeinfo (3:4)
second = timeinfo (5:10)
print*,day,'-',month,'-',year ! xuất ra dạng ngày-tháng-năm
print*,hour,'-',minute,'-',second ! xuất ra giờ-phút-giây
read*
end

Kết quả :
10-06-2019
07-12-08.643

*****

40
CHƯƠNG 4 : CÁC CẤU TRÚC ĐIỀU KHIỂN
Ngôn ngữ lập trình Fortran cung cấp đầy đủ các cấu trúc điều khiển có khả năng lựa chọn
thực hiện một lệnh hay khối lệnh phù hợp với tình huống xảy ra khi chạy chương trình cũng như
khả năng thực hiện một khối lệnh lặp đi lặp lại nhiều lần cho đến khi một điều kiện nào đó được
đáp ứng .
Ngôn ngữ Fortran 90 có ba dạng cấu trúc điều khiển chính như sau :
 Cấu trúc lựa chọn đơn với câu lệnh if , trong đó lệnh hoặc khối lệnh được thực hiện phụ thuộc
vào một điều kiện logic ( có giá trị đúng hoặc sai ) .
 Cấu trúc lựa chọn đa nhánh với câu lệnh select case .
 Cấu trúc vòng lặp với câu lệnh do .
Mỗi cấu trúc có các dạng cú pháp áp dụng khác nhau tùy theo yêu cầu của bài toán và có
thể được đặt lồng vào nhau ( nested ) , được đặt tên (named) nhằm giúp người đọc văn bản chương
trình dễ dàng theo dõi cấu trúc của chương trình cũng như hướng dẫn chương trình nhảy đến đúng
các vị trí xác định .
Ngoài ra , Fortran còn có các lệnh không tuân theo sự thực hiện tuần tự các câu lệnh trong
chương trình như các lệnh exit , cycle , goto .

4.1 Cấu trúc lựa chọn với câu lệnh if .


4.1.1 Lệnh if dạng đơn giản .
Cú pháp lệnh if dạng đơn giản :
 Trường hợp chỉ có một lệnh :
if ( biểu thức logic ) < câu lệnh được thực hiện nếu bt logic có giá trị đúng . >
Ví dụ : if (x >= 0.) print*, “ x la so duong . “
if ( delta < 0.) print*, “ Phuong trinh vo nghiem . “
 Trường hợp có nhiều câu lệnh ( khối lệnh ) :
if ( biểu thức logic ) then
< câu lệnh 1 >
< câu lệnh 2 >
end if
Ví dụ : if (x>= 0.) then
print*,” x la so duong .”
print*,”So x = “, x
end if
Nếu biểu thức logic có giá trị sai , chương trình sẽ bỏ qua câu lệnh if và thực hiện câu lệnh
tiếp theo trong chương trình .
4.1.2 Cấu trúc lựa chọn dạng if then else end if .
Cấu trúc :
[name :] if (biểu thức logic ) then
< khối lệnh 1 > ! thực hiện nếu biểu thức logic đúng .
else [ name ]
< khối lệnh 2 > ! thực hiện nếu biểu thức logic sai .
end if [ name ]

Chú thích : [name :] , [name] là tên tự đặt cho khối if ( optional ) .
Khi vào khối if , biểu thức logic được xét giá trị , nếu có giá trị đúng thì khối lệnh 1 trong
phần then sẽ được thực hiện ; ngược lại nếu có giá trị sai thì khối lệnh 2 trong phần else sẽ được
thực hiện . Khối if có thể được đặt tên ( không bắt buộc ) , tên tuân theo quy tắc đặt tên biến của
41
Fortran. Ví dụ : chương trình giải phương trình bậc nhất ax + b = 0 sử dụng cấu trúc dạng đầy đủ
if then else end if như sau :

program ptbn
implicit none
real :: a, b, x
print*," Nhap hai so a, b : " ; read*, a, b
if ( a /= 0.) then
x = -b/a
print*,"Nghiem so x = " , x
else
print*," Phuong trinh vo nghiem ." ! truong hop a = 0.
end if
read*
end program ptbn

Ví dụ : chương trình nội suy tuyến tính giữa hai điểm M1(x1,y1) , M2(x2,y2) với x1 < x2 và
điểm M (X,Y) có hoành độ X cho trước thỏa : x1 < X < x2 .Chương trình sẽ tính tung độ Y.

M2(x2,y2) M1(x1,y1)
Y Y M (X,Y)
M(X,Y)
M1(x1,y1) M2(x2,y2)

X X
Trường hợp : y2 > y1 Trường hợp : y2 < y1

program noisuy
implicit none
real :: x1,y1,x2,y2,X,Y
! Nhap toa do hai diem M1(x1,y1),M2(x2,y2)
! Dieu kien x1 < X < x2
print *," Toa do M1:";read*,x1,y1 Toa do M1:
print *," Toa do M2:";read*,x2,y2 1.,4.
print *," Hoanh do M "; read*,X Toa do M2:
if (y2>y1) then 6.,1.
Y = y1 + (X-x1)*(y2-y1)/(x2-x1) Hoanh do M
print '(a,2f8.3)',"Toa do M(X,Y) :",X,Y 4.
else ! Truong hop y2 < y1 Toa do M(X,Y) : 4.000 2.200
Y = y2 + (X-x2)*(y2-y1)/(x2-x1)
print '(a,2f8.3)',"Toa do M(X,Y) :",X,Y
end if
read*
end

Nhận xét : với cấu trúc if dạng đầy đủ như trên , chúng ta chỉ cần xét biểu thức (điều kiện)
logic chỉ một lần và thực hiện khối lệnh phù hợp với giá trị của biểu thức logic là đúng hoặc sai .
Từ khóa end if có thể viết liền thành endif . Phần khối lệnh then hoặc khối lệnh else gồm các câu
lệnh đơn giản hoặc các câu lệnh có cấu trúc .

42
Trong cấu trúc ( if then else end if ) , phần else có thể không có như sau ( tương tự câu
lệnh if đơn giản như 4.1.1 ) :
if ( biểu thức logic ) then
< khối lệnh > ! thực hiện nếu biểu thức logic đúng .
end if

Nếu biểu thức logic có giá trị đúng thì khối lệnh được thực hiện , nếu sai thì chương trình
bỏ qua câu lệnh if này và nhảy đến thực thi câu lệnh sau từ khóa end if .
Ví dụ : if ( x >= 0. ) then
y = sqrt (x)
print*,” Can bac hai cua “ , x , “ la : “ , y
end if

4.1.3 Cấu trúc if tổng quát .


Trong dạng này chúng ta xét lần lượt từng điều kiện ( từng biểu thức logic ) nối tiếp nhau
từ trên xuống dưới và chỉ thực hiện một khối lệnh tương ứng với biểu thức logic đầu tiên được
thỏa .Thực hiện xong thì thoát khỏi cấu trúc if . Trường hợp không có điều kiện nào được đáp ứng
thì khối lệnh sau từ khóa else ( nếu có ) sẽ được thực hiện . Cấu trúc if tổng quát có thể được đặt
một tên ( tùy chọn ) , tên tuân theo quy tắc tên biến .

[name :] if ( bt logic 1 ) then


< khối lệnh 1 > ! thực hiện khi bt logic 1 đúng .
else if ( bt logic 2 ) then [name]
< khối lệnh 2 > ! thực hiện khi bt logic 1 sai , bt logic 2 đúng
else if ( bt logic 3 ) then [name]
< khối lệnh 3 > ! thực hiện khi bt logic 1 , bt logic 2 đều sai và
! bt logic 3 đúng .
else [name]
<khối lệnh else> ! thực hiện nếu không có bt logic nào có giá trị đúng.
end if [name]

Ghi chú :
+ Từ khóa else if có thể được viết liền thành elseif . Khối lệnh gồm các câu lệnh đơn giản
hoặc câu lệnh có cấu trúc.
+ Phần else if (bt logic) then < khối lệnh > có thể được lặp lại nhiều lần tùy theo yêu cầu
của bài toán .
+ Phần else có thể không có .
Ví dụ : chương trình giải phương trình bậc hai ax2 + bx + c = 0 trong R với các hệ số a,b,c
nhập từ bàn phím ( giả sử a  0) được viết như sau :
program ptbh
implicit none
real (kind = 8) :: a,b,c,delta,x1,x2 ! dung so thuc chinh xac kep
print*," Hay nhap cac he so a, b,c : "
read*, a,b,c
delta = b*b - 4.*a*c
print*,delta ! Kiem tra gia tri va dau cua delta
test : if ( abs(delta) < epsilon(1.d0) ) then
x1 = -b/(2.*a)
print '(a,f10.4)'," Phuong trinh co nghiem kep : " , x1
43
else if (delta > 0.) then test ! Truong hop delta > 0.
x1 = (-b + sqrt(delta)) /(2.*a)
x2 = -b/a - x1
print*, " Phuong trinh co hai nghiem : "
print '(f10.4,a,f10.4)', x1, " va", x2
else test ! Truong hop delta < 0.
print*,"Phuong trinh vo nghiem . "
end if test
read*
end program ptbh

Bạn đọc chạy chương trình ptbh trên với các hệ số a,b,c khác nhau để kiểm tra chương trình .
Lưu ý quan trọng : trường hợp so sánh delta bằng 0. , chúng ta không nên dùng trực tiếp điều
kiện so sánh (delta == 0.) mà dùng điều kiện abs (delta) < epsilon (1.d0) , do kết quả của phép
tính biểu thức delta ( kiểu real ) do máy tính trả về có thể là một số thực rất nhỏ nhưng khác 0. và
có mang dấu ( Về số epsilon , bạn đọc xem lại bảng 2.2 , chương 2 . )
Ví dụ : Phương trình x2 – 2.468x + 1.522756 = ( x – 1.234) 2 có kết quả như sau :
Hay nhap cac he so a, b,c :
1. -2.468 1.522756
-1.3400738851920835E-16 ! Giá trị delta do máy biểu diễn là một số âm có giá trị tuyệt đối
! nhỏ hơn epsilon(1.d0) = 2.220446...E-16
Phuong trinh co nghiem kep : 1.2340 ( phù hợp ) .
Nếu dùng phép so sánh if ( delta == 0.) then ... sẽ có kết quả là phương trình vô nghiệm , do
delta có giá trị âm :
Hay nhap cac he so a, b,c :
1 -2.468 1.522756
-1.3400738851920835E-16 ! Giá trị delta âm .
Phuong trinh vo nghiem . ( Kết quả không phù hợp ) .

+ Khối if trong ví dụ trên được đặt tên là test thật sự không cần thiết . Việc đặt tên chỉ
thuận tiện cho việc đọc văn bản chương trình khi chúng ta có các khối if lồng vào nhau như ví dụ
sau :
program ifnested
implicit none
real :: x = 1.5 , y = -3.5
test1 : if (x >= 0.) then
test2 : if (y >=0.) then
print*,"Hai so x,y la so duong."
else test2
print*,"x la so duong , y la so am ngat . "
end if test2
else test1
print*," x la so am ngat ."
end if test1
read*
end program ifnested

Kết quả : x la so duong , y la so am ngat .


Nhận xét : khối if được đặt tên test2 được đặt nằm bên trong phần then của khối if bên
ngoài ( được đặt tên là test1) .
44
4.2 Cấu trúc lựa chọn select case .
Cấu trúc select case dùng để lựa chọn thực hiện một trong nhiều khối lệnh khác nhau tùy
thuộc vào kết quả trả về của một biểu thức kiểu rời rạc như integer , logical hoặc character . Sử
dụng cấu trúc select case chúng ta có thể tạo menu (bảng chọn ) gồm các hạng mục tính toán khác
nhau , người sử dụng sẽ lựa chọn thực hiện một hạng mục phù hợp từ menu này .
Cấu trúc select case có dạng như sau :
select case ( biểu thức chọn [selector] có kiểu integer, logical, character )
case (tập giá trị 1)
<khối lệnh được thực hiện khi kết quả của biểu thức chọn thuộc tập giá trị 1.>
case (tập giá trị 2)
<khối lệnh được thực hiện khi kết quả của biểu thức chọn thuộc tập giá trị 2.>

case (tập giá trị n)
<khối lệnh được thực hiện khi kết quả của biểu thức chọn thuộc tập giá trị n.>
case default
< khối lệnh được thực hiện khi kết quả của biểu thức chọn không thuộc các giá trị
được liệt kê ở các phần case nêu trên . >
end select

Lưu ý : a) Các tập giá trị thuộc phần case không được có phần tử chung và phải có cùng kiểu
dữ liệu với kiểu của biểu thức chọn nằm trong cặp dấu ngoặc của từ khóa select case .
b) Phần case default có thể không có trong cấu trúc select case .
c) Tập giá trị nằm trong case (…) có thể là một giá trị đơn ; nhiều giá trị được liệt kê cách
nhau bằng dấu phẩy hoặc một đoạn con có kèm theo cận trên , cận dưới . Ví dụ :
integer :: n
….
select case (n)
case ( : 0 )
<khối lệnh > ! được thực hiện nếu n  0 ,
case (1:5)
< khối lệnh > ! được thực hiện nếu 1  n  5
case (6 : )
< khối lệnh > ! được thực hiện nếu n  6
….
Ví dụ : chúng ta xem chương trình xếp loại học sinh như sau , điểm đầu vào là một số
nguyên từ 0 đến 10 . Biểu thức quyết định sự lựa chọn là một biến kiểu integer 1 byte (điểm số ).

program xeploai
implicit none
integer (kind = 1) :: diem
print '(a,$)',"Cho biet diem so : " ; read*,diem
select case (diem)
case(9,10)
print*,"Xep loai GIOI . "
case(7,8)
print*,"Xep loai KHA . "
case(5,6)
print*,"Xep loai TRUNG BINH ."
45
case (0:4) ! truong hop 0  diem  4
print*,"Xep loai KEM ."
case default
print*,"Diem vao khong hop le . "
end select
read*
end program xeploai
Kết quả :
Cho biet diem so : 7
Xep loai KHA .

Lưu ý quan trọng : khi điểm đầu vào có phần lẻ như 7.5 , 9.2 … chúng ta không dùng được
cấu trúc select case như trên mà phải dùng cấu trúc if tổng quát với biến điểm kiểu real như đoạn
chương trình sau ,cần phải xét từ điểm cao xuống điểm thấp không được xét theo chiều ngược lại.

real :: diem
print '(a,$)',"Cho biet diem so : " ; read* ,diem
if ((diem >= 9.) .and. (diem <= 10.)) then
print*," Xep loai GIOI ."
else if ((diem >= 7.) .and. (diem <9.)) then
print*," Xep loai KHA ."
else if ((diem >= 5.) .and. (diem < 7.)) then
print*," Xep loai TRUNG BINH ."
else if ((diem >= 0.) .and. (diem < 5.)) then
print*," Xep loai KEM ."
else
print*," Diem vao khong hop le."
end if

+ Cấu trúc select case còn có thể được lồng vào nhau và được đặt tên như chương trình
cho biết số ngày trong một tháng như sau :

program songay
implicit none
integer :: thang, nam
logical :: nhuan
print '(a,$)' ," Cho biet thang : " ; read*, thang
print '(a,$)' ," Cho biet nam : " ; read*, nam
test1 : select case (thang)
case (1,3,5,7,8,10,12) test1
print*,"Thang ",thang,"co 31 ngay."
case (4,6,9,11) test1
print*,"Thang ",thang,"co 30 ngay."
case (2) test1 ! Thang hai .
nhuan = ( mod(nam,400) == 0).or. &
&( mod(nam,4) == 0 .and.mod(nam,100 ) /= 0 )
test2 : select case (nhuan)

46
case(.true. ) test2 ! nam nhuan
print*,"Thang 2 nam ",nam, "co 29 ngay."
case(.false. ) test2 ! khong phai nam nhuan
print*,"Thang 2 nam ",nam, "co 28 ngay."
end select test2
case default test1
print*,"So lieu vao khong dung ."
end select test1
read*
end program songay

Kết quả Cho biet thang : 2


Cho biet nam : 2020
Thang 2 nam 2020 co 29 ngay.

Trong chương trình ví dụ trên , đối với tháng 2 , chúng ta còn phải xét thêm năm đó có là
năm nhuận hay không ( theo điều kiện của lịch Gregorius ) và lồng cấu trúc select case có tên test2
với biểu thức lựa chọn kiểu logical nằm bên trong phần case (2) . Khối select case có tên test2
không có phần case default .
Biểu thức lựa chọn trong select case có thể có kiểu character như trong ví dụ sau :

program quocgia
implicit none
character (len = 15) :: nuoc
print*," Cho biet ten quoc gia : "
read*, nuoc
select case (nuoc)
case('Viet Nam' , 'Thai Lan' ,'Nhat', 'Han Quoc')
print*,’Nuoc ‘,trim(nuoc), ‘ thuoc Chau A .’
case('Anh' , 'Phap' ,'Duc', 'Y', 'Bi' )
print*,’Nuoc ‘,trim(nuoc), ‘ thuoc Chau Au .’
case('Nam Phi' , 'Ai Cap' ,'Angieri', 'Nigieria')
print*,’Nuoc ‘,trim(nuoc), ‘ thuoc Chau Phi .’
case('My' , 'Canada' ,'Mehico', 'Braxin', 'Achentina' )
print*,’Nuoc ‘,trim(nuoc), ‘ thuoc Chau My .’
case('Uc' , 'Niu Dilan' )
print*,’Nuoc ‘,trim(nuoc), ‘ thuoc Chau Dai Duong .’
case default
print*, ‘So lieu vao khong phu hop .’
end select
read*
end program quocgia

Kết quả : Cho biet ten quoc gia :


'Viet Nam'
Nuoc Viet Nam thuoc Chau A .

47
4.3 Cấu trúc vòng lặp do .
Khi cần thực hiện một lệnh hay một khối lệnh lặp đi lặp lại nhiều lần , chúng ta sử dụng
cấu trúc vòng lặp do . Số lần thực hiện khối lệnh trong vòng lặp có thể được biết trước hoặc không
được biết trước .
4.3.1 Cấu trúc vòng lặp xác định .
Đây là dạng cấu trúc mà số lần thực hiện lệnh hoặc khối lệnh bên trong vòng lặp được
biết trước . Thực hiện xong một vòng còn gọi là hoàn thành một chu trình .
Cấu trúc vòng lặp xác định có dạng như sau ( phần tên là tùy chọn và theo quy tắc đặt tên
biến của Fortran ) :

[name :] do biến đếm kiểu integer = giá trị đầu , giá trị cuối , số gia
< khối lệnh >
end do [name ]

+ Giá trị đầu, giá trị cuối , số gia : biểu thức kiểu integer .
+ Biến đếm ( biến điều khiển vòng lặp ) phải được khai báo ở đầu chương trình ( phù hợp
với câu lệnh implicit none ) . Giá trị biến đếm có thể sử dụng trong các lệnh thuộc khối lệnh , nhưng
không được có câu lệnh làm thay đổi giá trị của biến đếm này .
+ Cấu trúc do end do có thể được đặt lồng vào nhau và được đặt tên .
+ Từ khóa end do có thể viền liền nhau thành enddo
Quy luật hoạt động của vòng do xác định như sau :

 Trường hợp 1 : giá trị đầu < giá trị cuối , số gia > 0
+ Đầu tiên biến đếm lấy giá trị đầu và thực hiện các lệnh trong khối lệnh .
+ Biến đếm sau đó được tăng lên một lượng bằng số gia và so sánh với giá trị cuối , nếu
biến đếm < = giá trị cuối thì thực hiện tiếp khối lệnh lần thứ hai ( chu trình 2 ) .
+ Các chu trình sau được thực hiện tuần tự theo cách tương tự như trên.
+ Vòng lặp kết thúc khi biến đếm lớn hơn giá trị cuối . Chương trình sẽ tiếp tục thực hiện
câu lệnh sau phần end do .
+ Nếu số gia không có thì xem số gia = 1 ( giá trị mặc định ) .

Ví dụ :
integer :: i i= 1
do i = 1,5 i= 2
print*,"i = ", i i= 3
end do i= 4
Kết quả :  i= 5

integer :: i i= 1
do i = 1,5,2 i= 3
print*,"i = ",i i= 5
end do

 Trường hợp 2 : giá trị đầu > giá trị cuối , số gia < 0
Lưu ý trong trường hợp này số gia âm phải được viết ra , nếu không chương trình sẽ bỏ
qua câu lệnh do .
+ Đầu tiên biến đếm lấy giá trị đầu và thực hiện các lệnh trong khối lệnh .

48
+ Biến đếm sau đó được giảm một lượng bằng trị tuyệt đối của số gia và so sánh với giá trị
cuối , nếu biến đếm >= giá trị cuối thì thực hiện tiếp khối lệnh lần thứ hai ( chu trình 2 ) .
+ Các chu trình sau được thực hiện tuần tự theo cách tương tự như trên.
+ Vòng lặp kết thúc khi biến đếm nhỏ hơn giá trị cuối . Chương trình sẽ thực hiện tiếp câu
lệnh sau phần end do .

Ví dụ :
integer :: i
do i = 5,1,-2
print*,"i = ",i i= 5
end do i= 3
Kết quả :  i= 1

+ Ví dụ : chương trình sau sẽ tính tổng n số tự nhiên đầu tiên : S = 1+2+3+...+n
program tong
implicit none
integer :: i,n,s
print*,"Cho biet so so hang n : "; read*,n
s=0
do i = 1,n
s=s+i
end do
print*,"Tong cua",n,"so tu nhien dau tien la :",s
read*
end program tong
Kết quả : Cho biet so so hang n :
20
Tong cua 20 so tu nhien dau tien la : 210

+ Ví dụ : chương trình tính giai thừa n ! = 1*2*3*… *(n-1)*n


program giaithua
implicit none
integer :: i,n,gt = 1
print'(a,$)'," Nhap so n : "
read*, n
do i = 1,n
Nhap so n : 6
gt = gt*i 
Giai thua n! = 720
end do
print*,"Giai thua n! = ",gt
read*
end

+ Ví dụ về vòng lặp xác định được lồng vào nhau và được đặt tên.
program nestdo
implicit none
integer :: i,j
49
ext : do i = 1,3
int : do j = 1,3
print'(i2,$)', (i+j)
end do int 234
print* 345
end do ext 456
read* Kết quả : 
end program nestdo

+ Với mỗi giá trị của biến đếm i ở vòng do ngoài ( được đặt tên là ext ) , biến đếm j ở vòng do
trong ( được đặt tên là int ) lấy lần lượt tất cả các giá trị từ 1 đến 3 và xuất ra tổng (i+j) trên cùng
một hàng .
+ Xâu định dạng ‘(i2,$)’ dùng để xuất một số nguyên chiếm 2 ô màn hình ( kí hiệu định dạng là i2
), số được viết căn lề phải và con trỏ không xuống dòng .

 Vòng lặp do dạng ngầm .


Chúng ta xem hai ví dụ sau về việc sử dụng vòng do dạng ngầm :
+ Chương trình xuất ra bảng nhân có 10 dòng , mỗi dòng có 10 số nguyên .
program bangnhan
implicit none
integer :: i,j
do i = 1,10
print '(10(1x,i3))', (i*j,j=1,10) ! vòng do ngầm bên trong với biến chạy j từ 1 đến 10
end do
read*
end
Ghi chú : chương trình trên xuất ra biểu thức i*j . Xâu định dạng ‘(10(1x,i3))’ sắp xếp xuất
ra 10 số nguyên trên một dòng , mỗi số chiếm 3 cột (định dạng i3) , hai số liền nhau cách nhau một
cột ( kí hiệu định dạng 1x ) . Kết quả :

+ Chương trình xuất ra màn hình bình phương các số từ 1 đến 10 ( biểu thức i*i ).
program do_ngam
implicit none
integer :: i
print '(10(1x,i4))', (i*i,i=1,10) ! vòng do ngầm với biến i chạy từ 1 đến 10 .
read*
end

50
4.3.2 Cấu trúc vòng lặp không xác định .
Đây là dạng vòng lặp có số lần thực hiện lệnh hoặc khối lệnh bên trong vòng lặp không
được biết trước mà tùy thuộc vào một điều kiện ( biểu thức logic ) . Cú pháp một số dạng vòng
lặp không xác định như sau :
a) Cấu trúc vòng lặp do while .

[name :] do while ( biểu thức logic )


<khối lệnh> ! lệnh đơn giản hay lệnh có cấu trúc.
end do [name ]

+ Đầu tiên biểu thức logic sau từ khóa while được kiểm tra trước , nếu nó có giá trị đúng
(.true.) thì các lệnh thuộc khối lệnh bên trong vòng do mới được thực hiện . Nếu biểu thức logic có
giá trị sai thì chương trình bỏ qua khối do và nhảy đến thực hiện câu lệnh sau phần end do. Khối
do while vì thế có thể không được thực hiện lần nào .
+ Sau mỗi chu trình , biểu thức logic lại được kiểm tra , nếu đúng thì thực hiện chu trình kế
tiếp , nếu sai thì chương trình thoát khỏi khối do while .
+ Cấu trúc do while thuộc loại “ tiền kiểm “ ( kiểm tra điều kiện logic trước khi thực hiện
các lệnh trong vòng do ) .
+ Lưu ý : để tránh vòng lặp vô tận , cần phải có câu lệnh bên trong khối lệnh làm thay đổi
giá trị các toán hạng của biểu thức logic đầu vào sau mỗi bước thực hiện vòng lặp .

Ví dụ : Xét chuỗi điều hòa s = 1 + 1/2 + 1/3 + 1/4 + … + 1/n và một số thực a > 2 cho
trước ( nhập từ bàn phím ) . Tìm số số hạng nhỏ nhất của chuỗi để s >= a .

program dieuhoa
implicit none
integer :: i = 1
real :: s, a
print*,"Cho biet so a :"; read*,a
s = 1.
do while (s < a)
i= i+1
s = s + 1. /i ! hoặc s = s + 1. /real (i)
end do
print*," So so hang nho nhat i = " , i
read*
end program dieuhoa
Kết quả :
Cho biet so a :
4.
So so hang nho nhat i = 31
Lưu ý : trong chương trình trên nếu viết s = s + 1/i thay vì viết đúng là s = s + 1./i ( hoặc
có thể viết s = s + 1./real(i) ) thì chúng ta sẽ rơi vào vòng lặp vô tận do phép chia hai số nguyên 1/
i ( i  2) sẽ luôn bằng 0 và s = 1. không đổi .

+ Chúng ta xem chương trình tính ước số chung lớn nhất và bội số chung nhỏ nhất của hai
số nguyên dương m , n sử dụng vòng lặp do while như sau :

51
program usc_bsc
implicit none
integer :: m,n,ms,ns,uscmax,bscmin
print*," Cho hai so nguyen duong m , n : " ; read*, m , n
ms = m ; ns = n ! luu cac so m , n
do while (m /= n)
if ( m > n ) then
m=m-n
else ! truong hop n > m
n = n -m
end if
end do
uscmax = m ; bscmin = (ms*ns)/m
print*,"Uoc so chung lon nhat :",uscmax
print*,"Boi so chung nho nhat :",bscmin
read*
end program usc_bsc
Kết quả :
Cho hai so nguyen duong m , n :
64 24
Uoc so chung lon nhat : 8
Boi so chung nho nhat : 192
Nhận xét : Khối lệnh bên trong vòng lặp do là khối lệnh if then else endif .

b) Cấu trúc vòng lặp không xác định dạng “ hậu kiểm “ ( điều kiện logic được
kiểm tra sau ) :
[ name :] do
< lệnh >
< lệnh >

if (biểu thức logic kiểm tra ) exit ! Nếu đúng thì thoát ra.
end do [ name ]
+ Đầu tiên các lệnh trong vòng do được thực hiện lần lượt cho đến khi gặp câu lệnh kiểm
tra if ở cuối chu trình , nếu biểu thức logic có giá trị đúng thì chương trình thoát khỏi vòng do (
thực thi lệnh exit ) và thực hiện tiếp các lệnh sau phần end do . Ngược lại nếu có giá trị sai thì
chương trình quay trở lại thực hiện chu trình tiếp theo của vòng do .
+ Các lệnh thuộc cấu trúc này được thực hiện ít nhất một lần . ( Trong khi vòng do while có
thể không được thực hiện lần nào ) .
+ Lệnh exit dùng để thoát khỏi vòng do , sẽ được nói rõ thêm ở phần sau .
+ Lưu ý : để tránh vòng lặp vô tận , cần phải có câu lệnh bên trong vòng do làm thay đổi giá
trị các toán hạng của biểu thức logic kiểm tra sau mỗi bước thực hiện vòng lặp .
Trong ví dụ về vòng lặp dưới đây , chỉ khi chúng ta nhập giá trị ‘n’ hay ‘N’ cho biến traloi
( kiểu character 1 kí tự ) thì chương trình mới thoát khỏi vòng do .
program test
implicit none
character traloi
do
print*,'Hello ! '

52
print*,'Welcome to Viet Nam ! '
print*,' Ban muon tiep tuc y/n ' ; read*, traloi ! Nhap n hoac N de thoat .
if ((traloi == 'n') .or. (traloi == 'N')) exit
end do
read*
end

c) Cấu trúc vòng lặp tổng quát :


Xét vòng lặp do có số lần lặp vô tận (infinite loop ) như sau : ( phải tránh dạng vòng lặp
kiểu này ! ) :

[ name :] do
< khối lệnh>
end do [ name ]

Có hai cách để thoát khỏi vòng do vô tận này :


+ Dùng lệnh stop để kết thúc chương trình .
+ Dùng lệnh exit để thoát ra khỏi vòng do có chứa lệnh exit này , lúc này chương trình sẽ
tiếp tục thực hiện câu lệnh sau phần end do .
Các lệnh stop, exit nêu trên thường kết hợp với các biểu thức logic để quyết định việc thoát
khỏi vòng do . Chúng ta xem cấu trúc vòng do tổng quát như sau :

[name : ] do
< lệnh >

if ( biểu thức logic1 ) cycle ! trở lại đầu vòng lặp ,
! thực hiện chu trình kế tiếp.

if ( biểu thức logic 2 ) exit ! thoát khỏi vòng do .
end do [ name ]

Trong cấu trúc tổng quát nêu trên : khi gặp lệnh cycle , chương trình sẽ không thực hiện
tiếp các lệnh của chu trình hiện thời sau lệnh cycle mà quay trở lại thực hiện từ đầu các lệnh của
chu trình kế tiếp ( sau từ khóa do ) .
Khi gặp lệnh exit , chương trình thoát khỏi vòng do có chứa lệnh exit này và thực hiện tiếp
các lệnh sau phần end do .
Lưu ý : Khi chương trình ‘lỡ‘ rơi vào vòng lặp vô tận , cần nhấn tổ hợp phím Crtl – C để
dừng việc thực thi chương trình . Trường hợp không hiệu quả , chúng ta có thể nhấn phím có logo
windows , sau đó chọn Restart trong nhóm Shut down để khởi động lại máy tính .
Ví dụ : Chương trình kiểm tra xem một số nguyên dương m nhập từ bàn phím có phải là số
nguyên tố hay không ? Chúng ta kiểm tra tuần tự phép chia số m cho số 2 đến số nguyên lớn hơn
hay bằng căn bậc hai của m , nếu gặp phép chia có số dư bằng 0 ( chia chẵn ) thì m không phải là
số nguyên tố . Lúc này lệnh exit sẽ kết thúc sớm vòng do và thực hiện tiếp các lệnh sau phần end
do .
program nguyento
implicit none
integer :: i,m,n
53
logical :: test = .true.
print*," Cho so nguyen duong m : " ; read*,m
n = sqrt(real(m)) + 1.
do i = 2,n
if (mod(m,i) == 0) then
test = .false.
exit ! thoat khoi vong do
end if
end do
if (test) then
print*," So ", m," la so nguyen to. "
else
print*," So ", m," khong phai la so nguyen to. "
end if
read*
end program nguyento

Kết quả : Cho so nguyen duong m :


2017
So 2017 la so nguyen to.
Cho so nguyen duong m :
4607
So 4607 khong phai la so nguyen to.

+ Chúng ta thấy có sự tương đương giữa hai dạng vòng do như sau :

do while ( bt logic ) do
…  if (.not.(bt logic) ) exit

< khối lệnh > < khối lệnh >
end do end do

Nhận xét : khi bt logic đúng thì các lệnh trong vòng do mới được thực hiện .

+ Ví dụ : chúng ta xem vòng lặp xác định in ra các số từ 1 đến 7 nhưng không in ra các số
4 và 5 như sau ( sử dụng lệnh cycle ) :

program cycle
implicit none
integer :: i i= 1
do i = 1,7 i= 2
if ((i == 4).or.(i == 5)) cycle i= 3
i= 6
print*," i = ", i 
i= 7
end do
read*
end

54
Nhận xét : Khi i = 4 hoặc i = 5 lệnh cycle có hiệu lực , chương trình sẽ bỏ qua lệnh in các
số này và thực hiện chu trình kế tiếp .

+ Chúng ta xem cấu trúc vòng lặp do được lồng vào nhau và được đặt tên như dưới đây ,
các lệnh cycle , exit có thể kèm theo tên của vòng lặp và sẽ có các ứng xử khác khau .

loop_ext : do
! Chương trình nhảy đến đây khi gặp lệnh cycle loop_ext
loop_int : do
! Chương trình nhảy đến đây khi gặp lệnh cycle
< lệnh >

If (bt logic 1 ) cycle
If (bt logic 2 ) cycle loop_ext

If (bt logic 3 ) exit
If (bt logic 4 ) exit loop_ext
end do loop_int
! Chương trình nhảy đến đây khi gặp lệnh exit
end do loop_ext
! Chương trình nhảy đến đây khi gặp lệnh exit loop_ext

Nhận xét : các lệnh exit , cycle chỉ có tác dụng trong phạm vi vòng do chứa các lệnh này
ngoại trừ trường hợp lệnh có kèm theo tên của vòng lặp như sơ đồ nêu trên . Lệnh exit , cycle được
phép sử dụng trong mọi kiểu vòng do.
Vòng lặp do kết hợp với các lệnh cycle , exit có thể dùng để kiểm tra việc nhập số liệu : nếu
số liệu nhập chưa đúng thì lệnh cycle yêu cầu phải nhập lại , nếu nhập đúng rồi thì lệnh exit sẽ kết
thúc vòng do , thực thi các lệnh sau chỉ thị end do .
program kiemtra
implicit none
real :: x
do
print*," Nhap so duong x "; read*,x
if ( x <0.) cycle ! Nhập lại
if ( x >= 0.) exit ! Nhập đúng thoát ra khỏi vòng do
end do
print*," So x : ", x ; read*
end
+ Chương trình kiểm tra việc nhập điểm :
program kiemtradiem
implicit none
integer :: diem
do
print*," Vao diem tu 0 den 10 : "; read*,diem
if ((diem <= 10) .and. (diem >= 0)) exit ! Nhập đúng thì thoát ra
print*," Diem vao khong dung .Nhap lai." ! Nhập lại
end do
print*,"Diem da vao hop le :", diem

55
read*
end
Vao diem tu 0 den 10 :
12
Diem vao khong dung .Nhap lai.
Vao diem tu 0 den 10 :
8
Diem da vao hop le : 8
4.4 Lệnh goto .
+ Cú pháp : goto nhãn
Nhãn (label) là một số nguyên dương khác 0 ( không có dấu ) có từ 1 đến 5 chữ số .Nhãn
do người lập trình tự chọn và không cần xếp đặt theo thứ tự . Ví dụ : 10 , 20 , 100 ...
+ Lệnh goto sẽ chuyển hướng chương trình đến thực hiện câu lệnh có gắn nhãn tương ứng
với nhãn của lệnh goto , nằm ở một vị trí nào đó trong chương trình . Nhãn được đặt ngay trước
câu lệnh liên kết và cách câu lệnh một hoặc vài khoảng trống . Lệnh goto thường đi kèm với một
biểu thức logic. Ví dụ :
do
< lệnh >

if ( biểu thức logic ) goto 100

end do

100 y = x*x ! thực hiện sau lệnh goto 100 nếu biểu thức logic đúng.
+ Nhận xét : lệnh goto phá vỡ cấu trúc tuần tự của chương trình và nếu chương trình có quá
nhiều lệnh goto như các phiên bản Fortran trước sẽ làm cho chúng ta khó đọc , khó theo dõi cấu
trúc của văn bản chương trình . Do Fortran 90 đã cung cấp đầy đủ các cấu trúc điều khiển , tạo sự
thuận tiện cho người lập trình nên lệnh goto được khuyến cáo hạn chế sử dụng .

4.5 Lệnh stop .


Lệnh stop dùng để kết thúc việc thực thi một chương trình . Lệnh stop có thể xuất hiện ở
cuối một chương trình ( trước chỉ thị end program ) hoặc tại một vị trí khác trong chương trình nếu
việc thực thi chương trình vì lý do nào đó không thể tiếp tục .
Ví dụ :
program main
implicit none

stop
end program main

Ví dụ :
do
< lệnh >
if ( bt logic ) then
print*,” Chuong trinh khong the tiep tuc .“
read*
stop ! chuong trinh ket thuc tai day.
end if

56
end do
...
4.6 Thực hành .
4.6.1 Bạn đọc ứng dụng cấu trúc if tổng quát để viết chương trình tính thuế thu nhập cá nhân
theo biểu thuế suất như sau :

Thu nhập chịu thuế Thuế suất Tối đa ( triệu đồng)


(triệu đồng )
tn <= 5 5% 0.25
5 < tn <= 10 10% 0.75
10 < tn <= 18 15% 1.95
18 < tn <= 32 20% 4.75
32 < tn <= 52 25% 9.75
52 < tn <= 80 30% 18.15
tn > 80 35%
Ghi chú : cột tối đa ghi tiền thuế tncn của người có mức thu nhập chịu thuế bằng với cận trên của cột số
1.Ví dụ : tnct = 18 triệu đồng → thuế tncn = 1.95 triệu đồng.

+ Bạn đọc phân tích và chạy test chương trình đáp án sau :

program thue_tncn
implicit none
real :: thunhap , thue
print*,"Don vi tinh : TRIEU DONG."
print*,"Cho biet thu nhap chiu thue : "
read*, thunhap
if (thunhap > 80.) then
thue = (thunhap - 80.)*0.35 + 18.15
else if (thunhap > 52.) then
thue = (thunhap - 52.)*0.30 + 9.75
else if (thunhap > 32.) then
thue = (thunhap - 32.)*0.25 + 4.75
else if (thunhap > 18.) then
thue = (thunhap - 18.)*0.20 + 1.95
else if (thunhap > 10.) then
thue = (thunhap - 10.)*0.15 + 0.75
else if (thunhap > 5.) then
thue = (thunhap - 5.) *0.10 + 0.25
else if (thunhap <= 5.) then
thue = thunhap*0.05
else
print*,"So lieu vao khong dung "
end if
print'(a,f9.3)', " ** Thue thu nhap : " , thue
read*
end
Kết quả :
Don vi tinh : TRIEU DONG.
Cho biet thu nhap chiu thue :
48.5
** Thue thu nhap : 8.875
57
4.6.2 Ứng dụng vòng do xác định để tính gần đúng tích phân xác định bằng công thức hình
thang :

b 1
s   f ( x)dx   1  x 2 dx h = (b – a)/n ; xi = a + i*h → yi = f(xi) ( i = 0..n)
a 0
s = [ (y0 + yn) * 0.5 + y1 + y2 +.. + yn-1 ] * h
Các cận a,b và số đoạn chia n được nhập từ bàn phím .
Bạn đọc phân tích và chạy chương trình sau :

program tichphan
implicit none
real :: a,b,x,y,h,s
integer :: i,n
print*," Vao cac so a,b,n "
read*,a,b,n
h = (b-a)/n
s = (sqrt(1+a*a) + sqrt(1+b*b))*0.5
do i = 1, (n-1)
x = a + i*h
s = s + sqrt(1+x*x)
end do
s=s*h
print'(a,f10.4)'," Ket qua : " , s
read*
end

Áp dụng :
Vao cac so a,b,n
0 1 100
Ket qua : 1.1478

+ Dựa vào bài tập trên , bạn đọc lập trình tính các tích phân xác định sau :

1 2

e e
x2  x2
dx ; dx
0 0

4.6.3 Ứng dụng vòng lặp không xác định do while để tính giá trị của hàm mũ ex theo chuỗi
số sau :
ex = 1 + x/1! + x2/2! + x3/3! + … + xn/n! n! =1*2*3*….*(n-1)*n

58
program ham_mu
implicit none
real (kind = 8) :: s,t,x ! dung so thuc chinh xac kep
integer :: n
print*," Cho gia tri x : " ; read*,x
s = 1._8
t=x
n=1
do while ( t >= 1.d-10) ! ngắt bỏ các số hạng < 10-10
s=s+t
n=n+1
t = t*(x/n)
end do
print*," So so hang cua chuoi n : " , n
print*, " Tri so exp(x) : "
print '(d18.10)', s
read*
end

Áp dụng : Tính số e ( lấy x = 1.d0) bằng chương trình trên .


Cho gia tri x :
1.d0
So so hang cua chuoi n : 14
Tri so exp(x) :
0.2718281828D+01

4.6.4 Lập trình tính số hạng thứ n >2 của dãy số Fibonacci : 1 1 2 3 5 8 13 21 ...
u1 = 1 ; u2 = 1 ; un = un-1 + un-2 ( n  3)
program fibonacci
implicit none
integer :: i,n,a,b,u
a = 1 ; b = 1 ! hai gia tri u1 , u2
print ‘(a,$)’, “Cho so n > 2 :” ; read*,n
do i = 3,n
u = a+b ! gia tri u(i)
a=b;b=u
end do
print*,”** So hang thu”,n,” bang :”,u
read* ; end
Kết quả :
Cho so n > 2 : 8
** So hang thu 8 bang : 21

4.6.5 Lập chương trình chuyển đổi nhiệt độ từ độ Farenheit sang độ Celcius và ngược lại .
Sử dụng các công thức : C =5./9.*(F-32.) và F = 9./5.*C+32. Tạo menu và sử dụng cấu trúc
select case với biến chọn kiểu integer để lựa chọn công thức chuyển đổi phù hợp .

program doinhietdo
implicit none
integer :: chon
59
real :: c,f
character :: traloi
do
print*,"**** CHUONG TRINH DOI NHIET DO ****"
print*,"==================================="
print*,"1.Doi tu do F ra do C : bam phim so 1" !Tạo menu .
print*,"2.Doi tu do C ra do F : bam phim so 2"
print '(a,$)'," Ban chon phim so : "; read*,chon
print*
select case (chon)
case (1)
print '(a,$)'," Cho nhiet do F :" ; read*,f
c = 5./9.*(f-32.)
print '(a,f6.2)'," Nhiet do C la : ",c
case (2)
print '(a,$)'," Cho nhiet do C :" ; read*,c
f = 9./5.*c + 32.
print '(a,f6.2)'," Nhiet do F la : ",f
case default
print*," So lieu nhap chua dung!"
end select
print*,"------------------------------------------------------------"
print*,"Tiep tuc bam phim y hoac Y."
print*,"Thoat khoi chuong trinh bam n hoac N ."
print '(a,$)'," Tiep tuc hay thoat ? "; read*,traloi
if ((traloi == 'n').or.(traloi == 'N')) exit ! Thoát khỏi vòng lặp do end do
print*
call system ('cls') ! Lau man hinh , clear screen
end do
end program doinhietdo

**** CHUONG TRINH DOI NHIET DO ****


===================================
1.Doi tu do F ra do C : bam phim so 1
2.Doi tu do C ra do F : bam phim so 2
Ban chon phim so : 1
Cho nhiet do F :90
Nhiet do C la : 32.22
-----------------------------------------------------------
Tiep tuc bam phim y hoac Y.
Thoat khoi chuong trinh bam n hoac N .
Tiep tuc hay thoat ? n

Ghi chú : Khi menu hiện ra , chúng ta chọn phím số 1 hoặc số 2 để chọn kiểu chuyển đổi . Vào
nhiệt độ F hoặc C theo yêu cầu . Để tiếp tục chương trình chúng ta bấm phím y hoặc Y hoặc phím chữ bất
kỳ khác với phím n ,N ; để thoát khỏi chương trình chúng ta bấm phím n hoặc N .
Lệnh call system (‘cls’) gọi lệnh lau màn hình cls của hệ thống ( clear screen ) , các nội dung cũ bị
xóa và bắt đầu một phiên làm việc mới . Nếu không có lệnh này các kết quả tính toán sẽ nối tiếp liên tục
trên màn hình kết quả . Toàn bộ các lệnh nằm trong khối do end do và chỉ khi điều kiện logic trong lệnh
if ( ) được thỏa thì lệnh exit mới cho phép thoát khỏi vòng lặp .

*****
60
Chương 5 : CÁC LỆNH NHẬP / XUẤT VÀ ĐỊNH DẠNG DỮ LIỆU

Các lệnh nhập / xuất dữ liệu cho phép sự trao đổi ( truyền ) dữ liệu giữa bộ nhớ máy tính
và các thiết bị ngoại vi như màn hình , bàn phím , máy in , các thiết bị ghi /đọc bên ngoài như ổ
đĩa cứng , usb ... Quá trình truyền dữ liệu sẽ làm phát sinh một số vấn đề do sự chuyển đổi dữ liệu
từ dạng kí tự text thông thường sang dạng nhị phân (mã máy ) và ngược lại . Ngoài ra , việc xuất /
nhập dữ liệu theo một định dạng được sắp xếp trước sẽ giúp cho việc trình bày hoặc nhập dữ liệu
được chính xác , gọn gàng và mỹ thuật .

5.1 Các lệnh xuất dữ liệu ra màn hình .


 Màn hình thường được xem là một thiết bị xuất dữ liệu chuẩn ( standard output ) . Để
xuất dữ liệu ra màn hình theo định dạng tự động (free format , tùy thuộc vào cấu trúc máy tính) ,
chúng ta sử dụng các lệnh sau :
print*, < các đối tượng xuất . > hoặc
write (*,* ) < các đối tượng xuất . >
Các đối tượng được xuất trên một dòng màn hình , có thể là tên biến, hằng , biểu thức tính
được, giá trị trực tiếp ... được liệt kê cách nhau bằng dấu phẩy . Lưu ý : khi xuất xong , con trỏ tự
động xuống dòng.
Dấu * trong lệnh print * cho biết các dữ liệu xuất được định dạng tự động , các dữ liệu số
được viết ra với tất cả các chữ số thập phân được thể hiện bởi máy tính .
Trong lệnh write (*,*) , dấu * đầu tiên cho biết kênh xuất dữ liệu là màn hình , dấu * thứ
hai cho biết dữ liệu xuất được định dạng một cách tự động .
Ví dụ : print*, “Chieu cao H = “, h,” m”  write (*,*) “Chieu cao H = “, h,” m”
Lưu ý : Các lệnh print*, hoặc write (*,*) không có đối tượng xuất dùng để tạo một dòng
trống trên màn hình ( blank line ) .
Ví dụ :
program testinout  Chieu cao H = 18.165 m
implicit none #
real :: h = 18.165 #
print*,"Chieu cao H = ",h,"m" Chieu cao H = 18.165 m
print* ! Tao dong trong
write(*,*) ! Tao dong trong
write(*,*)"Chieu cao H = ",h,"m" Kí hiệu # tượng trưng cho dòng trống.
read*
end
 Để xuất dữ liệu ra màn hình theo một định dạng xác định ( formatted output ) , chúng ta
sử dụng các câu lệnh có hình thức như sau :
print ‘( phần định dạng )’ , < các đối tượng xuất . > , tương đương với
write (* , ‘( phần định dạng )’) < các đối tượng xuất . >

hoặc sử dụng kết hợp với câu lệnh format có gắn nhãn đứng riêng như sau :
write (*, nhãn ) < các đối tượng xuất.>  print nhãn ,< các đối tượng xuất. >
nhãn format ( phần định dạng ) ! nhãn ( label ) là số nguyên dương khác 0 ,
! có từ 1 đến 5 chữ số do chúng ta tự đặt .
Phần định dạng nằm trong cặp dấu ngoặt đơn sẽ cho biết số lượng , kiểu dữ liệu , kiểu định
dạng , vị trí của các đối tượng xuất , cũng như lệnh điều khiển con trỏ . Có sự tương ứng giữa nội
dung phần định dạng và các đối tượng xuất . Ví dụ lệnh xuất ra màn hình có định dạng cho 3 đối
tượng như sau :

61
print ‘(a ,f8.2, a)’, “Chieu cao H = “, h ,” m” hoặc tương đương với :
write (*,’(a ,f8.2, a)’) “Chieu cao H = “, h ,” m”
Phần định dạng (a,f8.2,a) tương ứng với 03 đối tượng xuất như sau :
+ a  “Chieu cao H = ” ! a dùng để định dạng xâu kí tự với số ô biểu diễn xâu bằng
chiều dài thực tế của xâu .
+ f8.2  h ! f8.2 dùng định dạng cho số thực h = 18.165 , được viết ra theo dạng dấu
chấm tĩnh trong 8 ô ( gồm cả phần dấu  , phần nguyên, dấu chấm thập phân , phần lẻ thập phân
và các ô trống phía trước ) trong đó phần lẻ chiếm 2 ô . Số được căn lề phải và có thể được làm
tròn chữ số cuối cùng . ( Định dạng fw.d ) .
w=8

1 8 . 1 7
d=2

+ a  “m” ! a dùng để định dạng cho xâu kí tự thứ hai .

Chi tiết viết phần định dạng sẽ được trình bày ở các phần sau .
Ghi chú : Fortran dành số 6 cho thiết bị xuất chuẩn là màn hình nên :
+ Lệnh xuất write (6,*) tương đương với lệnh write (*,*) ;
+ Lệnh write (6,’(định dạng )’) tương đương với lệnh write (*,’(định dạng)’)

5.2 Các lệnh nhập dữ liệu từ bàn phím.


Bàn phím thường được xem là thiết bị nhập chuẩn . Để nhập giá trị cho các biến từ bàn
phím theo định dạng tự động ( free format input ) , chúng ta sử dụng các lệnh sau :
read *, < danh sách các tên biến . > hoặc
read (*,*) < danh sách các tên biến . > hoặc
read (5,*) < danh sách các tên biến .> ! số 5 là kênh dành cho bàn phím .
Danh sách các tên biến được viết cách nhau bằng dấu phẩy .
Dấu * trong lệnh read *, cho biết sự chuyển đổi dữ liệu nhập từ bàn phím sang dạng thể
hiện bởi máy tính được định dạng tự động . Trong lệnh read (*,*) , dấu * đầu tiên cho biết kênh
nhập dữ liệu là bàn phím , dấu * thứ hai cho biết kiểu định dạng khi chuyển đổi là tự động .
Khi nhập giá trị cho nhiều biến cùng một lúc trên màn hình , các giá trị nhập phải viết cách
nhau một hay vài khoảng trống hoặc dùng dấu phẩy để phân cách . Dùng phím Enter để kết thúc
câu lệnh nhập . Chúng ta cũng có thể nhập giá trị cho từng biến , sau mỗi giá trị nhập , chúng ta ấn
Enter . Ví dụ :
Cho x là biến kiểu real , i là biến kiểu integer , ten là biến kiểu character (len = 15) , test là
biến kiểu logical , câu lệnh nhập giá trị cho 4 biến này từ bàn phím như sau :
read *, x, i, ten ,test  read (*,*) x, i, ten ,test

Khi gặp lệnh read , chương trình sẽ dừng lại chờ cho chúng ta nhập các giá trị phù hợp từ
bàn phím .Nếu nhập một lần cho cả 4 biến , thực hiện như sau :
2.345 2018 ‘Le Van A’ .true. <Enter> hoặc
2.345 , 2018 , ‘Le Van A’ , .true. <Enter>

Nếu nhập cho từng biến : 2.345 <Enter>


2018 <Enter>
‘Le Van A’ <Enter>
.true. <Enter>
62
Lưu ý : khi nhập nội dung cho biến xâu kí tự , chúng ta dùng cặp dấu nháy đơn hoặc nháy
kép bao lấy nội dung xâu kí tự . Riêng đối với kí tự đơn như ‘y’ hay ‘Y’ thì không cần cặp dấu
nháy này .Các hằng .false. , .true. : vào false , true hoặc vào trực tiếp F , T .
Khi gặp 2 câu lệnh : print ‘(a,$)’ , “ Cho biet gia tri x = “ ; read *, x con trỏ sẽ không
xuống dòng sau dấu = ( do có sử dụng kí hiệu điều khiển con trỏ $ trong phần định dạng ) và chờ
nhập giá trị cho biến x theo lệnh read tiếp theo .
Nhập dữ liệu có kèm theo phần định dạng dữ liệu ( formatted input ) thường được sử dụng
cho việc nhập từ tập tin ( trình bày ở phần sau ) .
Các lệnh read* , print* được gọi là dạng nhập xuất với danh sách trực tiếp và có phần định
dạng tự động ( free format I/O ) .

5.3 Cách viết phần định dạng trong các lệnh xuất / nhập dữ liệu .
Phần trình bày một số các mẫu định dạng thông dụng như dưới đây được minh họa bằng
các lệnh xuất ra màn hình cho dễ quan sát , các mẫu định dạng này còn được dùng để nhập/xuất dữ
liệu sử dụng phương tiện lưu trữ dạng tập tin ( file ) .
5.3.1 Định dạng cho số nguyên .
a) Mẫu định dạng : niw
n : số lần lặp lại mẫu định dạng iw . i : số kiểu integer
w : width , tổng số ô (kí tự ) dùng biểu diễn số nguyên bao gồm phần dấu , phần số và các
khoảng trống phía trước số . Số được viết căn lề phải ( right justified ) . Ví dụ m = 6 , n = -12 với
lệnh xuất dữ liệu :
print ‘(i3,i4)’ , m , n  write (*,’(i3,i4)’) m , n , thể hiện ra như sau :

m n
w=3 w=4

6 - 1 2

Ví dụ : k = 25 ; l = - 40 lệnh write (*, ‘(2i4)’) k , l :

k l
w=4 w=4

2 5 - 4 0

Lưu ý : Định dạng ‘(2i4)’  ‘(i4,i4)’ . Nếu số chữ số ( kể cả dấu ) của số nguyên lớn hơn
w ( thiếu chỗ ) , chương trình sẽ in ra w dấu * .
 Khi nhập hai số nguyên k = 25 , l = - 40 có định dạng i4 bằng bàn phím :
Lệnh read (*,’(2i4)’) k,l
Chúng ta phải nhập vào đúng dạng như sau : ^^25^-40 <Enter> , trong đó kí tự ^ tượng
trưng cho một kí tự trống trên màn hình ( dùng phím space của bàn phím ) .Lệnh read có phần
định dạng thường dùng với dữ liệu dạng tập tin , khi nhập dữ liệu từ bàn phím chúng ta chỉ dùng
kiểu định dạng mặc định : read *, k, l .

b) Mẫu định dạng : niw.m ( w  m)


Trong w ô dành để thể hiện số nguyên , phải có ít nhất m ô chứa kí số (digit ) . Nếu số chữ
số của số nguyên nhỏ hơn m thì sẽ có các số 0 điền vào phía trước . Nếu số chữ số lớn hơn hay
bằng m thì giống như trường hợp định dạng iw ở trên .
63
Ví dụ : m = 123 định dạng i6.4  ^^0123 ! phải có 4 ô có số.
định dạng i6.5  ^00123
n = -123 định dạng i6.4  ^-0123
định dạng i6.5  -00123 ! vừa đủ chỗ
định dạng i6.6  ****** ! không đủ chỗ . Thiếu chỗ cho dấu -

5.3.2 Định dạng cho số thực dạng dấu chấm tĩnh .
Mẫu định dạng : nfw.d
n : số lần lặp lại mẫu định dạng fw.d f : kiểu số thực dạng dấu chấm tĩnh .
w : tổng số ô dùng biểu diễn số thực bao gồm phần dấu  , phần nguyên , dấu chấm thập
phân , phần lẻ thập phân và các khoảng trống phía trước số ( nếu có ) .
d: số ô dành cho phần lẻ thập phân . Số được viết căn lề phải và có thể được làm tròn ở chữ
số cuối cùng .

Ví dụ số thực -57.2867 ; lệnh write (*,’(f10.3)’) viết ra như sau :

w = 10

- 5 7 . 2 8 7
d=3

Lưu ý : nếu a = 123.456 , với định dạng f10.7 ( w = 10, d = 7 ) sẽ không đủ chỗ thể hiện số
a , lúc này chương trình in ra 10 dấu * .
Cần kiểm tra : w  (d + 1[dấu chấm] ) + (số chữ số phần nguyên + 1 [ dấu ] ).
w  d + 2 + số chữ số phần nguyên .
5.3.3 Định dạng cho số thực dạng bậc ( lũy thừa 10 ) .
a) Mẫu định dạng : new.d
n : số lần lặp lại mẫu định dạng ew.d e : kiểu số thực dạng bậc
w : tổng số ô dùng biểu diễn số thực bao gồm phần định trị (mantissa) [ dấu  , phần nguyên
, dấu chấm , phần lẻ thập phân ] ; phần bậc ( gồm : chữ e, dấu  , phần mũ ) , các khoảng trống
phía trước ( Tổng quát : w  d + 7 ) .
d: số ô dành cho phần lẻ thập phân thuộc phần định trị . Số được viết căn lề phải và phần
định trị m có dạng chuẩn hóa ( 0.1  m < 1 ) .
d

 0 . e
w  d +7

Ví dụ : pi = 3.1415926  0.31416 101, lệnh write (*,’(e12.5)’) viết ra như sau :

d=5

0 . 3 1 4 1 6 e + 0 1

w = 12

64
b) Mẫu định dạng : new.dee
Trong định dạng này , số e ở cuối cho biết số chữ số dành cho phần mũ .
d e

 0 . e 
w  5 +d + e

Ví dụ : pi = 3.1415926 , lệnh write (*,’(e12.3e4)’) viết ra như sau :


^0.314e+0001 ! số chữ số dành cho phần mũ là 4 .

c) Các mẫu định dạng ngw.d , nesw.d , ndw.d


+ Mẫu định dạng gw.d sẽ tự động lựa chọn giữa dạng fw.d và dạng ew.d phụ thuộc vào
giá trị của dữ liệu số .
+ Mẫu định dạng kiểu khoa học ( scientific form ) esw.d tương tự như ew.d , nhưng phần
nguyên của phần định trị là số khác số 0 ( nằm giữa 1 và 10 ) . Chiều rộng w  d + 7 .
+ Mẫu định dạng dw.d tương tự mẫu ew.d nhưng dùng cho số thực có độ chính xác kép .
Trong phần bậc , vị trí chữ e sẽ được thay bằng chữ d .
Ví dụ : print ‘(es14.6)’ , 234.5678  ^^2.345678e+02
print ‘(d18.11)’ , 123.45678962_8  ^0.12345678962D+03
( 0.12345678962 103 )

Ví dụ : Chúng ta xem chương trình sau về xuất dữ liệu số thực ra màn hình :
program testinout3
implicit none
real :: x = 2.71828
real (kind = 8) :: y = 2.718281828_8 ! so thuc do chinh xac kep .
write(*,*) " Format tu dong "
write(*,*) x,y
write(*,*)
write(*,*) " Format theo mau dinh dang "
write(*,'(f12.5 ,f16.9)') x ,y ! Dinh dang Fw.d
write(*,'(e15.6, d20.10)') x,y ! Dinh dang Ew.d , Dw.d
write(*,'(e17.6e4, d22.10e4)') x,y ! Dinh dang Ew.dEe , Dw.dEe
write(*,'(g15.6,g18.10)') x,y ! Dinh dang Gw.d
read*
end Format tu dong
2.71828 2.718281828
Kết quả : 
Format theo mau dinh dang
2.71828 2.718281828
0.271828E+01 0.2718281828D+01
0.271828E+0001 0.2718281828D+0001
2.71828 2.718281828

5.3.4 Định dạng cho giá trị kiểu logical .


Mẫu định dạng : lw l : kiểu logical .

65
Khi xuất , sẽ có (w-1) kí tự trống trước kí tự T ( nếu biến logical có giá trị .true.) hoặc kí tự
F ( nếu biến có giá trị .false.) .

^^^T ! nếu test = .true.


Ví dụ : print ‘(l4)’, test 
^^^F ! nếu test = .false.

5.3.5 Định dạng cho giá trị kiểu xâu kí tự ( character ) .


Mẫu định dạng : aw a : kiểu xâu kí tự .
w : tổng số ô biểu diễn xâu kí tự . Nếu w > chiều dài xâu thì sẽ có các kí tự trống chèn ở
phía trước xâu , nếu w < chiều dài xâu thì chỉ có w kí tự đầu của xâu được viết ra ( bị cắt cụt ) .
Nếu w không có : xâu kí tự được xuất theo chiều dài thực của xâu .

Ví dụ : print ‘(a)’, ‘Nam’  Nam


print ‘(a6)’, ‘Nam’  ^^^Nam
print ‘(a2)’, ‘Nam’  Na

Giá trị trực tiếp xâu kí tự có thể đưa vào trong phần định dạng (format ) , dùng để giải thích,
ghi chú như ví dụ sau :
integer :: i = 15
write (*,’( “Gia tri cua i = “ , i4 )’) i !  Gia tri cua i = ^^15

write (*,100) nghiem1, nghiem2


100 format (1x, 'Cac nghiem so : ', f6.2, 2x ,f6.2)

Kết quả : Cac nghiem so : ^^12.45^^^^4.12

5.3.6 Các nội dung định dạng khác ( điều khiển vị trí con trỏ ) .
 Mẫu định dạng : nx
Con trỏ sẽ bỏ qua n ô [ kí tự ] khi đọc hoặc con trỏ nhảy về bên phải n ô trống trước khi
xuất dữ liệu . ( Dịch chuyển theo phương ngang  ) .
Ví dụ : x = 3.4 và y = 9.45 có kiểu real .
write ( * ,’(f6.2, 3x ,f5.3)’)  ^^3.40^^^9.450 ! có 3 ô trống giữa 2 số ( định dạng 3x) .

 Mẫu định dạng : tc ( nhảy theo tab )


c : số nguyên dương là số thứ tự của cột trên dòng text từ trái qua phải . Con trỏ sẽ nhảy
đến cột có thứ tự là c và xuất dữ liệu .
Ví dụ : m = 9 , n = 16 có kiểu integer .
t5 t12
write (* , ‘(t5,”m=”,i3,t12,”n=”,i4)’)  ^^^^m=^^9^^n=^^16
Con trỏ nhảy đến cột 5 và xuất số nguyên m = ^^9 , sau đó nhảy đến cột 12 và xuất số
nguyên n = ^^16 .

 Xuất dữ liệu xong , con trỏ giữ nguyên vị trí , không xuống dòng
write (*,’(2i3,$)’) m , n  ^^9^16_ ! dùng dấu $ trong phần định dạng .
hoặc write (*,’(2i3)’, advance = ‘no’) m , n ! dùng option advance = ‘no’

66
Ghi chú : option advance = ‘no’ không được dùng đi kèm với định dạng fmt = *
print ‘(a,$)’, “ Nhap gia tri cho x = “  Nhap gia tri cho x = _ ! con trỏ chờ nhập
read*, x ! dữ liệu trên cùng dòng .
+ Dấu điều khiển xuống dòng / :
 Khi gặp dấu / , con trỏ nhảy xuống dòng phía dưới .
write (*,’(i3, /,i4)’) m , n  ^^9
^^16
5.4 Ghi /đọc dữ liệu sử dụng tập tin ( file ) .
5.4.1 Giới thiệu chung về tập tin.
Ngôn ngữ Fortran cho phép sử dụng phương tiện tập tin ( file ) còn gọi là tập tin ngoài (
external file ) để lưu trữ dữ liệu trên ổ đĩa cứng , ổ đĩa di động …Chúng ta có thể xuất kết quả tính
toán ra tập tin thay vì xuất ra màn hình , hoặc nhập dữ liệu đầu vào đã được sắp xếp theo một định
dạng xác định từ các tập tin thay vì nhập trực tiếp từ bàn phím . Khi khối lượng dữ liệu xử lý lớn ,
chúng ta phải sử dụng tập tin .
Mỗi tập tin phải có một tên riêng , tên tập tin gồm 2 phần : phần tên và phần mở rộng dùng
để nhận dạng loại tập tin . Ví dụ : toado.dat , solieu.txt , ptbh.f90 , ptbh.exe …Thông thường , các
tập tin chứa dữ liệu dạng text ( gồm các kí tự chữ viết, chữ số , dấu nối …trong bộ mã ASCII )
được tạo bằng các trình soạn thảo văn bản như Notepad hay phần soạn thảo ngay trong môi trường
IDE của các phần mềm biên dịch Fortran , chúng thường có phần mở rộng *.dat , *.txt ...để chúng
ta dễ nhận dạng .
 Phân loại tập tin theo kiểu định dạng :
Tập tin thuộc kiểu được định dạng ( formatted ) nếu chứa các dữ liệu dạng text ( có thể mở
ra xem bằng Notepad ) , trái lại tập tin thuộc kiểu không được định dạng ( unformatted ) nếu chứa
các dữ liệu được ghi ở dạng nhị phân ( mã máy ) .
 Phân loại tập tin theo cách truy cập dữ liệu :
Tập tin thuộc loại truy cập tuần tự ( sequential ) : để truy cập nội dung một mẫu ghi
(record) trên tập tin , chương trình sẽ phải đọc tất cả các mẫu ghi ở phía trước mẫu ghi này . Các
mẫu ghi có thể có kích thước khác nhau .
Tập tin thuộc loại truy cập trực tiếp ( direct ) : các mẫu ghi trên tập tin có kích thước
giống nhau ( ví dụ 4 byte cho một mẫu ghi kiểu real ) , được đánh số thứ tự và mẫu ghi được truy
cập trực tiếp thông qua số thứ tự của mẫu ghi .
5.4.2 Ví dụ về dữ liệu dạng tập tin.
Chúng ta xem và phân tích chương trình như sau :
program main
implicit none
real :: x,y,z
! Mo tap tin , ghi du lieu va dong tap tin .
open (10, file = "d:\work\file01.dat") ! mở file
write (10,'(3f5.2)') 1.2 , 2.3 , 3.4 ! ghi 3 số thực có định dạng vào file
close (10) ! đóng file
! Mo, doc du lieu va dong tap tin .
open (10, file = "d:\work\file01.dat", status = “old”) ! mở file đã có
read (10,*) x,y,z ! đọc từ file và gán cho biến .
close (10) ! đóng file
! Xuat du lieu ra man hinh de kiem tra
print '(3f5.2)',x,y,z
read*
end program main
67
Lưu ý quan trọng : Trước khi ghi dữ liệu lên tập tin hoặc đọc dữ liệu từ tập tin , tập tin này
phải được mở ra bằng lệnh open và được gán ( kết nối ) với một đơn vị logic ( tương tự với khái
niệm biến kiểu file trong một số ngôn ngữ lập trình ) thông qua một số nguyên dương do chúng ta
tự đặt ( từ 1 đến 2 147 483 647 ) . Con số này gắn liền với tập tin khi tập tin được mở và được xem
như số của kênh truyền dữ liệu dành riêng cho một tập tin cụ thể . Thông thường kênh số 5 được
dành cho bàn phím , kênh số 6 dành cho màn hình và số 0 dành cho hệ thống xử lý . Sau khi mở
tập , các lệnh write ( ghi ) / read ( đọc ) / close ( đóng tập ) tiếp theo phải được ghi rõ số của kênh
truyền dữ liệu tương ứng với tập tin mà chúng ta muốn truy cập . Số này phải là duy nhất cho một
tập tin và các tập tin khác nhau sẽ kết nối với các đơn vị logic khác nhau . Trong ví dụ trên , chúng
ta gán unit = 10 cho đơn vị logic hay kênh truyền dữ liệu kết nối với tập tin trên ổ đĩa d , thư mục
work “ d :\work\file01.dat “ ( ghi đầy đủ cả đường dẫn ) .
Các lệnh liên quan đến tập tin của chương trình trên :
 Lệnh mở tập tin , dạng rút gọn : open (10, file = "d:\work\file01.dat")
hoặc dạng có ghi các tham số thích hợp :
open (unit = 10, file = "d:\work\file01.dat", status = “unknown”)
+ unit = số của đơn vị logic liên kết ( số của kênh truyền dữ liệu ) do chúng ta tự đặt và
tránh các số 0, 5, 6 khi sử dụng tập tin ngoài .
+ file = “ tên tập tin .” ( ghi đầy đủ cả đường dẫn nếu cần ) .
+ status = “----“ ( tình trạng của tập tin ) có thể có các giá trị sau :
“ unknown “ : đây là giá trị mặc định ( default ) , tình trạng của tập tin phụ thuộc vào trình
biên dịch . Nếu chúng ta định mở một tập tin chưa có trên đĩa để ghi dữ liệu , thì hệ thống sẽ tự
động tạo ra một tập tin mới . Nếu tập tin đã có thì nội dung mới sẽ được ghi đè lên tập tin ( nội
dung cũ bị mất ) .
“old” : tập tin bắt buộc phải có trên đĩa . Nếu không có , chương trình sẽ báo lỗi . Thường
sử dụng cho việc đọc tập tin đã có trên đĩa .
“new” : tập tin bắt buộc phải chưa có trên đĩa . Thường sử dụng cho việc ghi / xuất dữ liệu
ra một tập tin hoàn toàn mới .
“replace” : nếu tập tin đã có , nó sẽ bị xóa và một tập tin cùng tên sẽ được tạo ra . Nếu tập
tin chưa có , một tập mới sẽ được tạo ra .
Ghi chú : Lệnh open tổng quát có rất nhiều tham số đi kèm , chúng ta sẽ tìm hiểu dần ở các
phần tiếp theo .

 Lệnh ghi dữ liệu lên tập tin ( k là số của kênh truyền dữ liệu ) :
write ( k , * ) < danh sách dữ liệu . > ! định dạng tự động .
write ( k , ‘(phần định dạng )’ ) < danh sách dữ liệu . > ! dữ liệu được ghi
! theo định dạng được mô tả trong phần định dạng .

 Lệnh đọc dữ liệu từ tập tin ( k là số của kênh truyền ) và gán cho các biến :
read ( k , * ) < danh sách các biến . > ! định dạng tự động .
read ( k , ‘(phần định dạng )’ ) < danh sách các biến . > ! dữ liệu được đọc
! theo định dạng được mô tả trong phần định dạng .
Lưu ý : danh sách các biến phải tương ứng với dữ liệu sắp xếp trên tập tin.
 Lệnh đóng tập tin : sau khi ghi / đọc dữ liệu xong , tập tin phải được đóng lại bằng lệnh
close (k) để bảo vệ dữ liệu .
Như vậy , chương trình ví dụ trên đầu tiên sẽ mở một tập tin trên đĩa có tên kèm theo đường
dẫn là d :\work\file01.dat và gán số 10 cho tập tin này là số của đơn vị logic liên kết ( số của kênh
truyền dữ liệu ) . Tập tin này có thể chưa có hoặc đã có trên đĩa ( xem phần status = “unknown” ở

68
trên ) . Lệnh write tiếp theo sẽ ghi 03 số thực có định dạng f5.2 vào tập ( được ghi trên một dòng
) , sau đó là lệnh đóng tập close (10) , kết thúc phần ghi lên tập tin .
Lệnh open thứ hai sẽ mở lại tập tin nói trên ( lúc này tham số status có thể gán là old vì tập
tin đã có ) , chương trình đọc dữ liệu từ tập tin này , gán cho các biến thực x, y, z và đóng tập ,
kết thúc phần đọc . Lệnh print* xuất giá trị các biến x,y,z từ bộ nhớ ra màn hình để kiểm tra .

Hình 5.1 : Nội dung tập tin file01.dat 

5.4.3 Quản lý các lỗi khi mở tập tin .


Các lệnh mở tập tin thường đi kèm với các tham số quản lý khi gặp lỗi .
a) open (unit = 10, file = “file01.dat”, status = “old”, iostat = ierr )
Trong mẫu lệnh mở tập tin trên , tham số ( tùy chọn ) iostat = ierr cho phép chương trình
được tiếp tục trong trường hợp việc mở tập tin gặp lỗi ( ví dụ tập tin không tồn tại trên đĩa ) . Biến
ierr phải được khai báo là biến integer ở đầu chương trình và có giá trị bằng 0 nếu việc mở tập tin
bình thường hoặc có giá trị khác 0 trong trường hợp mở tập bị lỗi .
b) open (unit = 10, file = “file01.dat”, status = “old”, err = 100 )
Tham số tùy chọn err = 100 cho phép chương trình tiếp tục nếu xảy ra lỗi khi mở tập tin ;
trong trường hợp này chương trình sẽ nhảy đến thực hiện dòng lệnh có gắn nhãn 100 .

5.4.4 Các tham số đi kèm với lệnh mở tập tin open .

Bảng 5.1 : Các tham số của lệnh open .

Tham số Ý nghĩa “ Các tùy chọn “

Status = Tình trạng Unknown (mặc định ) ; Old ( đã có ) ; New ( chưa có ) ; Replace
tập tin. (thay thế ) .
Action = Thao tác với ReadWrite (mặc định) ; Read ( chỉ để đọc ) ; Write ( chỉ để ghi )
tập tin .
Form = Kiểu tập tin Formatted (mặc định , file dạng text ) ; Unformatted ( file dạng nhị
phân ) .
Access = Kiểu truy Sequential (mặc định , truy cập tuần tự ) ; Direct ( truy cập trực tiếp
cập . )
Recl = n n : kích Bắt buộc phải có tham số này nếu kiểu truy cập là Direct .
thước mẫu
ghi tính
bằng byte .
Position = Vị trí con trỏ Dùng với kiểu truy cập tuần tự .
file . Asis ( mặc định ) : mở file và đứng ở vị trí mặc định sau khi file được
kết nối .
Append : tiếp tục ở sau mẫu ghi cuối cùng . ( Nối đuôi ) .
Rewind : trở về đầu tập tin.
Iostat =ierr Quản lý lỗi Biến nguyên ierr có giá trị = 0 nếu mở tập tin không có lỗi , có giá
khi mở file. trị  0 nếu mở tập tin bị lỗi. Phải khai báo ierr ở đầu chương trình .
Err = nhãn Quản lý lỗi Nếu có lỗi khi mở tập tin, chương trình nhảy đến dòng lệnh có gắn
khi mở file. nhãn tương ứng với tham số err .

69
Ví dụ về quản lý lỗi khi mở tập tin .
implicit none
integer :: ierr

open (unit = 20 , file = “ data02.dat “, iostat = ierr )
if (ierr /= 0) then
print* , “ Co loi khi mo file “ ! câu thông báo lỗi khi mở file .
read* ! dung lai de doc thong tin .
stop ! ket thuc chuong trinh
end if
….
5.4.5 Đọc dữ liệu từ tập tin .
 Mẫu ví dụ về câu lệnh đọc dữ liệu từ tập tin và gán cho biến :
read (10,*) x ! đọc file liên kết với kênh số 10 với định dạng tự động
! và gán cho biến x .
read (10,*) x,y,z  read ( unit = 10 , fmt = * ) x,y,z
! đọc file liên kết với kênh số 10 với định dạng tự động và gán cho các biến x,y,z
read (unit = 20, fmt = ‘(a)’ ) dong ! biến dong có kiểu character .
! Đọc file liên kết với kênh số 20 với định dạng là xâu kí tự ( file gồm các dòng text ).
! Đọc từng dòng của file xong gán cho biến dong ( có len khai báo phù hợp với chiều dài
dòng text trên file )
 Tham số unit có thể được gán :
+ Số của kênh truyền dữ liệu kết nối với tập tin cần truy cập.
+ Dấu * , khi đọc dữ liệu từ bàn phím .
+ Xâu kí tự , dùng để chuyển đổi nội dung xâu kí tự thành dữ liệu số : read (xkt ,* )
x ( xem thêm phần tập tin trong ( internal file ) ) .
 Tham số format fmt có thể được gán :
+ Dấu * : định dạng tự động .
+ Xâu kí tự định dạng ‘(phần định dạng )’, quy định rõ phần định dạng được sắp xếp
trong tập tin .
Các tùy chọn về quản lý lỗi khi đọc dữ liệu từ tập tin . Các mẫu như sau :
read (10,*, iostat = ierr ) x ! khi có lỗi , biến nguyên ierr có giá trị  0 .
read (10,*, err = 100) x ! chương trình nhảy đến câu lệnh có nhãn 100 khi có lỗi.
read (10,*, end = 200) x ! chương trình nhảy đến câu lệnh có nhãn 200
! khi đọc đến cuối file ( hết dữ liệu để đọc ) .

5.4.6 Ghi dữ liệu ra tập tin .


 Mẫu ví dụ về lệnh ghi dữ liệu ra tập tin ( ví dụ x,y là hai biến real ) :
write (20,*) x,y  write (unit = 20 , fmt = * ) ! Ghi giá trị hai biến x,y lên tập tin
! được liên kết với kênh số 20 với định dạng tự động .
write (unit = 20 , fmt = ‘(2f10.2)’) x,y ! Tương tự như trên nhưng phần định dạng
! khi ghi hai số x,y là 2f10.2 . Dữ liệu được ghi lên một dòng tập tin.
 Tham số unit có thể được gán :
+ Số của kênh truyền dữ liệu kết nối với tập tin cần truy cập .
+ Dấu * , khi ghi dữ liệu ra màn hình .
+ Xâu kí tự , dùng để chuyển đổi dữ liệu dạng số thành xâu , ví dụ : write (xkt ,* )
12. ; lúc này xkt = ‘12’ ( xem thêm phần tập tin trong ( internal file ) ) .

70
 Tham số format fmt có thể được gán :
+ Dấu * : ghi theo định dạng tự động .
+ Xâu kí tự định dạng ‘(phần định dạng )’, quy định rõ phần định dạng khi ghi dữ
liệu vào tập tin .
Lưu ý : đối với file dạng text ( formatted ) mỗi lệnh write tương ứng với việc ghi dữ liệu ra
một dòng xong con trỏ xuống dòng kế tiếp . Trường hợp viết xong không muốn xuống dòng , trong
phần định dạng phải thêm kí hiệu $ hoặc đưa vào option advance = “no” . Ví dụ :
write (* , fmt = ‘(2f10.2,$ )’) x,y
write (* , fmt = ‘(2f10.2)’, advance = “no” ) x,y

Tùy chọn advance = “no” không được dùng chung với fmt = *
Có thể đưa dòng text ( giá trị trực tiếp xâu kí tự ) vào phần định dạng của lệnh write :
nmax = 100
write (*,’(“nmax = “,i4)’) nmax !  nmax = ^100
Hoặc để phần định dạng riêng ở câu lệnh format liên kết :
write (*,300) nmax ! phần định dạng nằm ở câu lệnh có nhãn 300
300 format (“namx = “, i4)
5.4.7 Lệnh rewind (k) hoặc rewind ( unit = k) .
Có tác dụng đưa con trỏ file về đầu tập tin để có thể đọc/ ghi dữ liệu từ vị trí đầu tiên của
tập tin . Có thể đặt lệnh này ở một dòng riêng hoặc đưa vào option position = “rewind” của lệnh
open . Ví dụ :

open (10, file = “ solieu01.txt” , status = “old” )
rewind (10) ! đưa con trỏ file về vị trí đầu tiên của tập tin . Đọc dữ liệu từ đầu.
read (10,’(3f5.2)’ ) x,y,z

Lưu ý : khi muốn ghi tiếp tục vào một tập tin đã có , chúng ta sử dụng option position = “
append “ trong lệnh open ( đưa con trỏ file về cuối tập tin ) .

5.4.8 Lệnh đóng tập tin .


Khi ghi dữ liệu vào hoặc đọc xong dữ liệu từ tập tin , chúng ta phải đóng tập tin lại để bảo
vệ dữ liệu bằng lệnh close (k) hoặc close ( unit = k ) , với k là số của đơn vị logic liên kết với tập
tin .
Lệnh close ( k, status = “ “) có thể kèm theo tham số status = “keep” ( giữ tập là giá trị
mặc định ) hoặc status = “delete “ ( xóa tập ) .
5.4.9 Khái niệm về tập tin trong ( internal file ) .
Khác với tập tin ngoài ( external file ) được dùng để lưu trữ dữ liệu lâu dài trên các thiết bị
ngoại vi như ổ đĩa cứng ,usb như chúng ta đã biết ở các phần trên …Trong Fortran 90 , chúng ta
còn có thể ghi/ đọc dữ liệu với tập tin trong ( internal file ) . Đây là dạng chuyển đổi từ dữ liệu
dạng số sang dạng xâu kí tự ( ghi số vào biến xâu kí tự ) hoặc ngược lại , đọc ( chuyển đổi ) dữ
liệu từ biến xâu kí tự ra dữ liệu số . Tập tin trong sử dụng biến xâu kí tự như là một biến trung gian
được khai báo tường minh ở đầu chương trình để lưu trữ dữ liệu trong bộ nhớ của máy tính khi
chương trình đang thực thi .Tập tin trong là một dạng vào/ra ( I/O) của Fortran . Ví dụ :
program main
implicit none
real :: x
character (len = 5) :: xau xau = 15.
! Doi so thanh xau
x = 20.
71
write (xau,*) 15. ! ghi so 15. vao xau
print*,"xau = "//xau
! Doi xau thanh so Kết quả .
xau = "20."
read (xau ,*) x ! doc tu xau va gan cho x
print*,"x = ",x
read*
end program main

Ví dụ : Đọc từ biến xâu kí tự ra dữ liệu số :

character ( len = 9) :: day = ‘123456789’
integer :: n
real :: x

read (day,’(i3,f6.2)’) n,x
write (*,’(“n = “, i3 ,” x = “, f7.2)’) n, x  n = 123 x = 4567.89

+ Ghi số vào biến xâu kí tự :


character ( len = 80) :: tap
real :: x = 3.45

write (tap ,’ (“ x : “, f8.3)’) x
write (*,*) tap  x : ^^^3.450 ! ^ tượng trưng cho kí tự trống .

Ví dụ về đọc file theo từng dòng , xong viết ra màn hình
program main
implicit none
integer :: ierr
character (len = 50) :: dong ! co toi da 50 ki tu
open (10,file ='d:\danhsach.txt', status = 'old', &
*************************
& iostat = ierr , err = 100)
Dai Hoc Bach Khoa TP.HCM
if (ierr /= 0 ) then Dai Hoc Kien Truc TP.HCM
print*,' Co loi khi mo file - dung tham so iostat ' Dai hoc Kinh Te TP.HCM
read* Dai hoc Tong Hop TP.HCM
stop ! Ket thuc chuong trinh Trung Tam Ngoai Ngu DHSP
end if Trung Tam day nghe Q.5
************************
do
read (10,'(a)',end = 30) dong ! khi doc het file nhay file d:\danhsach.txt
! den dong co nhan 30
write (*,'(a)') dong ! viet ra man hinh theo tung dong.
end do
30 close (10)
read*
stop
100 print*,' Co loi khi mo file - dung tham so err '
read*
end program main

72
Lưu ý : vòng lặp do sẽ đọc từng dòng của tập tin d :\danhsach.txt ( được soạn sẵn bằng
Notepad ) và xuất ra màn hình . Khi đến cuối file ( hết dữ liệu để đọc ) chương trình sẽ nhảy đến
câu lệnh có nhãn 30 và đóng tập tin lại , kết thúc chương trình .
Nếu tập tin không có trên đĩa , chương trình sẽ xuất ra dòng thông báo lỗi :
Co loi khi mo file - dung tham so err
5.4.10 Tập tin không được định dạng ( unformatted file )
Chúng ta có thể ghi / đọc dữ liệu sử dụng tập tin kiểu không được định dạng ( unformatted
) , dữ liệu được ghi ở dạng mã máy ( hệ nhị phân ) .
Trong lệnh open phải đưa vào option form = “ unformatted “.
Ví dụ :
open ( 10, file = “ketqua.dat “ , form = “unformatted “ )

write (10) x,y

read (10) x,y
close (10)

Các lợi ích khi sử dụng tập tin unformatted :


+ Tiết kiệm được chỗ ghi trên đĩa .
+ Không mất thời gian để chuyển đổi dữ liệu từ dạng nhị phân sang dạng text và ngược lại
+ Giữ được độ chính xác của các biến số học .
Lưu ý : Dữ liệu được lưu trên các tập tin dạng này không xem được bằng Notepad.

5.4.11 Tập tin truy cập trực tiếp .


Đối với tập tin kiểu truy cập trực tiếp , chúng ta phải đưa vào tham số access = “direct “ và
tham số recl = k , cho biết chiều dài một mẫu ghi là k byte . Các mẫu ghi trên tập tin đều có cùng
chiều dài và được đánh số thứ tự . Ví dụ :
open (10, file = “ ketqua.dat “, access = “direct” , recl = 4 )
Ở đây chiều dài mỗi mẫu ghi là 4 byte ( ví dụ mẫu ghi là số thực có độ chính xác đơn . )
Các lệnh write , read liên kết với tập tin kiểu này có thể truy cập trực tiếp đến mẫu ghi thứ
n qua tham số rec được gán bằng n :
write (10, rec = n ) hoặc read (10, rec = n) .

Bạn đọc phân tích và chạy chương trình sau về ghi và đọc dữ liệu từ file kiểu truy cập trực
tiếp , mỗi mẫu ghi là một số thực real ( 4 byte ) :

program main
real :: x
integer :: i
open (10,file = "ketqua.dat",access = "direct", recl = 4 , form = "unformatted")
do i =1,20
x=i
write (10,rec = i) x ! ghi 20 so thuc vao file
end do
close (10)
open (10,file = "ketqua.dat",access = "direct", recl =4 , form = "unformatted")
do i = 5,10
read (10,rec = i) x ! doc tu mau ghi so 5 den so 10
73
print '(f5.1,$)', x 
end do 5.0 6.0 7.0 8.0 9.0 10.0
close (10)
read* Kết quả ghi ra màn hình .
end
5.4.12 Sử dụng namelist để chứa dữ liệu .
Chúng ta có thể tạo ( khai báo ) một danh sách các biến trong một namelist , được đặt một
tên , và gán các giá trị cho các biến này :
namelist / my_NML/ x,y,m,n ! x,y biến real và m,n biến integer
my_NML là tên của namelist do chúng ta tự đặt .
Sau khi liên kết với một tập tin đã mở , các lệnh write ( k, nml = my_NML) hoặc read (k,
nml = my_NML ) sử dụng tham số nml = < tên namelist> , có thể ghi danh sách này lên tập tin
hoặc đọc từ tập tin đã có danh sách này . Ví dụ :
program main
real :: x,y
integer :: m,n
namelist /my_NML / x,y,m,n ! tạo danh sách có tên là my_NML chứa 4 biến
x =1.23 ; y = 2.34 ; m = 5 ; n = 10 ! gán giá trị cho các biến
open (10,file = "vidu05.dat")
write (10, nml = my_NML) ! ghi namelist có tên my_NML vào tập tin
close (10)
open (10,file = "vidu05.dat")
read (10,nml = my_NML) ! Đọc namelist có tên my_NML từ tập tin vào bộ nhớ
close (10)
print '(2f6.2,2i4)' , x,y,m,n ! viết ra màn hình  1.23 2.34 5 10
read*
end

Hình 5.2 : Nội dung namelist my_NML được ghi vào tập vidu05

5.4.13 Sử dụng lệnh inquire để truy vấn thông tin .


Lệnh inquire cho phép truy vấn rất nhiều thông tin liên quan đến tập tin , chúng ta chỉ thực
hành hai trường hợp đơn giản sau :
a) Lệnh inquire dùng để hỏi xem một tập tin có tồn tại trên đĩa hay không trước khi tiến
hành mở tập tin bằng lệnh open . Kết quả trả về một biến kiểu logical .
Ví dụ :
logical :: test

inquire ( file = 'ketqua.dat', exist = test ) ! dùng tham số exist
if (.not. test) then
print*,'Khong co file ketqua.dat' ! thông báo không có file , test = .false.
read*
74
stop
end if
b) Lệnh inquire dùng để hỏi kích thước của các biến ( các mẫu ghi , file kiểu unformatted
) trong một danh sách đi kèm .Lệnh sử dụng tham số iolength = <biến integer > . Kết quả trả về là
một số integer cho biết tổng số byte các biến chiếm chỗ
integer :: long

inquire ( iolength = long ) x,y ! 02 mẫu ghi kiểu real mặc định
print*, long  8
5.5 Thực hành .
Bạn đọc phân tích các câu lệnh và chạy hai chương trình sau . Cần chuẩn bị trước các tập
tin dữ liệu đầu vào và lưu tại thư mục phù hợp ( Lưu ý : do lệnh read đọc dữ liệu trên file theo từng
dòng một nên số lượng biến được gán giá trị phải tương ứng với số lượng dữ liệu trên dòng.
5.5.1 Chương trình sau sẽ đọc n ( = 10 ) điểm số từ một file d:\solieu02.txt , sau đó xuất ra
màn hình để kiểm tra và tính điểm trung bình .
program diemtb
implicit none 12.50
18.65
integer, parameter :: n = 10
11.25
integer :: i 14.80
real :: diem , tong = 0.,dtb 16.00
open (unit = 2, file ='d:\solieu02.txt', status = 'old') 15.85
do i =1,n 13.25
read (unit = 2, fmt = * ) diem ! doc diem tu file 11.75
print '(f6.2)', diem ! viet ra man hinh 17.20
tong = tong + diem ! cong don 14.20
end do
close (2) Diem trung binh : 14.55
dtb = tong /real(n) ! diem trung binh
print*
print '(a,f6.2)'," Diem trung binh : ", dtb Kết quả trên màn hình .
read*
end program diemtb
5.5.2 Chương trình sau đọc điểm toán , lý , hóa của từng học sinh từ một tập tin d:\diem.txt
, sau đó tính tổng điểm và điểm trung bình của từng học sinh . Kết quả xuất ra màn hình .
File d:\diem.txt
program diem
implicit none Anh 8.5 6.2 7.5 22.2 7.4
character (len = 8) :: ten Binh 7.2 9.1 5.4 21.7 7.2
real :: toan,ly,hoa,tong,tb Chinh 5.0 7.3 4.3 16.6 5.5
open (unit = 1, file ='d:\diem.txt',status ='old') Dung 6.5 7.5 7.6 21.6 7.2
do Hung 8.0 6.5 5.0 19.5 6.5
read (unit = 1, fmt = *, end = 20) &
ten ,toan,ly,hoa ! đọc 4 số liệu trên một dòng
tong = toan+ly+hoa ; tb = tong/3. Kết quả trên màn hình.
print '(a8,3f5.1,2x,2f5.1)', ten,toan,ly,hoa,tong,tb ! xuất ra màn hình trên một dòng.
end do
20 close (1) ! nhay den day khi doc het du lieu tren file
read*
end

*****
75
Chương 6 : CẤU TRÚC DỮ LIỆU KIỂU MẢNG ( ARRAY )

6.1 Một số khái niệm .


Trong Fortran , cấu trúc mảng ( array ) được sử dụng để lưu trữ , xử lý một tập các
phần tử có cùng kiểu dữ liệu và có liên quan với nhau tạo thành một đối tượng như danh sách ,
vectơ , ma trận …Các phần tử của mảng sử dụng một tên chung là tên mảng ( quy tắc đặt tên mảng
tương tự như đặt tên biến ) và được phân biệt với nhau bằng các chỉ số thích hợp ( là các số nguyên
) . Trong bộ nhớ , các phần tử của mảng được sắp xếp liền kề nhau theo một thứ tự quy định .
Mảng một chiều ( rank = 1 ) : tương ứng với các đối tượng như vectơ trong toán học, dãy
số.
Mảng hai chiều ( rank = 2 ) : tương ứng với các đối tượng như ma trận , bảng số liệu gồm
có hàng và cột .
Mảng ba chiều ( rank = 3 ) : tương ứng với hình ảnh một tập các bảng tính gồm các sheet
( tờ ) được đánh số thứ tự và được xếp chồng lên nhau , mỗi sheet là một bảng tính gồm có hàng
và cột .
Số chiều lớn nhất có thể có của một mảng là 7 .
Mảng có thể được khai báo bình thường theo dạng tĩnh ( kích thước mảng được khai báo cố
định trong bộ nhớ ngay từ đầu chương trình ) hoặc có thể được khai báo theo dạng động ( dynamic
array ) , trong trường hợp này , khi chạy chương trình kích thước vùng nhớ sẽ được cấp phát cho
mảng khi có yêu cầu và có thể thu hồi lại vùng nhớ đã cấp phát này.
Fortran 90 cho phép sử dụng tên mảng như một biến đơn trong các biểu thức tính toán ,
miễn là các toán hạng có dạng như nhau , chúng ta không cần phải sử dụng các vòng lặp do với
biến đếm như các phiên bản trước , văn bản chương trình vì thế sẽ gọn hơn . Cụ thể , các toán tử
số học + - * / ** cho phép kết hợp các toán hạng là các mảng cùng dạng , các giá trị hằng số ;
các hàm chuẩn của Fortran cũng cho phép sử dụng đối số là mảng thay vì là giá trị đơn .

6.2 Khai báo mảng theo dạng tĩnh .


6.2.1 Khai báo mảng một chiều .
 Ví dụ về khai báo hai vectơ va , vb , mỗi vectơ gồm 3 thành phần số là các số thực kiểu
real , các chỉ số gồm 3 số nguyên 1,2,3 :
real ,dimension (1:3) :: va, vb ! vectơ trong R3
real ,dimension (3) :: va, vb ! cận dưới là 1 có thể bỏ qua.
Chúng ta sử dụng từ khóa dimension để khai báo mảng . Phần chỉ số , là số nguyên , đi kèm
trong cặp dấu ngoặc đơn ( ) cho biết số phần tử của mảng và dãy giá trị các chỉ số có dạng (cận
dưới : cận trên ) .
Vectơ va như trên sẽ có 3 thành phần : va (1), va (2), va (3)

va(1) va(2) va(3)

Cách khai báo tương đương , không dùng từ khóa dimension :


real :: va (1:3) , vb (1:3)  real :: va (3) , vb (3)
 Ví dụ : real , dimension ( -1 : 1 ) :: vecto
Mảng vecto khai báo như trên có 3 phần tử : vecto(-1), vecto (0), vecto (1)
Nhận xét : chỉ số của mảng có thể là số nguyên âm , số 0 , số nguyên dương .

6.2.2 Khai báo mảng hai chiều .


 Ví dụ về khai báo một ma trận có 3 hàng , 2 cột ; các phần tử là các số thực .

76
real ,dimension (1:3,1:2 ) :: matran  real ,dimension ( 3, 2 ) :: matran
Chỉ số hàng 1 : 3 được khai báo trước chỉ số cột 1 : 2

Dimension 2

matran (1,1) matran (1,2)


matran (2,1) matran (2,2)
matran (3,1) matran (3,2)
Dimension 1

Lưu ý : phần tử matran (3,1) ở hàng 3 cột 1.


 Ví dụ : Khai báo vectơ , matrận có kích thước mỗi chiều phụ thuộc vào một hằng nguyên
n được khai báo trước .
integer, parameter :: n = 3
real, dimension (1: n) :: vecto ! vectơ trong R3
real,dimension (1: n,1: n) :: matran ! ma trận vuông cấp 3

+ Trong khai báo mảng , kiểu của các phần tử có thể là số nguyên, số thực, số phức , kiểu
logical , xâu kí tự , dữ liệu dạng hỗn hợp ( bản ghi ) .
+ Mỗi phần tử của mảng như vecto (2) , matran (2,2) trong các ví dụ trên được xử lý như
một biến đơn có kiểu real trong các biểu thức , phép gán .
+ Các chỉ số có thể viết ở dạng biểu thức nguyên , có giá trị phù hợp với phạm vi của chỉ số
, ví dụ vecto ( i ) ; matran (2+i , j*3 )
+ Khi có ma trận vuông cấp 3 : matran (1:3,1:3) , chúng ta có thể trích ra các vectơ hàng ,
vectơ cột , ma trận con dùng cách viết sử dụng chỉ số như sau :
matran (1, :)  vectơ hàng thứ 1 matran (: , 2 )  vectơ cột thứ 2
matran (1:2, 1:2)  ma trận con 2 x 2 , góc trái phía trên .

+ Một số thuật ngữ về mảng :


 Rank : số nguyên dương , cho biết số chiều của mảng ( tối đa là rank = 7) . Ví dụ vectơ
có rank = 1 , ma trận có rank = 2 .
 Extent : số nguyên dương , cho biết số phần tử của mỗi chiều . Ví dụ đối với ma trận 3 x
2 ( 3 hàng , 2 cột ) thì extent theo chiều thứ nhất là 3 , theo chiều thứ hai là 2 .
 Shape : dạng của mảng , là dãy các số extent của từng chiều được viết theo thứ tự , cách
nhau bằng dấu phẩy , nằm trong cặp dấu ngoặc đơn . Ví dụ :vectơ có 3 thành phần có shape 
(3) ; shape của ma trận có 4 hàng 3 cột  (4,3)
 Size : số phần tử của mảng , là tích các số extent .
Ví dụ : ma trận có shape là ( 4,3) có size = 4 x 3 = 12 .
Ví dụ : real, dimension (1:3 , 1:6, 1:8) :: hoso
Rank = 3 ; Extent : 3 , 6 , 8 ; Shape ( 3,6,8) ; Size = 144 .

Các ma trận A,B,C khai báo như sau có cùng shape là ma trận 5 hàng , 3 cột ; nhưng chỉ
số có các cận khác nhau.
real, dimension (-4:0 , 0:2) :: A
real , dimension (5,3) :: B  real , dimension (1: 5,1: 3) :: B
real, dimension (3:7 , 2:4) :: C

77
B(1,1) B(1,3)
C(3,2) C(3,4)
A(-4,0) A(-4,2)

A(-2,1)

A(0,0) A(0,2)
B(5,1) B(5,3)
C(7,2) C(7,4)

Hình 6.1 : Vị trí tương ứng các phần tử của ma trận A,B,C cùng dạng

6.3 Thứ tự vị trí các phần tử của mảng trong bộ nhớ .


Chúng ta có khai báo như sau :
real, dimension (1: 3) :: vecto ! vectơ trong R3
real,dimension (1: 3,1: 3) :: matran ! ma trận vuông cấp 3 .
 Các phần tử vecto (1), vecto (2) ,vecto (3) có vị trí liền kề nhau trong bộ nhớ từ địa chỉ
thấp đến địa chỉ cao .
 Các phần tử của ma trận có vị trí theo thứ tự như sau trong bộ nhớ :
matran (1,1) , matran (2,1) , matran (3,1) , matran (1,2), matran (2,2), matran (3,2), matran
(1,3) , matran (2,3) , matran (3,3)
Quy luật : chỉ số thứ nhất ( chỉ số hàng ) thay đổi trước , rồi mới đến chỉ số thứ hai ( chỉ số
cột ) .
Do vậy , khi sử dụng vòng lặp do , chúng ta viết như sau sẽ có hiệu quả hơn :
do j = 1,n ! chỉ số cột nằm ở vòng do ngoài
do i = 1,n ! chỉ số hàng nằm ở vòng do trong
u = matran (i,j) ! chỉ số hàng i thay đổi trước .
print*,u
end do
end do
Lưu ý : Đối với ma trận , Fortran ưu tiên điền ( add ) các phần tử lần lượt theo từng cột (
chỉ số hàng thay đổi trước ) , trừ khi có khai báo thay đổi thứ tự mặc định này ( xem trình bày ở
các phần sau ) . Cần nhớ quy luật này để khi vào giá trị của ma trận từ bàn phím bằng lệnh read *,
chúng ta vào đúng giá trị cho dòng , cột của ma trận .

6.4 Khai báo mảng theo dạng cấp phát động ( dynamic array )
Chúng ta khai báo một vectơ trong R3 và một ma trận vuông cấp 3 theo dạng cấp phát động
( cấp phát vùng nhớ cho mảng khi chạy chương trình ) như sau :
integer :: n
real,dimension (:) ,allocatable :: vecto ! khai báo thuộc tính allocatable cho vecto
real,dimension (: , :) ,allocatable :: matran ! và matran
n=3

78
allocate ( vecto (1: n) , matran (1: n, 1: n) ) ! chỉ định việc cấp phát vùng nhớ .
if (allocated (vecto)) print *,” vecto da duoc cap “ ! test việc cấp phát .
if (allocated (matran)) print *,” matran da duoc cap “

deallocate (vecto, matran ) ! thu hồi lại vùng nhớ .

program main
implicit none
integer :: n
real,dimension (:), allocatable :: vecto
real,dimension (:,:), allocatable :: matran
n=3
allocate (vecto (1:n), matran (1:n,1:n))
if (allocated (vecto)) print*,"vecto Yes"
if (allocated (matran)) print*,"matran Yes" vecto Yes
vecto = (/1.,2.,3./) ! gan gia tri matran Yes
print*,vecto 1. 2. 3.
deallocate (vecto,matran) Da thu hoi vung nho !
print*," Da thu hoi vung nho ! " vecto No
if (allocated (vecto)) then
print*," vecto Yes " Kết quả .
else
print *, " vecto No "
end if
read*
end
Trong phần khai báo biến mảng cấp phát động , chúng ta sử dụng thuộc tính allocatable (
từ khóa ) ; các chỉ số của dimension được để trống .
Chỉ thị allocate ( ) thực hiện việc cấp phát vùng nhớ cho các biến mảng và chỉ thị deallocate
( ) thu hồi lại các vùng nhớ đã cấp phát .
Hàm allocated ( ) có kiểu logical , dùng để test xem biến mảng có được cấp phát vùng nhớ
hay chưa ?
 Khai báo mảng cấp phát động có kèm theo tham số stat để quản lý lỗi :
integer :: n = 3, ierr
real,dimension (:), allocatable :: vecto
allocate ( vecto (1:n), stat = ierr )
if (ierr /= 0) print*,” Co loi khi cap phat .”

Nếu bộ nhớ không đủ hay mảng đã được cấp phát rồi , ierr sẽ có giá trị  0 .
Chúng ta xem chương trình sau : cấp phát vùng nhớ cho một biến mảng một chiều , kiểu số
thực chính xác kép , có số phần tử tăng dần cho đến khi bộ nhớ không đủ để cấp phát ( biến ierr 
0) . Qua đó biết được vùng nhớ khả dụng của bộ nhớ .
program testmemory
implicit none
integer, parameter :: r =8
integer :: ierr,n
real (kind = r ),dimension (:),allocatable :: vecto
n= 0 ; ierr = 0
do while (ierr == 0)
n = n + 1024*1204
79
if (allocated (vecto)) deallocate (vecto)
allocate ( vecto (1:n) , stat = ierr )
end do
n = n - 1024*1024
print*, " Vung nho kha dung (MB) : ", (n*r)/(1024*1024)
read*
end
Kết quả : Vung nho kha dung (MB) : 1854
6.5 Nhập giá trị cho các phần tử của mảng .
 Nhập ‘ thủ công ‘ cho từng phần tử của mảng .
Gán giá trị cho ma trận 3 hàng 2 cột các số thực :
 1. 4. 
real, dimension (1:3, 1:2) :: matran  
matran   2. 5. 
 3. 6. 
matran (1,1) = 1. ; matran (2,1) = 2. ; matran (3,1) = 3.  
matran (1,2) = 4. ; matran (2,2) = 5. ; matran (3,2) = 6.
 Nhập các phần tử liên tiếp của mảng khi chạy chương trình bằng chỉ thị read*, các phần
tử được liệt kê cách nhau dấu phẩy hoặc khoảng trống ( dùng phím cách space ) .
real , dimension (1:3,1:2) :: a ! Ma trận 3 hàng, hai cột
real, dimension (3) :: v ! Vectơ trong R3
print*,' Vao cac phan tu cua ma tran a (3,2) : ' ; read*,a
! Vào 1.,2.,3.,4.,5.,6. hoặc 1. 2. 3. 4. 5. 6. xong Enter .
print*,' Vao cac phan tu cua vecto v (3) : ' ; read*,v
! Vào 1.,2.,3. hoặc 1. 2. 3. xong Enter .
 Nhập qua một công thức ( nếu thiết lập được công thức ) cho từng phần tử của mảng .
real, dimension (1:3, 1:2) :: matran
integer :: i,j

do j = 1,2
do i = 1,3
matran ( i,j ) = i + (j-1) *3 ! dùng vòng lặp do
end do
end do
 Nhập qua một file :
real, dimension (1:3, 1:2) :: matran
integer :: i,j
..
open ( 10, file = “ solieu.dat “ )
do i = 1,3
read ( 10 ,* ) matran (i,1) , matran (i,2) ! đọc theo từng hàng .
end do
close (10) 1. 4.
Hoặc : 2. 5.
real, dimension (1:3, 1:2) :: matran 3. 6.
integer :: i,j
..
open ( 10, file = “ solieu.dat “ ) file : solieu.dat
do i = 1,3
read ( 10 ,* ) (matran (i,j) , j =1,2) ! sử dụng vòng lặp ngầm cho biến j

80
end do
close (10)
Nhận xét : các số liệu được đọc theo từng hàng ứng với mỗi chu trình của vòng do .
Trong lệnh read (10,* ) ( matran (i,j), j = 1,2) , ứng với mỗi giá trị của biến đếm i ,biến đếm j
bên trong lấy các giá trị từ 1 đến 2 , số phần tử của mảng được đọc là 2 cho mỗi lần đọc.
6.6 Sử dụng công cụ array constructor để nhập giá trị cho mảng .
 Chúng ta xem ví dụ sau về việc nhập giá trị cho một vectơ có 6 phần tử .
integer :: i
real, dimension (1: 6) :: vecto
vecto = (/2.,4.,6.,8.,10.,12./) !  nhập các phần tử 2. 4. 6. 8. 10. 12. cho vecto .
Lưu ý : các giá trị được viết giữa các dấu (/ /) ; hai dấu (/ hoặc /) phải được viết liền
nhau không có khoảng trống giữa ( và / . Các phần tử liệt kê cách nhau dấu phẩy .
Dòng lệnh gán như trên còn có thể được viết dưới dạng công thức có kèm theo biến vòng
lặp hoặc kết hợp liệt kê với công thức :
vecto = (/ (2.*i, i =1,6) /)  vecto = (/2.,4.,(2.*i , i = 3,5) ,12. /)
Nếu vecto = (/ (1.*i , i = 6,1,-1) /)  vecto có các phần tử 6. 5. 4. 3. 2. 1.
Việc gán các giá trị cho biến mảng vectơ có thể thực hiện ngay trong phần khai báo :
real, dimension (1:3) :: vecto_a = (/2.,4.,6. /)
 Đối với mảng nhiều chiều như ma trận ,chúng ta sử dụng hàm reshape như sau :
real, dimension (1:3,1:2) :: matran
matran = reshape ( (/1.,2.,3.,4.,5.,6./) , (/3,2/) )
matran = reshape ( (/ (1.*i, i=1,6 ) /) , (/3,2/) )
+ Đối số thứ nhất của hàm reshape là
danh sách giá trị các phần tử của ma trận , được viết
theo quy luật của chỉ số ( điền theo cột ).
Điền giá trị theo cột trước

+ Đối số thứ hai là dạng ( shape ) của ma trận : 3 hàng 2 cột.
 Chúng ta có thể thay đổi thứ tự việc đưa giá trị vào ma trận :
real, dimension (1:3,1:2) :: matran
matran = reshape ( (/1.,2.,3.,4.,5.,6./) , (/3,2/) , order = (/2,1/) )

Tham số order = (/2,1/) chỉ định chỉ số thứ 2 ( cột ) sẽ thay đổi trước , thứ tự điền như sau
: matran (1,1) , matran (1,2) , matran (2,1). matran (2,2) , matran (3,1), matran (3,2)

Với order = (/2,1 /)


Điền giá trị theo từng hàng trước 

 Trường hợp dãy danh sách không đầy đủ để điền vào ma trận , tham số pad = (/…/) sẽ
cho biết quy luật điền các giá trị còn thiếu .
real, dimension (1:3,1:2) :: matran
matran = reshape ( (/1.,2.,3./) , (/3,2/) , order = (/2,1/) , pad = (/0./) )

Các phần tử còn thiếu sẽ được  1. 2. 


điền số 0.  
matran   3. 0. 
 0. 0. 
 
81
real, dimension (1:3,1:2) :: matran
matran = reshape ( (/1.,2.,3./) , (/3,2/) , order = (/2,1/) , pad = (/0.,1./) )

Các phần tử còn thiếu sẽ được  1. 2. 


 
điền số 0. , số 1. , liên tiếp . matran   3. 0. 
 1. 0. 
 

6.7 Mảng và phép gán .


 Chúng ta khai báo hai ma trận có cùng dạng (3,2) , các phép gán sau là hợp lệ :
real, dimension (1:3,1:2) :: matran1 ,matran2
matran1 (1,2) = 4. ! Gán cho riêng một phần tử hàng 1, cột 2 của matran1
matran1 = 0. ! Gán tất cả các phần tử của matran1 đều bằng 0.
matran2 = matran1 ! Sao chép nội dung của matran1 sang matran2
! matran2 (i,j) = matran1 (i,j) với i = 1..3, j = 1..2
Trong phép gán ( copy ) nguyên cả biến ma trận , yêu cầu hai ma trận phải cùng dạng với
nhau . Trong trường hợp này, các cận của chỉ số không nhất thiết phải giống nhau .Ví dụ :
real, dimension (1:3,1:2) :: matran1
real, dimension (-1:1,0 :1) :: matran2 ! 3 hàng , 2 cột
matran1 (1,1) = matran2 (-1,0)
matran1 (2,1) = matran2 ( 0,0)
matran1 (3,1) = matran2 ( 1,0)
matran1 = matran2  matrarn1(1,2) = matran2 (-1,1)
matran1 (2,2) = matran2 ( 0,1)
matran1 (3,2) = matran2 ( 1,1)

 Các số thực , số nguyên có thể kết hợp với từng phần tử của mảng có dạng bất kỳ ( có
cùng kiểu dữ liệu với số hoặc tự động chuyển đổi kiểu theo quy luật của Fortran ) qua các phép
toán số học : +, - , *, / , ** . Các biến mảng được xem như một toán hạng trong biểu thức, các giá
trị số cũng xem như một mảng cùng dạng và phép tính được thực hiện trên tất cả các phần tử của
mảng.
program arraytest
implicit none
real , dimension (1:3) :: vecto = (/1.,2.,3./)
real , dimension (1:3,1:2) :: matran,matran1 3. 4. 5.
matran = reshape((/1.,2.,3.,4.,5.,6./), (/3,2/)) 2. 4. 6.
print*,vecto + 2. 0.5 1. 1.5
print*,vecto * 2. ! giống như 2*vecto 1. 4. 9.
print*,vecto / 2.
print*,vecto ** 2 3. 4. 5. 6. 7. 8.
print* 2. 4. 6. 8. 10. 12.
print*,matran + 2. 0.5 1. 1.5 2. 2.5 3.
print*,matran * 2. 1. 4. 9. 16. 25. 36.
print*,matran / 2.  36.
matran1 = matran **2
print*,matran1
print*,matran1 (3,2)
read*
end

82
 Các biến mảng ( vectơ, matrận ) cùng dạng có thể xử lý như một toán hạng , tính toán
bình thường qua các phép toán số học . Việc tính toán được thực hiện trên tất cả các phần tử tương
ứng ( cùng vị trí ) của mảng . Kết quả trả về là một mảng có cùng dạng .
real,dimension (1:3) :: vecto1,vecto2, vecto
real , dimension (1:3,1:2) :: matran
matran = reshape((/1.,2.,3.,4.,5.,6./), (/3,2/))
vecto1 = (/1.,2.,3./)
vecto2 = (/2.,4.,6./)
vecto = vecto1 + vecto2 ! cộng 2 véctơ
3. 6. 9.
print*,vecto 8. 16. 24.
vecto = 2.* vecto1 + 3.* vecto2 ! tổ hợp tuyến tính -1. -2. -3.
print*,vecto 2. 8. 18.
vecto = vecto1 - vecto2 ! trừ 2 véctơ 2. 2. 2.
print*,vecto 1. 16. 729.
vecto = vecto1 * vecto2 ! nhân 2 véctơ 1. 4. 9. 16. 25. 36.
print*,vecto 1. 1. 1. 1. 1. 1.
print* , vecto2 / vecto1 ! chia 2 véctơ
print*, vecto1**vecto2 ! lũy thừa
print*, matran * matran ! nhân từng phần tử tương ứng của ma trận .
! không phải là phép nhân ma trận trong đại số .
print*, matran / matran ! chia từng phần tử tương ứng của ma trận .
read*
end
6.8 Mảng và cách sử dụng chỉ số để truy cập các bộ phận của mảng .
Để truy cập một bộ phận ( phần tử, hàng, cột, mảng con …) của mảng , chúng ta sử dụng
tên mảng kèm theo các chỉ số theo dạng :
< chỉ số đầu > : <chỉ số cuối > : < số gia > cho mỗi chiều của mảng .
Ví dụ : real , dimension (1:3) : vecto
print *, vecto (1:3: 2) ! sẽ xuất ra các phần tử : vecto (1) , vecto (3)
+ Trường hợp chỉ số đầu không ghi : lấy giá trị cận dưới.
+ Trường hợp chỉ số cuối không ghi : lấy giá trị cận trên .
+ Trường hợp số gia không ghi : lấy giá trị = 1
+ Số gia có thể là số âm . 1. 3.
real , dimension (1:3) :: vecto = (/1.,2.,3./) 4. 5. 6.
real , dimension (1:3,1:3) :: matran 3. 6. 9.
matran = reshape((/1.,2.,3.,4.,5.,6.,7.,8.,9./), (/3,3/)) 1. 3. 4. 6. 7. 9.
print*, vecto (1:3:2) ! phan tu 1 va 3 3. 1. 6. 4. 9. 7.
print*,matran (:,2) ! cot thu 2
1. 2. 4. 5.
print*,matran (3,:) ! hang thu 3
print*,matran (1:3:2,1:3) ! hang 1,3
print*,matran (3:1:-2,1:3) ! hang 3,1 Kết quả .
print*,matran (:2,:2) ! ma tran 2 x 2 goc tren , ben trai
Ghi chú : các đối tượng như matran (:,1)  vectơ cột 1 ; matran (2,:)  vectơ hàng 2 trở thành
các mảng một chiều và được xử lý như vectơ trong các biểu thức tính toán.
 Chúng ta có thể khai báo một mảng các số nguyên làm danh sách các chỉ số .
program array
implicit none
integer :: i
integer,dimension (1:3) :: chiso = (/1,3,5/)
integer,dimension (1:5) :: chuyenvi = (/2,3,4,5,1/)
83
real,dimension (1:5,1:5) :: matran ! Kết quả
matran = reshape ( (/ (i*1.,i=1,25) /), (/5,5/) )
print*, matran (chiso,2) ! xem hinh 6.2 6. 8. 10.
print*, matran (chiso,chiso) ! xem hình 6.3 1. 3. 5. 11. 13. 15. 21. 23. 25.
print*
print*, matran (chuyenvi,2) ! xem hình 6.4 7. 8. 9. 10. 6.
read* ; end

 1. 6. 11. 16. 21.   1. 6. 11. 16. 21.   1. 6. 11. 16. 21. 


     
 2. 7. 12. 17. 22.   2. 7. 12. 17. 22.   2. 7. 12. 17. 22. 
 3. 8. 13. 18. 23.   3. 8. 13. 18. 23.   3. 8. 13. 18. 23. 
     
 4. 9. 14. 19. 24.   4. 9. 14. 19. 24.   4. 9. 14. 19. 24. 
 5. 10. 25.   5. 10. 25.   5. 10. 25. 
 15. 20.  15. 20.  15. 20.
6.
Hình 6.2 Hình 6.3 Hình 6.4

6.9 Biểu thức có chứa biến mảng .Phép gán .


real ,dimension (1:3) :: vecto
real, dimension (1:3,1:3) :: matran, matran1 ! shape (3,3)
real, dimension (-1:1, 0 : 2) :: matran2 ! shape (3,3)

Các biểu thức và phép gán như sau là hợp lệ , lưu ý các mảng phải có dạng giống nhau
trong các biểu thức hoặc phép gán .
 matran = matran1 * matran 2 ! nhân từng cặp phần tử có vị trí giống nhau
! kết quả là một ma trận có cùng dạng .
! không phải là phép nhân ma trận trong đại số.
Kết quả : matran (1,1) = matran1(1,1) * matran2(-1,0)
matran (2,1) = matran1(2,1) * matran2(0,0) …
 matran = sin (matran1) ! lấy sin trên tất cả các phần tử của matran1.
! xem biến matran1 là đối số của hàm sin ( ).
Kết quả : matran (1,1) = sin (matran1(1,1) )
matran (2,1) = sin (matran1(2,1) ) …
Tương tự chúng ta có thể viết : cos (matran) ; exp (matran )  Các đối số của hàm còn
có thể là các mảng .

 matran1 ( : ,1) = sqrt (vecto (1:3)) ! tính toán như các vectơ cùng dạng .
matran1 ( : ,1) = matran2 (0, :) + vecto ( : ) ! các toán hạng có cùng dạng .

 a a12    exp(a11 ) exp(a12 ) 


exp   11    
  a21 a22    exp(a21 ) exp(a22 ) 

Hình 6.5 : Ví dụ mảng được sử dụng làm đối số của hàm exp ( ) .

84
6.10 Lệnh Where .
 Trong phép gán cho biến mảng , chúng ta có thể sử dụng lệnh where đi kèm với một
điều kiện logic, chỉ phần tử nào của mảng thỏa điều kiện , phép gán mới được thực hiện :
real, dimension (1:2,1:2) :: matran
matran = reshape ((/100.,0.01 , 10. , 0./), (/2,2/) )
where (matran > 0.) matran = log10 (matran) ! phép gán áp dụng
! cho các phần tử > 0.
print*, matran

 100. 10.   2. 1 
    
 0.01 0.   2. 0. 

 Cấu trúc where dạng đầy đủ :


where (matran > 0.)
 100. 10.   2. 1 
  
matran = log10 (matran )
 
else where  0.01 0.   2. 10. 
matran = -10.
end where
print*, matran
Nhận xét : cấu trúc này có dạng tương tự như cấu trúc If then else end if .
 Sử dụng một biến mảng kiểu logical có cùng dạng để test điều kiện thực hiện phép gán .
real, dimension (1:2,1:2) :: matran
logical , dimension (1:2,1:2) :: test ! ma trận (2,2) có kiểu logical
matran = reshape ((/100.,0.01 , 10. , 0./), (/2,2/) )
test = reshape ((/.true.,.true. , .true., .false./), (/2,2/) ) ! mask

 2. 1
where (test ) matran = log10 (matran ) ! kết quả  
 2. 0. 
Biến mảng logical test được dùng như một mặt nạ ( mask ) , đặt chồng lên trên biến mảng
matran có cùng dạng . Chỉ phần tử nào ở vị trí .true. mới thực hiện việc tính log10 .

6.11 Một số hàm chuẩn ( intrinsic functions ) có đối số là mảng .
 Hàm lấy tổng và tích các phần tử của một ma trận , một vectơ.

 1. 4. 7.  print*, sum (matran )  45. ! tổng các phần tử của ma trận .
  print*, sum (matran, dim = 1)  6. 15. 24. ! tổng theo từng cột .
matran   2. 5. 8. 
 3. 6. 9.  print*, sum (matran, dim = 2) 12. 15. 18. ! tổng theo từng hàng.
 
print*, sum (matran (:,2))  15. ! tổng các phần tử cột thứ 2
print*, sum (matran (3,:))  18. ! tổng các phần tử hàng thứ 3
print*, sum ( (/ (matran(i,i), i=1,3) /) )  15. ! tổng các phần tử trên đường chéo chính

print*, product (matran)  362 880. ! tích các phần tử của ma trận .
print*, product (matran, dim = 1)  6. 120. 504. ! tích theo từng cột.
print*, product (matran, dim = 2)  28. 80. 162. ! tích theo từng hàng .
print*, product ( (/ (matran(i,i), i=1,3) /) )  45. ! tích các phần tử trên đường chéo chính
85
Phép tính véctơ , ma trận .
 Tích vô hướng của 2 vectơ . Ví dụ : vecto = (/1.,2.,3./)
print*, dot_product (vecto,vecto) ! (1.)(1.) + (2.)(2.) + (3.)(3.) = 14.
n
 Tích hai ma trận A.B = C  cij   aik bkj
k 1

Điều kiện nhân được : số cột của ma trận A phải bằng số hàng của ma trận B .
 1. 4. 
 1. 3. 5.   
A  B   2. 5.  print *, matmul (A,B)  22. 28. 49. 64.
 2. 4. 6.   3. 6. 
 
 22. 49. 
Tích hai ma trận A.B =  
 28. 64. 
 Hàm trả về ma trận chuyển vị ( đổi cột thành hàng, hàng thành cột ) .
(Transpose of matrix ) .

 1. 4. 7.   1. 2. 3.
A =  2. 5. 8.   A =  4. 5. 6.
T

 3. 6. 9.   7. 8. 9.
   
print*, transpose (matran)  1. 4. 7. 2. 5. 8. 3. 6. 9.

 Hàm trả về phần tử có giá trị bé nhất , lớn nhất .
Giá trị bé nhất :
print*,minval (matran)  1.
print*,minval (matran,dim =1)  1. 4. 7. ! min theo từng cột
print*,minval (matran,dim =2)  1. 2. 3. ! min theo từng hàng
Giá trị lớn nhất :
print*,maxval (matran)  9.
print*,maxval (matran,dim =1)  3. 6. 9. ! max theo từng cột
print*,maxval (matran,dim =2)  7. 8. 9. ! max theo từng hàng
+ Trả về giá trị bé nhất có kèm theo điều kiện : Ví dụ :
print*, minval (matran, matran >= 5.)  5.
+ Trả về vị trí ( location ) của phần tử có giá trị bé nhất, lớn nhất :
print *, minloc (matran )  1 1 ! phần tử bé nhất có vị trí (1,1)
print *, maxloc (matran )  3 3 ! phần tử lớn nhất có vị trí (3,3)
print *, maxloc (matran , dim = 1)  3 3 3 ! phần tử lớn nhất theo từng cột
! trả về vị trí hàng có phần tử lớn nhất
 Các hàm truy vấn có kiểu logical .
all ( điều kiện ): xem xét tất cả các phần tử theo điều kiện ; any (điều kiện ) : xem xét ít
nhất có một phần tử đạt điều kiện .
if (all (matran >= 0.)) print* ,” Tat ca phan tu deu la so duong.”
if (any (matran >= 0.)) print* ,” Co it nhat mot phan tu la so duong.”
print*, all (matran >= 5.,dim = 1)  F F T ! chỉ có cột 3 thỏa .
print*, all (matran >= 5.,dim = 2)  F F F ! không có hàng nào thỏa.
print*, any (matran >= 5.,dim = 1)  F T T ! cột thứ 2,3 thỏa
print*, any (matran >= 5.,dim = 2)  T T T ! cả 3 hàng đều thỏa

86
 Hàm count ( điều kiện ) , đếm số phần tử của ma trận thỏa điều kiện .
print*, count (matran >= 0.)  9 ! đếm số phần tử của ma trận theo điều kiện  0.
print*, count ( matran >2. .and. matran <6. )  3
 Hàm pack trả về một danh sách các phần tử theo một điều kiện .
 1. 4. 7. 
  print*, pack (matran, matran >= 6.)  6. 7. 8. 9.
matran   2. 5. 8. 
 3. 6. 9. 
 

 Chúng ta xem chương trình chuyển đổi dạng ( shape ) từ ma trận sang vectơ và ngược
lại . Lưu ý : số phần tử của vectơ và ma trận phải bằng nhau .
program changeshape
implicit none
real , dimension (1:4) :: vecto ! vecto co 4 phan tu .
real , dimension (1:2,1:2) :: matran, matran2 ! matran 2 x 2 co 4 phan tu .
matran = reshape ( (/1.,2.,3.,4./), (/2,2/))
vecto = reshape (matran , (/4/))  1. 
print*,vecto ,' ',vecto(3:4)  1. 2. 3. 4. 3. 4.  
 1. 3.   2. 
matran2 = reshape (vecto, (/2,2/))
    3. 
print*,matran2 , ' ', matran2 (:,1)  1. 2. 3. 4. 1. 2.  2. 4. 
read*  
end  4. 

 Hàm trả về các chỉ số cận dưới , cận trên của mảng .
lbound (lower bound ) : cận dưới ; ubound ( upper bound ) : cận trên .
dimension ( lb1:ub1 , lb2:ub2) ! sắp xếp 4 chỉ số cận dưới, cận trên .
program main
implicit none
real , dimension (-1:1,0:2) :: matran ! matran 3 x 3
matran = reshape ( (/1.,2.,3.,4.,5.,6.,7.,8.,9./), (/3,3/))
print*, lbound(matran)  -1 0
 (1, 0) (1,1) ( 1, 2) 
print*, ubound(matran)  1 2  
print*,lbound(matran,dim = 1)  theo chỉ số thứ 1 -1  (0, 0) (0,1) (0, 2) 
 (1, 0) (1, 2) 
print*,ubound(matran, dim =1)  theo chỉ số thứ 1 1  (1,1)
print*,lbound(matran,dim =2)  theo chỉ số thứ 2 0 Vị trí các chỉ số .
print*,ubound(matran, dim =2)  theo chỉ số thứ 2 2
print*,size(matran)  số phần tử của ma trận : 9
print*,size(matran,dim = 1)  số phần tử theo chỉ số thứ 1 : 3
print*,size(matran,dim = 2)  số phần tử theo chỉ số thứ 2 : 3
print*,shape(matran)  dạng 3 3
read*
end

 Viết / đọc dữ liệu kiểu mảng một chiều theo từng dòng có định dạng .
Chương trình viết /đọc một mảng 100 số thực theo từng dòng ,sử dụng chỉ thị format , mỗi
dòng có 10 phần tử .
program test100
implicit none
real,dimension (100) :: x , y
integer :: i
87
x = (/ (1.*i ,i = 1,100) /) ! Tạo giá trị mảng x từ 1. đến 100.
open (1,file = 'd:/data05.txt')
write (1,20) x ! ghi vào file theo định dạng được quy định bởi câu lệnh format có nhãn 20
20 format (10f5.0) ! viết 10 phần tử trên một dòng với định dạng f5.0
close (1)
open (1,file = 'd:/data05.txt')
read (1,20) y ! đọc giá trị từ file và gán cho mảng y hoặc dùng lệnh read (1,*) y
close (1)
write(*,20) y ! viết mảng y ra màn hình theo format (10f5.0) để kiểm tra.
read*
end

Hình 6.6 : Kết quả xuất dữ liệu ra màn hình theo từng dòng 10 phần tử .

6.12 Thực hành .


6.12.1 Chương trình chuyển đổi dãy nhiệt độ Fahrenheit thành dãy nhiệt độ bách phân
Celsius , sử dụng biểu thức mảng c = (5./9.)*(f – 32.)
program fatocel
implicit none
real,dimension (1 : 25) :: f , c ! Khai bao hai bien mang mot chieu
integer :: i
!Day nhiet do Fahrenheit Do F Do C
f = (/ (5.*i,i=1,25) /) ! Day nhiet do F tu 5 den 125 , so gia la 5 *********
!Doi qua do Celsius 5.0 -15.0
10.0 -12.2
c = (5./9.)*(f-32.)
15.0 -9.4
! Xuat ket qua thanh hai cot
20.0 -6.7
print '(1X,a,3X,a)','Do F', 'Do C'
25.0 -3.9
print*,repeat('*',9) 30.0 -1.1
do i = 1,25 35.0 1.7
print '(f5.1,2X,f5.1)', f(i), c(i) 40.0 4.4
end do 45.0 7.2
read* 50.0 10.0
end program fatocel 55.0 12.8
Một phần kết quả  60.0 15.6

+ Dựa vào chương trình trên , bạn đọc tự viết chương trình chuyển đổi dãy nhiệt độ Celsius
, từ 1 đến 1000 C, thành dãy nhiệt độ Fahrenheit tương ứng .

6.12.2 Chương trình sắp xếp một dãy số thực thành một dãy số có giá trị từ lớn đến nhỏ. Ví
dụ sắp xếp điểm trung bình của n học sinh .

88
program sort
implicit none
integer ::i,j
integer , parameter :: n = 10
real , dimension (n) :: a 7.8 2.5 8.9 3.6 5.7 6.9 7.3 3.5 4.9 9.2
real :: tgian 9.2 8.9 7.8 7.3 6.9 5.7 4.9 3.6 3.5 2.5
a = (/ 7.8,2.5,8.9,3.6,5.7,6.9,7.3,3.5,4.9,9.2 /)
print*,a ! Xuat day a chua sap xep .
do i = 1,(n-1)
do j = (i+1),n
if (a(i) < a(j)) then
tgian = a(i) ; a(i) = a(j); a(j) = tgian ! Doi cho hai phan tu .
end if
end do
end do
print*,a ! Xuat day a da sap xep tu lon den nho.
read*
end program sort

+ Dựa vào chương trình trên , bạn đọc viết một chương trình sắp xếp điểm từ thấp đến cao
, đồng thời xuất ra điểm số cao nhất, thấp nhất ( sử dụng các hàm maxval , minval ) , số
học sinh đạt điểm trung bình (  5. và ≤ 7.) , khá ( >7. và <9. ), số học sinh giỏi (  9.) ( sử
dụng hàm count có kèm điều kiện ) .

6.12.3 Một công ty sản xuất xe gắn máy tại 03 nhà máy , số xe sản xuất của từng nhà máy
theo từng quý được cho trong bảng sau . Đơn giá mỗi xe là 20 triệu đồng.
Đơn vị : xe
Quý I Quý II Quý III Quý IV
Nhà máy 1 550 1270 1550 2000
Nhà máy 2 380 850 1020 1200
Nhà máy 3 650 1100 1650 1900

Lập trình tính :


1.Tổng số xe sản xuất trong năm .
2. Số xe sản xuất được trong từng quý .
3. Số xe sản xuất trong năm của từng nhà máy .
4. Giá trị sản xuất trong năm của công ty .

program sanxuat
implicit none
integer ,dimension ( 3,4) :: sp
integer , parameter :: gia = 20 ! don gia mot xe , don vi trieu dong
sp = reshape ( (/550,380,650,1270,850,1100,1550,1020,1650 &
,2000,1200,1900 /), (/3,4 /))
print*,'Tong so xe sx trong nam : ',sum(sp)
print*,'So xe sx theo tung quy : ',sum (sp,dim = 1)
print*,'So xe sx trong nam cua tung nha may : ',sum (sp,dim = 2)
print*,'Gia tri san xuat trong nam ( trieu dong ) : ',sum (20*sp)
read*
end

89
Kết quả :
Tong so xe sx trong nam : 14120
So xe sx theo tung quy : 1580 3220 4220 5100
So xe sx trong nam cua tung nha may : 5370 3450 5300
Gia tri san xuat trong nam ( trieu dong ) : 282400

6.12.4 Giải hệ n phương trình tuyến tính n ẩn bằng phương pháp lặp đơn ( điều kiện
hội tụ được đáp ứng ) .

Cho hệ phương trình tuyến tính dạng ma trận : Ax = b


Chúng ta biến đổi thành dạng : x = Cx + d , chọn vectơ đầu x0 = d và tính dãy vectơ x theo
công thức lặp : xi+1 = Cxi + d ( i =0,1,..n) . Kết quả : Lim xi = x khi i  

Ví dụ : giải hệ thống 3 phương trình 3 ẩn sau đây :


5x1 + x2 + x3 = 6 x1 = 0x1 - 0.2x2 - 0.2 x3 + 6/5
x1 + 6x2 - x3 = -14  x2 = -1/6 x1 + 0x2 +1/6 x3 - 7/3
x1 - x2 + 5x3 = 18 x3 = - 0.2 x1 + 0.2x2 + 0 x3 + 18/5

 0 0.2 0.2   6/5 


   
C   1/ 6 0 1/ 6  d =  7 / 3  Công thức lặp : x = Cx + d
 0.2 0.2 0   18 / 5 
  
3
Điều kiện hội tụ được đáp ứng : c
k 1
ik < 1 ( i  1..3) . Văn bản chương trình :

! Giai he pttt bang pp lap don


! 5x1 + x2 + x3 = 6
! x1 + 6x2 -x3 = -14
! x1 - x2 + 5x3 = 18
program giaihpt
implicit none
integer :: k
real,dimension (3,3) :: c
real,dimension (3) :: x,d
c = reshape((/0.,-1./6.,-0.2,-0.2,0.,0.2,-0.2,1./6.,0. /),(/3,3/))
d = (/ 6./5. ,-7./3.,18./5. /)
x=d ! gán giá trị đầu x0
do k =1,6 ! thực hiện 6 vòng lặp
x = matmul(c,x) + d ! biểu thức lặp , sử dụng hàm nhân ma trận .
print '(a,i2,3f8.3)','Buoc lap',k ,x
end do
print*
print*,'Ket qua : '
print '(a,f8.3)',' x1 = ',x(1)
print '(a,f8.3)',' x2 = ',x(2)
print '(a,f8.3)',' x3 = ',x(3)
!Kiem tra
print*
print*,'Kiem tra gia tri ve trai : '
print '(a,f8.3)',' Phuong trinh 1 : ',5*x(1)+x(2)+x(3)
print '(a,f8.3)',' Phuong trinh 2 : ',x(1)+6*x(2)-x(3)
print '(a,f8.3)',' Phuong trinh 3 : ',x(1)- x(2)+5*x(3)
90
read*
end

Kết quả :
Buoc lap 1 0.947 -1.933 2.893
Buoc lap 2 1.008 -2.009 3.024
Buoc lap 3 0.997 -1.997 2.997
Buoc lap 4 1.000 -2.000 3.001
Buoc lap 5 1.000 -2.000 3.000
Buoc lap 6 1.000 -2.000 3.000

Ket qua :
x1 = 1.000
x2 = -2.000
x3 = 3.000

Kiem tra gia tri ve trai :


Phuong trinh 1 : 6.000
Phuong trinh 2 : -14.000
Phuong trinh 3 : 18.000

+ Bạn đọc tự giải hệ phương trình sau Ax = b bằng phương pháp lặp đơn.

 1.02 0.25 0.30   0.515   2.0 


     
A   0.41 1.13 0.15  b   1.555  , so sánh với nghiệm chính xác : x   2.5 
 0.25 0.14 1.21   2.780   3.0 
     

6.12.5 Giải hệ n phương trình n ẩn theo phương pháp Gauss .


Chúng ta giải hệ 3 phương trình tuyến tính sau bằng phương pháp Gauss .
2x1 + 3 x2 + 5x3 = 2
3x1 – 2.5 x2 + 4x3 = 10
-4x1 + 3 x2 + 2x3 = 2

 2. 3. 5. 2. 

Đặt A   3. 2.5 4. 10.  là ma trận mở rộng , chúng ta sẽ biến đổi thành dạng tam giác
 4. 3. 2. 2. 
 
 1. a12 a13 a14 
* * *

 * 
 . Kết quả :
* *
: A =  0. 1. a23 a24
 0. 0. 1. a34* 
 
x3 = a*34 ; x2 = a*24 – a*23x3 ; x1 = a*14 – a*13x3 –a*12x2

Bạn đọc tự phân tích và chạy chương trình sau :
program gauss
implicit none
real,dimension (3,4) :: a
real,dimension (3) :: x

91
integer :: i,j
a = reshape( (/2.,3.,-4.,3.,-2.5,3.,5.,4.,2.,2.,10.,2. /) , &
(/3,4/))
print*,'Ma tran mo rong :'
do i =1,3
print '(4f6.2)',a(i,:) ! kiem tra he so ma tran A theo hang
end do
do i =1,3
a(i,:) = a(i,:)/a(i,i)
do j = i+1,3
a(j,:) = a(j,:) - a(i,:)*a(j,i)
end do
end do
print*,'Ma tran tam giac :'
do i =1,3
print '(4f6.2)',a(i,:) ! kiem tra he so ma tran tam giac
end do
x(3) = a(3,4)
x(2) = a(2,4) - a(2,3)*x(3)
x(1) = a(1,4) - a(1,3)*x(3) - a(1,2)*x(2)
print*
print*,'Cac nghiem so x1,x2,x3 : '
print '(3f6.2)',x
read*
end program gauss

Hình 6.7 : Kết quả giải hệ pttt bằng pp Gauss

+ Bạn đọc tự giải hệ phương trình sau :


2.1x1 – 3.4 x2 + 1.8x3 = 17.81
-1.9x1 + 5.2 x2 -3.6x3 = -28.49
4.3x1 -2.1 x2 -1.5x3 = 5.58
Đáp án :
Ma tran mo rong :
2.10 -3.40 1.80 17.81
-1.90 5.20 -3.60 -28.49
4.30 -2.10 -1.50 5.58
Ma tran tam giac :
1.00 -1.62 0.86 8.48
0.00 1.00 -0.93 -5.83
-0.00 -0.00 1.00 3.80

Cac nghiem so x1,x2,x3 :


1.50 -2.30 3.80

92
+ Ghi chú : Trường hợp tổng quát để giải hệ n phương trình tuyến tính bằng phương pháp
khử Gauss , bạn đọc tham khảo văn bản chương trình ở phụ lục B .
6.12.6 Phân tích số liệu thống kê : số trung bình , độ lệch tiêu chuẩn :
Cho vectơ x có n phần tử , sử dụng các công thức sau để tính :
n

1 n  ( x(i)  x) 2

Số trung bình : x 


n i 1
x(i ) . Độ lệch tiêu chuẩn :   i 1
n 1
Biểu thức mảng trong Fortran :
tb = sum (x)/ size(x) do_lech = sqrt ( sum ((x-tb)**2) / (size(x) -1. ) )

Bạn đọc phân tích và chạy test chương trình sau :

program ThongKe
implicit none
real,dimension (:),allocatable :: x
integer :: n
real :: tb , do_lech
print*,"Cho biet so phan tu : " ; read*,n
allocate (x(n))
print*, " Vao",n, "gia tri : " ; read *, x
tb = sum(x)/size(x) ! so trung binh
do_lech = sqrt ( sum((x-tb)**2)/(size(x)-1.) ) ! do lech tieu chuan
print '(a,f8.3)'," So trung binh : ",tb
print '(a,f8.3)'," Do lech tieu chuan : " , do_lech
deallocate (x)
read*
end
Nhận xét : trong chương trình trên , vectơ x được cấp phát động , số phần tử khai báo lúc
chạy chương trình . Các biểu thức tính số trung bình , độ lệch tiêu chuẩn là các biểu thức
mảng . Chú ý việc sử dụng các hàm sum , size .
Kết quả chạy test :

Hình 6.8 : Kết quả tính số trung bình , độ lệch tiêu chuẩn .

Trong trường hợp dãy số x có các số bằng 0. ( hoặc số vượt quá phạm vi xem xét nào đó )
và chúng ta muốn loại bỏ chúng , không muốn đưa vào dãy số liệu để tính toán . Lúc này chúng
ta dùng hàm sum , count có kèm điều kiện như ví dụ sau ( loại bỏ các số 0. ) :
program ThongKe2
implicit none
real,dimension (:),allocatable :: x
integer :: n , i
real :: tb , do_lech ,A,B

93
print*,"Cho biet so phan tu : " ; read*,n
allocate (x(n)) Cho biet so phan tu :
print*, " Vao",n, "gia tri : " ; read *, x 7
tb = sum(x, x/=0.)/count(x /= 0.) Vao 7 gia tri :
A = sum((x - tb)**2,x /= 0.) 1.1 0.0 1.3 1.4 0.0 1.6 1.7
B = count(x /= 0.) -1. So trung binh : 1.420
do_lech = sqrt( A/B) Do lech tieu chuan : 0.239
print '(a,f8.3)'," So trung binh : ",tb So phan tu khac 0.: 5
print '(a,f8.3)'," Do lech tieu chuan : " , do_lech Vi tri phan tu so voi so 0.: T F T T F T T
print*," So phan tu khac 0.: ",count(x /= 0.)
print* , " Vi tri phan tu so voi so 0.: ", ( x /= 0.)
deallocate (x)
read*
end
6.12.7 Bạn đọc lập trình tính và in ra các hệ số của tam giác Pascal như sau ( n = 10
dòng đầu tiên ) :

Hình 6.9 : Các hệ số của tam giác Pascal ( 10 dòng đầu ) .

Đáp án đề nghị :
program Pascal
implicit none
integer :: i,j
integer,parameter :: n = 10
integer, dimension (1:n,1:n) :: p
p(1,1) = 1 ! Hàng 1
p(2,1) = 1 ; p(2,2) = 1 ! Hàng 2
do i = 3,n ! Hàng 3 đến hàng n
p(i,1) = 1; p(i,i) = 1 ! Hệ số đầu , cuối mỗi hàng bằng 1
do j = 2 , i-1
p(i,j) = p(i-1,j) + p(i-1,j-1) ! Công thức tổng quát cho hệ số nằm bên trong
end do
end do
do i = 1,n ! Xuất ra màn hình
do j = 1,i
print ‘(i4,$)’,p(i,j)
end do
print*
end do
read *
end
*****

94
CHƯƠNG 7 : CHƯƠNG TRÌNH CON ( SUBPROGRAM )
7.1 Khái niệm về chương trình con.
Trong lập trình , để tránh việc viết những đoạn chương trình thực hiện cùng một công việc
nào đó nhiều lần , chúng ta sẽ chuyển đoạn chương trình này thành một chương trình con (
subprogram ) và từ chương trình chính ( main program ) , khi cần, sẽ gọi chương trình con này ra
để thực hiện .
Đối với một chương trình lớn , cần giải quyết một vấn đề phức tạp , người lập trình thường
phải nghiên cứu , phân chia vấn đề thành các vấn đề nhỏ hơn để dễ giải quyết , dễ kiểm soát theo
một sơ đồ phân cấp từ trên xuống dưới với mức độ khó giảm dần . Khi viết chương trình sẽ bắt đầu
thực hiện theo trình tự ngược lại từ dưới lên trên , giải quyết lần lượt vấn đề dễ trước khó sau .
Mỗi vấn đề nhỏ sẽ được giải quyết bằng một chương trình con . Lúc này , trong chương trình
chính chỉ thể hiện cấu trúc khối tổng quát của chương trình, còn toàn bộ chi tiết đều được bố trí
trong các chương trình con .
Chúng ta còn có thể xây dựng các chương trình con mẫu ; người sử dụng chỉ biết truyền dữ
liệu đầu vào và lấy kết quả ở đầu ra mà không quan tâm đến chi tiết thuật giải bên trong chương
trình . Các tổ chức phát triển ngôn ngữ Fortran đã xây dựng nhiều thư viện chứa hàng trăm chương
trình mẫu thông dụng ở nhiều lĩnh vực tính toán khác nhau , tạo sự thuận lợi cho cộng đồng lập
trình Fortran.

7.2 Cấu trúc chương trình Fortran.


7.2.1 Cấu trúc tổng quát một chương trình Fortran .
Chương trình Fortran thường có bốn đơn vị chương trình như sau :
 Program : chương trình chính .
 Subroutine : chương trình con dạng thủ tục .
 Function : chương trình con dạng hàm .
 Module : đơn vị chương trình độc lập , chứa các thông tin , chương trình tiện
ích sử dụng chung .
Trong Fortran , chương trình chính , chương trình con kiểu subroutine , chương trình con
kiểu function thường được gọi chung là procedure . (Tuy nhiên cũng có tài liệu xem procedure
chỉ gồm hai loại chương trình con là subroutine và function .)
Chương trình con subroutine và function còn được chia thành dạng chương trình con bên
trong ( internal ) và chương trình con bên ngoài ( external ) .
Chương trình con internal được định nghĩa bên trong một chương trình chính hoặc một
chương trình chủ , được biên dịch cùng lúc với chương trình chính ; còn chương trình con external
là đơn vị chương trình đứng riêng , được biên dịch độc lập .

Chương trình con kiểu hàm ( function ) trả về một giá trị thông qua tên hàm , tên hàm cùng
các đối số ( arguments ) phù hợp được sử dụng trong các biểu thức tính toán tương tự như cách sử
dụng các hàm chuẩn của Fortran ( intrinsic functions ) .

Chương trình con kiểu thủ tục ( subroutine ) không trả về một giá trị thông qua tên của
subroutine và việc thực hiện subroutine được gọi từ chương trình chính bằng lệnh call kèm theo
tên của subroutine cùng các đối ( arguments ) nếu có . Thông thường kết quả trả về của subroutine
được gửi vào các đối đầu ra của subroutine .

95
Một hay nhiều Kết quả *: Một giá
đối số . Function trị .

Một hay nhiều Kết quả : Một hay


đối đầu vào *. Subroutine nhiều giá trị ; hoặc
thực thi các chỉ thị .

Ghi chú : * Subroutine có thể không có đối đầu vào . Kết quả của Function có thể là một mảng.

Hình 7.1 : Hai loại chương trình con trong Fortran .

7.2.2 Vị trí của các chương trình con internal và external .


Chúng ta xem sơ đồ bố trí chương trình con sau đây :
program main ! chương trình chính .
implicit none
! Các chỉ thị khai báo của chương trình chính .
! Các chỉ thị thi hành của chương trình chính .
contains ! từ khóa . Bắt đầu khai báo các chương trình con internal.

! Văn bản các chương trình con internal được đặt ở đây .
! Các chương trình con internal được quyền truy cập các biến
! của chương trình chính ( trừ trường hợp có sự trùng tên biến ).

end program main ! kết thúc chương trình chính .

! Văn bản các chương trình con external được đặt ở đây .
! Các chương trình con external hoàn toàn độc lập với chương trình chính .

7.3 Chương trình con dạng subroutine ( kiểu thủ tục ) .


7.3.1 Ví dụ về internal subroutine .
Chúng ta xem chương trình con dạng internal subroutine qua ví dụ đơn giản sau :
program main
implicit none
integer :: i = 6 ! Biến của chương trình chính . Biến toàn cục ( global variable ).
call Test ! Gọi subroutine bằng lệnh call .
read*
stop
contains
subroutine Test
integer :: j = 8 ! Biến j của chương trình con . Biến địa phương (local variable ).
print*,i,j ! subroutine truy cập được giá trị biến i của chương trình chính .
end subroutine Test
end program main
Kết quả trên màn hình : 6 8 ! là giá trị của hai biến i , j .

96
Nhận xét : chương trình con subroutine Test thuộc loại internal , không có đối hình thức ,
truy cập được giá trị của biến toàn cục ( biến i ) của chương trình chính . Trái lại , biến địa phương
( biến cục bộ ) j của chương trình con chỉ có tác dụng trong phạm vi chương trình con này , nếu
chúng ta đặt câu lệnh print*, j ở chương trình chính thì chương trình sẽ báo lỗi .
Chương trình sau có biến toàn cục và biến cục bộ trùng tên :
program main
implicit none
integer :: i = 6
call Test2 ! Kết quả trên màn hình : 8 , là giá trị của biến i cục bộ
read*
stop
contains
subroutine Test2
integer :: i = 8 ! Biến cục bộ trùng tên với biến toàn cục .
print*, i ! Biến cục bộ được ưu tiên truy cập và in ra .
end subroutine Test2
end program main

Trong trường hợp biến toàn cục và biến cục bộ có tên trùng nhau như ví dụ trên , chương
trình con sẽ ưu tiên truy cập biến cục bộ , giá trị xuất ra màn hình lúc này sẽ là 8.
Trong hai ví dụ nêu trên , chương trình chính và chương trình con loại internal sẽ được biên
dịch đồng thời . Tất cả các chỉ thị , câu lệnh của chương trình con subroutine sẽ được thực hiện tại
vị trí gọi chương trình con trong chương trình chính ( in – lining ) .
7.3.2 Cú pháp của chương trình con subroutine .
+ Cú pháp của chương trình con subroutine :
subroutine <tên_subroutine> ( các đối hình thức )
! Các chỉ thị khai báo các đối hình thức ( dummy arguments )
! Các chỉ thị khai báo các biến, đối tượng cục bộ (local variables, local objects )
! sử dụng trong subroutine
! Các lệnh thực thi .
end subroutine <tên_subroutine>

Tên subroutine được đặt theo quy tắc đặt tên biến.
Các đối hình thức có thể là tên biến , tên mảng , tên chương trình con .Subroutine có thể
không có đối .
Tên các đối hình thức có thể khác với tên của các đối thực sự trong lệnh call , dùng để gọi
thực thi một subroutine .
Chỉ thị return trong chương trình con cho phép thoát ra khỏi subroutine tại bất cứ vị trí nào
của chương trình .
Subroutine còn có thể chứa các chương trình con bên trong khai báo qua từ khóa contains.

+ Các đối hình thức là biến, mảng trong subroutine được khai báo với các thuộc tính sau :
intent(in) : dành cho các biến mà giá trị đầu vào được cung cấp từ chương trình chính (
chương trình gọi subroutine ) . Các giá trị này được sử dụng trong subroutine nhưng không được
có lệnh làm thay đổi giá trị của các biến có thuộc tính này .
intent(out) : dành cho các biến mà giá trị đầu vào không được sử dụng trong subroutine ,
tuy nhiên giá trị của biến được thay đổi trong subroutine . Thông thường đây là biến chứa kết quả
tính toán của subroutine và gửi ra chương trình gọi .

97
intent(inout) : dành cho biến có thuộc tính cả hai loại nêu trên . Biến nhận giá trị đầu vào
từ chương trình gọi , bị biến đổi trong subroutine và chứa kết quả trả về ( khác với giá trị ban đầu
) khi ra khỏi subroutine.
Chúng ta xem chương trình ví dụ sau về khai báo thuộc tính cho các đối hình thức của
subroutine :

program main
implicit none
real :: a,b,c
a = 2. ; b = 1. ; c = 4.
print*,a,b,c !  2. 1. 4.
call Test3(a,b,c)
print*,a,b,c !  2. 6. 12.
read*
stop a  x : chỉ nhận giá trị đầu vào .
contains b  y : chứa giá trị kết quả đầu ra .
subroutine Test3(x,y,z) c  z : nhận giá trị đầu vào , chứa giá trị
real,intent(in) :: x kết quả đầu ra .
real,intent(out) :: y
real,intent(inout) :: z
y = 3.*x
z = x + y +z
end subroutine Test3
end program main

+ Cú pháp gọi chương trình con subroutine từ chương trình chính ( chương trình chủ ) như
sau :
call < Tên_subroutine > ( danh sách các đối thực sự )

Các đối thực sự ( actual arguments ) phải tương ứng 1-1 với các đối hình thức nhưng tên có
thể khác nhau . Trong trường hợp các đối là các biến , chúng phải có cùng kiểu dữ liệu và có cùng
số kind .

7.3.3 Ví dụ về external subroutine .


Chúng ta xem chương trình sau về cách bố trí external subroutine và cách khai báo interface
( giao diện ) cho external subroutine trong chương trình chính .
program main
implicit none
real :: a,b,c
! Khai bao interface cho subroutine
interface
subroutine Test3(x,y,z)
real,intent(in) :: x
real,intent(out) :: y ! khai báo khối interface
real,intent(inout) :: z
end subroutine Test3
end interface

! Ket thuc khai bao interface


a = 2. ; b = 1. ; c = 4.
call Test3(a,b,c)
98
print*,a,b,c ! Kết quả : 2. 6. 12.
read*
end program main

! Khai bao external subroutine

subroutine Test3(x,y,z)
implicit none
real,intent(in) :: x
real,intent(out) :: y Chương trình độc lập.
real,intent(inout) :: z Khai báo bên ngoài chương trình chính
y = 3.*x ( sau từ khóa end program ) .
z = x + y +z
end subroutine Test3

+ Văn bản chương trình con subroutine dạng bên ngoài (external ), được bố trí bên ngoài
chương trình chính ( dưới dòng end program main ) . External subroutine là đơn vị chương trình
độc lập .
+ Bên trong chương trình chính, chúng ta khai báo phần interface ( giao diện ) chứa tên
của external subroutine kèm các đối hình thức , khai báo kiểu dữ liệu cùng thuộc tính của các đối
hình thức này . Phần nội dung các lệnh thi hành trong subroutine không đưa vào interface .Trình
biên dịch cần thông tin của khối interface khi biên dịch chương trình chính , đảm bảo cho chương
trình chính và chương trình con dạng external có số lượng , kiểu và thuộc tính của các đối tương
thích với nhau .

7.3.4 So sánh internal subroutine và external subroutine .


Đặc điểm của internal subroutine :
_ Nằm bên trong một đơn vị chương trình là chương trình chính hoặc chương trình chủ .
Không cần khai báo khối interface ( giao diện ) .
_ Từ khóa contains phân cách chương trình chính và văn bản chương trình con dạng internal
_ Có khả năng truy cập các biến toàn cục ( trừ trường hợp trùng tên ) .Tuy nhiên đây cũng
có thể là điểm cần phải lưu ý khi chúng ta có thể vô tình làm thay đổi giá trị của biến toàn cục .
_ Chỉ được truy cập bởi chương trình chính chứa nó . Vì vậy , để các đơn vị chương trình
khác có thể sử dụng chương trình con này , cần phải chuyển ra dạng external subroutine , hoặc sử
dụng cấu trúc chương trình dạng module ( xem chương 8 ) .

7.3.5 Sử dụng lệnh call .


Lệnh call dùng để gọi subroutine từ chương trình chính có thể được sử dụng theo các cách
sau đây ( xem ví dụ 7.3.2 , 7.3.3 ) .
+ Các đối thực sự xuất hiện đúng theo vị trí các đối hình thức :
call Test3 (2.,b,c) ! 2.  x , b  y, c  z
+ Sử dụng tên các đối hình thức và gán giá trị của các đối thực sự cho các đối hình thức ,
lúc này có thể thay đổi vị trí của đối số.
call Test3 ( x = 2., y = b , z = c) ! có thể thay đổi thứ tự của đối số .
 call Test3 ( y = b , z = c, x = 2.)
+ Sử dụng dạng hỗn hợp :
call Test3 (2.,y = b, z = c ) ! kết hợp hai cách trên .

99
7.3.6 Thuộc tính save áp dụng cho các biến cục bộ
Đối với các biến cục bộ trong chương trình con subroutine được gán thuộc tính save , giá
trị của biến sẽ được lưu lại trong bộ nhớ sau khi subroutine được thực hiện xong . Ở các lần thực
hiện subroutine tiếp theo, giá trị biến cục bộ này sẽ lấy giá trị cập nhật trong bộ nhớ .
program main
implicit none
integer :: n
do n =1,5
call testsave
end do
read*
stop
contains i= 1
subroutine testsave i= 2
integer,save :: i = 0 Kết quả : i= 3
i = i+1 i= 4
print*,’i = ‘,i i= 5
end subroutine testsave
end program main

Trong chương trình trên , subroutine testsave được gọi 5 lần từ chương trình chính và giá
trị biến cục bộ i ( có gán thuộc tính save ) được in ra theo giá trị cập nhật được lưu trong bộ nhớ
sau mỗi lần subroutine được thực hiện . Một số trình biên dịch ( như Force 2.0 ) thực hiện tự động
việc lưu giá trị các biến cục bộ.
Nếu chúng ta khai báo lại biến cục bộ trong subroutine trên như sau :
integer :: i
i=0
i = i+1 ; print*,’i = ‘,i
Thì kết quả i = 1 sẽ xuất hiện năm lần trên màn hình .

7.4 Chương trình con kiểu function ( hàm ) .


Chương trình con function sẽ trả về một giá trị thông qua tên hàm cùng các đối số phù hợp
; tên hàm được sử dụng trong các biểu thức tính toán tương tự như cách sử dụng các hàm chuẩn
của Fortran . Chương trình con function cũng có dạng bên trong (internal ) được khai báo bên trong
chương trình chính hoặc chương trình chủ ; và có dạng bên ngoài ( external) khi được khai báo bên
ngoài , độc lập với chương trình chính. Trong trường hợp hàm dạng external , trong chương trình
chính chúng ta cần khai báo interface cho external function này.

7.4.1 Ví dụ về chương trình con internal function.


program main
implicit none
real :: a,b,c,p
a = 3.;b = 4.;c = 5.
p = Tong (a,b,c) * 2. ! sử dụng tên hàm trong biểu thức , kết quả : 24.
print*, p
read*
contains
! Khai bao internal funtion

100
function Tong (x,y,z)
real :: Tong ! khai báo kiểu dữ liệu trả về của hàm qua tên hàm .
real,intent (in) :: x,y,z ! khai báo các đối số hình thức
Tong = x+y+z
end function Tong
end program main

Trong chương trình function ở trên , chúng ta định nghĩa hàm Tong (x,y,z) có 3 đối số hình
thức là x,y,z. Các đối số này được gán thuộc tính intent(in) , nghĩa là chỉ tiếp nhận giá trị đầu vào
từ chương trình chính , không được phép thay đổi các giá trị này trong chương trình con. Câu lệnh
cuối cùng : Tong = x + y +z , cho biết kết quả của hàm , được trả về qua tên hàm là Tong. Lưu ý :
tên hàm Tong phải được khai báo kiểu dữ liệu , số kind như là một biến .
Các hàm bên trong (internal function) chỉ được sử dụng trong phạm vi đơn vị chương trình
có chứa các hàm này.
Chỉ thị return cho phép ra khỏi chương trình con function tại bất cứ vị trí nào .

7.4.2 Cú pháp của chương trình con function ( hàm ).


+ Cú pháp function :
function <tên_hàm> ( các đối số hình thức )
! Khai báo kiểu dữ liệu của hàm ( kết quả trả về qua tên hàm ) .
! Khai báo kiểu dữ liệu của các đối số hình thức .
! Khai báo các biến cục bộ ( sử dụng trong chương trình con ) .
! Các lệnh thi hành .
tên_hàm = < biểu thức > ! dòng cuối trong các lệnh thi hành .
end function <tên_hàm>

+ Tên hàm được đặt theo quy tắc tên biến.


+ Có thể khai báo kiểu dữ liệu trả về của hàm ở dòng đầu tiên như sau :
<kiểu dữ liệu của hàm> function <tên_hàm> ( các đối số hình thức )
Ví dụ : real function Tong (x,y,z)
real, intent(in) :: x,y,z
Tong = x+y+z
end function Tong
+ Ở chương trình chính , chúng ta sử dụng tên_hàm kèm theo các đối số thực sự được đặt
trong cặp dấu ngoặc ( ) như là một toán hạng trong các biểu thức .Ví dụ : p = Tong (a,b,c) * 2. ;
q = Tong (5.,10.,15.) .

7.4.3 Ví dụ về chương trình con external function.


program main
implicit none
real :: a,b,c,p
! Khai bao interface cho function
interface
function Tong (x,y,z)
real :: Tong
real,intent (in) :: x,y,z
end function Tong
end interface

101
a = 3.;b = 4.;c = 5.
p = Tong (a,b,c) * 2.
print*,p ! kết quả 24.
print*,Tong(5.,10.,15.) ! kết quả : 30.
read*
end program main

! Khai bao external funtion la chuong trinh doc lap

function Tong (x,y,z)


implicit none
real :: Tong
real,intent (in) :: x,y,z
Tong = x+y+z
end function Tong

+ External function được khai báo bên ngoài chương trình chính . Trong chương trình chính
chúng ta cần khai báo phần interface cho external function.
+ Giống như phần khai báo interface cho subroutine , interface cho function gồm tên
function kèm các đối số hình thức , khai báo kiểu dữ liệu cho hàm ; kiểu dữ liệu , thuộc tính cho
các đối số hình thức .Phần chi tiết các lệnh thi hành trong function không đưa vào interface.
+ Trình biên dịch cần thông tin của khối interface khi biên dịch chương trình chính .
+ Các đối số hình thức của hàm thường được gán thuộc tính intent(in) , nhận giá trị đầu vào
từ chương trình chính .
Chúng ta xem chương trình tính diện tích hình tròn ,sử dụng đồng thời chương trình con
kiểu thủ tục và chương trình con kiểu hàm , dạng internal như sau :
program ct_con
implicit none
real :: r,dt
real,parameter :: pi =3.14159 ! sử dụng chung cho các chương trình con internal
print ‘(a,$)’,’Nhap gia tri ban kinh : ‘
read*,r
call dt_htron(r,dt) ! Gọi subroutine
print ‘(f8.4)’,dt ! Kết quả của subroutine lấy qua biến dt .
print ‘(f8.4)’,dtht(r) ! Kết quả của hàm lấy qua tên hàm.
read*
stop Nhap gia tri ban kinh : 2.
contains 12.5664
! internal subroutine 12.5664
subroutine dt_htron(ra,s)
real,intent(in) ::ra
real,intent(out) ::s
s = ra*ra*pi ! Truy cập được hằng pi của chương trình chính .
end subroutine dt_htron
! internal function
function dtht (r)
real :: dtht
real,intent(in) :: r
dtht = r*r*pi ! Truy cập được hằng pi của chương trình chính .
end function dtht
end program ct_con
102
Nhận xét : Trong chương trình chính , kết quả của subroutine dt_htron được chứa trong
biến dt , trong khi hàm dtht thì dùng ngay tên hàm kèm theo đối số thực sự để lấy kết quả .
Chúng ta viết lại chương trình tính diện tích hình tròn , sử dụng chương trình con dạng
external function , trong chương trình chính chúng ta cần khai báo interface cho chương trình con
external :
program dt_hinhtron
implicit none
interface
function dtht (r)
real :: dtht
real ,intent(in) :: r
end function dtht Vao gia tri ban kinh : 3.
end interface Dien tich : 28.2743
real :: ra
print ‘(a,$)’,” Vao gia tri ban kinh : “ Kết quả .
read *,ra
print ‘(a,f8.4)’,”Dien tich : “,dtht(ra)
read*
end program dt_hinhtron
! Khai bao external function ,la chuong trinh doc lap .
function dtht(r)
implicit none
real :: dtht
real,intent(in) :: r
real,parameter :: pi = 3.14159 ! khai báo hằng pi .
dtht = r*r*pi
end function dtht

7.4.4 Cấu trúc khối giao diện interface .


Khối interface dùng để khai báo giao diện của các chương trình con external trong chương
trình chính , có dạng như sau :
interface
subroutine / function <tên_subroutine /function > ( danh sách các đối hình thức )
! Khai báo kiểu dữ liệu của hàm ( áp dụng cho chương trình con function ) .
! Khai báo các đối hình thức ( tên , kiểu dữ liệu , thuộc tính ) .
end subroutine / function
end interface
7.4.5 Chương trình con dạng external không cần khai báo giao diện.
Sử dụng tính chất ngầm định ( implicit ) của Fortran như trong phiên bản FORTRAN 77 ,
đối với các chương trình con dạng ngoài ( external ) ,chúng ta có thể không cần khai báo giao diện
trong chương trình chính như trong ví dụ sau đây ( chương trình con function đổi độ ra radian ) .
program funcradi
implicit none Vao gia tri goc bang do :
real :: deg , radi ! Phải khai báo tên hàm radi như một biến . 30.
print*,’Vao gia tri goc bang do : ‘ sin = 0.5000
read*,deg cos = 0.8660
write(*,10) ‘sin = ‘,sin(radi(deg))
write(*,10) ‘cos = ‘,cos(radi(deg))
10 format (a,f8.4)

103
read*
end program funcradi

!Ham doi do ra radian

function radi(do)
implicit none
real :: radi, pi
real,intent(in) :: do ! external function
pi = 4.0*atan(1.)
radi = do*pi/180.
end function radi

Lưu ý : tên hàm radi trong trường hợp này phải được khai báo như một biến kiểu real trong
chương trình chính , phù hợp với câu lệnh implicit none .
Cũng có thể khai báo tên hàm riêng như sau :
real , external :: radi !Từ khóa external báo cho biết radi là một chương trình bên ngoài .
Giá trị số pi được tính qua công thức : 4.0*atan(1.) .
Từ phiên bản Fortran 90 trở về sau , việc khai báo rõ ràng ( explicitly ) các chương trình
con dạng external qua khối interface trong chương trình chính sẽ có lợi điểm là giúp cho trình biên
dịch kiểm soát tốt tên các chương trình con được sử dụng trong chương trình chính ; sự tương
thích về số lượng , kiểu dữ liệu , tham số kind , thuộc tính giữa các đối hình thức và các đối thực
sự khi gọi sử dụng các chương trình con . Khai báo interface cho các external procedure là một
đặc điểm an toàn của Fortran 90 . Tương tự như việc khai báo đầy đủ các biến ( tên, kiểu dữ liệu ,
tham số ) tham gia vào chương trình phù hợp với chỉ thị implicit none ở đầu một đơn vị chương
trình ( bỏ qua quy tắc ngầm định của Fortran ) .
Với công cụ module ( chương 8 ) , sẽ hiệu quả hơn khi chúng ta đưa các chương trình con
external vào một module và khi cần sử dụng trong chương trình chính chúng ta chỉ cần khai báo
sử dụng module có chứa chương trình mà chúng ta cần sử dụng . Các thư viện Fortran đều lưu các
chương trình mẫu dưới dạng các module .

7.5 Chương trình con có đối hình thức là mảng ( vectơ, ma trận, …).
7.5.1 Khai báo đối số là mảng có kích thước rõ ràng trong chương trình con.
Chúng ta xem ví dụ về chương trình con kiểu hàm tính tích vô hướng của hai vectơ , có
đối số hình thức là vectơ có 03 phần tử được khai báo tường minh :
program array_arg
implicit none
real,dimension(1:3) :: a,b ! vectơ có 3 thành phần số .
a = 2. ; b = 3.
print*,”Tich vo huong : “,TichVoHuong(a,b) ! Kết quả :  Tich vo huong : 18.
read*
stop
contains

function TichVoHuong(vec1,vec2)
real :: TichVoHuong
real,dimension(1:3),intent(in) :: vec1,vec2 ! Kích thước mảng được khai báo rõ ràng.
TichVoHuong = sum(vec1*vec2)
end function TichVoHuong
end program array_arg
104
Lưu ý : các đối số thực sự phải tương thích với các đối hình thức về kiểu dữ liệu , số kind
và phải có cùng dạng ( shape ) .

7.5.2 Khai báo đối số là mảng có kích thước thể hiện bằng một biến nguyên .
program array_arg
implicit none
real,dimension(1:10) :: a,b ! vectơ có 10 phần tử .
a = 1. ; b = 2.
print*,”Tich vo huong : “,TichVoHuong(a,b,10) ! Tich vo huong : 20.
read*
stop
contains
function TichVoHuong(vec1,vec2,n ) ! khai thêm đối số n là kích thước mảng
real :: TichVoHuong
integer,intent(in) :: n ! kích thước mảng tùy vào biến n
real,dimension(1: n),intent(in) :: vec1,vec2
TichVoHuong = sum(vec1*vec2)
end function TichVoHuong
end program array_arg

Chương trình con function này có thêm đối số n là kích thước của mảng.

7.5.3 Khai báo đối số là mảng có kích thước tự do .


program array_arg
implicit none
real,dimension(1:10) :: a,b
a = 1. ; b = 2.
print*,”Tich vo huong : “,TichVoHuong(a,b)
read*
stop
contains
function TichVoHuong(vec1,vec2)
real :: TichVoHuong
real,dimension ( : ) , intent(in) :: vec1,vec2 ! kích thước vectơ được để tự do.
TichVoHuong = sum(vec1*vec2)
end function TichVoHuong
end program array_arg

Trong trường hợp này , kích thước mảng trong function được để tự do , dùng kí hiệu ( : ) ,
khi sử dụng hàm ở chương trình chính ,các đối số thực sự sẽ truyền kích thước mảng sang các đối
hình thức. Trong chương trình con, chúng ta có thể khai báo một biến nguyên n kiểu integer và
gán n = size(vec1) , để lấy kích thước mảng của các đối thực sự , phục vụ cho các tính toán trong
chương trình con khi cần đến kích thức này ( xem ví dụ dưới đây ) .

Chúng ta xem ví dụ về chương trình con function dist(OM) tính khoảng cách từ gốc tọa độ
O đến điểm M (x,y,z) : OM  x 2  y 2  z 2

program Test
implicit none
real,dimension (1:3) :: a,b
105
a = 2.
b = 3.
print ‘(a,f8.4)’,’Khoang cach OA : ‘, dist(a)
print ‘(a,f8.4)’,’Khoang cach OB : ‘, dist(b)
print ‘(a,f8.4)’,’Khoang cach OC : ‘, dist((/ 3.,4./)) ! vectơ OC chỉ có hai thành phần số .
read*
stop
contains

function dist(OM)
real :: dist
real,dimension ( : ) , intent(in) :: OM ! Kích thước mảng được để tự do .
integer :: i,n
real :: temp
n = size(OM) ! Lấy kích thước của đối số thực sự .
temp = 0.
do i = 1,n
temp = temp + OM(i)**2
end do
dist = sqrt(temp)
end function
end program Test Hình 7.2 : Kết quả program Test

Lưu ý : vòng lặp do ở chương trình function sử dụng chỉ số cuối n = size (OM) phụ thuộc
vào kích thước của đối số thực sự trong hàm .

7.5.4 Hàm có kết quả trả về là một mảng .


Kết quả của hàm có thể là một mảng thay vì là một giá trị vô hướng . Chúng ta xem chương
trình function tính tích có hướng ( cross product ) của hai vectơ trong R3 , kết quả trả về là một
vectơ.
program tichcohuong
implicit none
real,dimension (1:3) :: x,y,z
x = (/3.,-2.,1./) cross(x,y) -3. -4. 1.
y = (/2.,-1.,2./)
z = cross(x,y) cross(y,x) 3. 4. -1.
print*,’cross(x,y)’,z
print*
z = cross(y,x)
print*,’cross(y,x)’,z
read*
stop
contains
function cross(a,b)
real,dimension(1:3),intent(in) :: a,b
real,dimension (1:3) :: cross
cross = (/a(2)*b(3)-a(3)*b(2),& ! Kết quả trả về là một vectơ.
a(3)*b(1)-a(1)*b(3),&
a(1)*b(2)-a(2)*b(1) /)
end function cross
end program tichcohuong

106
Trong ví dụ sau , kết quả trả về của hàm là một mảng có kích thước bằng với kích thước
biến mảng ( đối hình thức có kích thước tự do ) :
program main
implicit none
real,dimension(10) :: List = 1.
print*,Transform (List)
read*
stop
contains
function Transform (vecto)
real,dimension(:),intent(in):: vecto ! Kích thước biến mảng được để tự do .
real,dimension (size(vecto)) :: Transform ! Kích thước mảng hàm bằng kích thước biến mảng.
integer :: i,n
n = size(vecto)
do i = 1,n
Transform(i) = i*vecto (i)
end do
end function Transform
end program main

Kết quả trả về của hàm Transform :


1. 2. 3. 4. 5. 6. 7. 8. 9. 10.

7.6 Chương trình con kiểu đệ qui (recursive subprogram ).


Chương trình con kiểu đệ qui là chương trình có thể tự gọi đến chính nó . Chúng ta xem ví
dụ về chương trình con function tính giai thừa n ( n! = 1*2*3*..*(n-1)*n ) được lập trình theo kiểu
đệ qui như sau :
program dequi
implicit none
print ‘(4I8)’,fac(0),fac(1),fac(4),fac(8) ! Kết quả → 1 1 24 40320
read*
stop
contains
recursive function fac(n) result(res)
integer,intent(in) :: n
integer :: res
if (n <= 1 ) then
res = 1
else
res = n * fac(n-1) ! công thức truy hồi .
end if
end function fac
end program dequi

Nhận xét : dòng khai báo tên hàm phải có từ khóa recursive đồng thời kết quả trả về của
hàm trong chương trình con được gửi vào biến res ( có cùng kiểu với hàm ) đi kèm với từ khóa
result .
Kiểu lập trình đệ qui thường tốn bộ nhớ hơn cách sử dụng vòng lặp do thông thường.

107
7.7 Chương trình con có đối hình thức là xâu kí tự .
Các đối hình thức trong chương trình con có thể là xâu kí tự , lúc này chiều dài của xâu hình
thức có thể được để tự do ( đặt tham số len = * ) . Khi được truyền đối số thực sự từ chương trình
chính , xâu kí tự trong chương trình con sẽ lấy chiều dài theo xâu của đối đầu vào .Chúng ta xem
ví dụ sau :
program ChaoBan
implicit none Cho biet ten ban :
character (len = 10) :: name Hien
print*,’Cho biet ten ban :’ Hello Hien
read*,name
print*, hello(name) Kết quả .
read*
stop
contains
function hello(ten)
character(len = *),intent(in) ::ten ! chiều dài xâu ten được để tự do.
character(len = len(ten)+6) :: hello ! chiều dài của hàm phụ thuộc chiều dài xâu ten
hello = “Hello “//trim(adjustl(ten)) ! Kết quả của hàm kiểu xâu kí tự.
end function hello
end program ChaoBan

7.8 Chương trình con có đối hình thức được gán thuộc tính optional .
Các đối hình thức trong chương trình con có thể được gán thuộc tính optional , trong trường
hợp này các đối thực sự tương ứng với các đối optional có thể xuất hiện hoặc không xuất hiện trong
danh sách các đối đầu vào được gọi từ chương trình chính . Chúng ta xem ví dụ sau về chương
trình con external function tính hàm y = xn :
program main
implicit none
real :: x
interface
function y(x,n)
real :: y
real,intent(in) :: x
integer,intent(in),optional :: n
end function y
end interface
x = 2.
print ‘(4f6.2)’, y(x), y(x,2),y(x,3),y(x,6) ! Kết quả : → 2.00 4.00 8.00 64.00
read*
end program main
!external function
function y(x,n)
implicit none
real :: y
real,intent(in) :: x
integer,intent(in),optional :: n ! biến n có thuộc tính optional
integer :: ns ! biến cục bộ của hàm
ns = 1
if (present(n)) ns = n ! hàm present (n) kiểu logical
y = x**ns
end function y

108
+ Hàm present (n) có kiểu logical dùng để test xem đối số n , mang thuộc tính optional ,
có xuất hiện trong danh sách đối số đầu vào của hàm hay không ?
+ Do đối số n là optional nên chúng ta có thể viết : y(x) , y(x,3) , y(x,5) …

7.9 Chương trình con có đối hình thức là subroutine hoặc function.
Chương trình ví dụ sau có chương trình con function ( dạng external) tính tích phân xác
định theo công thức hình thang ( xem bài thực hành 4.6.2 ) . Trong danh sách các đối số hình thức
của function tính tích phân này, có đối là một hàm f nào đó mà chúng ta phải khai báo phần interface
trong chương trình con tính tích phân . Các biểu thức hàm cụ thể ( do người lập trình định nghĩa )
để đưa vào chương trình con tính tích phân được khai báo dưới dạng external function . Toàn bộ
các chương trình con external function được khai báo trong block interface ở chương trình chính .
Đối với các hàm đã được định nghĩa sẵn trong Fortran ( intrinsic functions ) như hàm sin , cos ,exp
…, chúng ta chỉ cần khai báo từ khóa intrinsic kèm theo tên hàm là có thể đưa vào sử dụng như
đối số của chương trình con .

Các tích phân xác định được tính trong chương trình :
1 1 1 

 (2 x  3x )dx ;  e dx ;  e dx ;  sin x dx . Số đoạn chia n = 1000


2 2x x

0 0 0 0

program main
implicit none
! khai bao block interface cho 3 function.
interface
function tichphan(f,a,b,n) ! hàm f là đối hình thức .
real :: tichphan
real,intent(in) :: a,b ! Khai báo interface cho hàm tichphan có
integer,intent(in) :: n ! chứa đối số là chương trình con.
interface
function f(x)
real :: f ! Khai báo interface cho hàm f là đối hình thức .
real,intent(in) :: x
end function f
end interface
end function tichphan

function g(x)
real :: g ! Khai báo interface cho hàm g(x) do người lập trình định nghĩa.
real,intent(in) :: x
end function g

function h(x)
real :: h ! Khai báo interface cho hàm h(x) do người lập trình định nghĩa.
real,intent(in) :: x
end function h
end interface ! Ket thuc khai bao block interface .
intrinsic :: sin , exp ! Khai báo sử dụng hàm chuẩn của Fortran ( sin(x) ; exp(x) )
print ‘(f10.5)’,tichphan(g,0.,1.,1000 )  kết quả : 2.00000
print ‘(f10.5)’,tichphan(h,0.,1.,1000)  kết quả : 3.19453
print ‘(f10.5)’,tichphan(exp,0.,1.,1000)  1.71828
print ‘(f10.5)’,tichphan(sin,0.,3.14159,1000)  2.00000
109
read*
end program main
! Khai bao cac external function
function tichphan(f,a,b,n) Công thức hình thang :
real :: tichphan
real,intent(in) :: a,b  f (a)  f (b) n 1 
b

integer,intent(in) :: n a f ( x ) dx  h    f (a  ih) 
interface  2 i 1 
function f(x) ba
h
real :: f n
real,intent(in) :: x
end function f
end interface ! khai báo interface cho đối là chương trình con function
real :: s, h
integer :: i
h = (b-a)/n ; s = 0.
do i = 1,n-1
s = s + f(a+i*h)
end do
tichphan = h*( (f(a)+f(b))/2. + s) ! công thức hình thang .
end function tichphan
function g(x) ! Hàm g(x) do người lập trình định nghĩa.
real :: g
real,intent(in) :: x
g = 2.*x + 3.*x*x ! g(x) = 2x + 3x2
end function g
function h(x) ! Hàm h(x) do người lập trình định nghĩa.
real :: h
real,intent(in) :: x
h = exp(2*x) ! h(x) = e2x
end function h

Bạn đọc cần phân tích kỹ chương trình nêu trên và chạy test với các tích phân xác định khác
nhau .

7.10 Thực hành .


7.10.1 Bạn đọc lập trình tạo chương trình con subroutine rotation tính tọa độ mới của
điểm M (x,y) sau khi hệ trục Oxy quay một góc a ( tính bằng độ ) thành hệ trục mới Ox ryr.
M (x,y)  M(xr,yr) (Ox,Oxr) = a
+ Đối số đầu vào : x,y,a
+ Đối số đầu ra : xr,yr
Trong subroutine cần đổi góc a tính bằng độ ra góc ar tính bằng radian .
Quan hệ giữa (xr,yr) và (x,y) :
xr = x*cos (ar) + y*sin(ar) ; yr = -x*sin (ar) + y*cos(ar)

+ Chương trình tham khảo : Vao toa do x,y,goc a (do) :


1. 1. 45.
program main Toa do moi xr,yr :
implicit none 1.414 0.000
real :: x,y,a,xr,yr
print*,’Vao toa do x,y,goc a (do) : ‘
110
read*,x,y,a
call rotation (x,y,a,xr,yr)
print*, ‘Toa do moi xr,yr :’
print ‘(2f10.3)’ ,xr,yr Kết quả .
read*
stop
contains yr y
subroutine rotation(x,y,a,xr,yr)
xr
real,intent(in) :: x,y,a
real,intent(out) :: xr,yr M
real ::ar, pi = 3.14159
ar = a/180.*pi ! Doi goc ra radian a
xr = x*cos(ar) + y*sin(ar) x
yr = -x*sin(ar) + y*cos(ar) O
return
end subroutine rotation Hình 7.3 : Hệ trục Oxy và Oxryr
end program main
7.10.2 Bạn đọc lập trình tính chương trình con function det2 (a) tính định thức cấp 2 , đối
số a là một ma trận (2,2) . Áp dụng để giải hệ thống hai phương trình tuyến tính hai ẩn số bằng
phương pháp định thức .
Chương trình tham khảo :
program main
implicit none
! Giai he 2 pttt 2 an
! Ax = b
real,dimension (2,2) :: a
real,dimension (2) :: b
real,dimension (2,3) :: am ! ma tran mo rong hai hang ba cot.
real :: x,y
print*,’Vao cac he so cua ma tran A :’ 3x + 4y = 2
read*,a 2x + 3y = 7
print*,’Vao cac he so cua vecto b :’ Hệ 2 pttt 2 ẩn số .
read*,b
am (1,1:2) = a(1,1:2) ; am(1,3) = b(1)
am (2,1:2) = a(2,1:2) ; am(2,3) = b(2)
Vao cac he so cua ma tran A :
if ( det2(a) ==0.) then
3. 2. 4. 3.
print*, ‘Khong giai ‘
Vao cac he so cua vecto b :
stop
2. 7.
read*
x : -22. y: 17.
end if
x = det2(am(:,3:2:-1))/det2(a)
y = det2(am(:,1:3:2))/det2(a) Kết quả .
print*,’x : ‘,x,’ ‘,’y: ‘,y
read*
stop
contains
function det2(a) ! chương trình con tính định thức cấp 2
real :: det2
real,dimension (2,2),intent(in) :: a
det2 = a(1,1)*a(2,2)-a(1,2)*a(2,1)
end function det2
end program main
111
Dựa vào chương trình trên, bạn đọc lập trình function det3(a) tính định thức cấp 3 và áp
dụng để giải hệ 3 phương trình tuyến tính 3 ẩn như sau bằng phương pháp định thức.
2x1 + 3 x2 + 5x3 = 2
3x1 – 2.5 x2 + 4x3 = 10
-4x1 + 3 x2 + 2x3 = 2

Kết quả : x1 = -1. ; x2 = -2. ; x3 = 2.

Ghi chú : Công thức tính định thức cấp 2 :


a11 a12
= a11a22  a12 a21
a21 a22

Công thức tính định thức cấp 3 :


a11 a12 a13
a21 a22 a23 = a11a22 a33  a12 a23a31  a13a21a32  a13a22a31  a11a23a32  a12a21a33
a31 a32 a33

7.10.3 Tính đạo hàm bằng công thức gần đúng .


Dựa vào định nghĩa của đạo hàm , trong lập trình chúng ta sử dụng công thức gần đúng
sau để tính đạo hàm của hàm f(x) tại x = x0 :
f’(x0) ~ [ f(x0 + h) – f(x0) ] / h , trong đó chọn h có bước nhỏ ( h = 0.01 hoặc 0.001) .
Bạn đọc phân tích chương trình sau , tính đạo hàm của hàm f(x) = x3  x tại x0 = 1 .
Chương trình có hai chương trình con function là f(x) và df(x) .
program daoham
implicit none
real :: x0 = 1.
print*,f(x0), df(x0) !  2. 3.503084
read*
stop
contains
real function f(x)
real,intent(in) :: x 1
f = x**3 + sqrt(x)
f '( x )  3 x 2

end function f
2 x
real function df(x) ! đạo hàm của f(x)
real,intent(in) :: x
real :: h = 0.001
df = (f(x+h) – f(x))/ h ! công thức gần đúng đạo hàm .
end function df
end program
Ghi chú : Tính toán trực tiếp với x0 = 1 ; có f (1) = 2 và f’(1) = 3,5 .

7.10.4 Tìm trị gần đúng của nghiệm riêng của phương trình vi phân cấp 1 y’ = f(x,y) với
điều kiện đầu y = y0 khi x = x0 bằng phương pháp Euler .

112
Phương trình : y’ = 3x – y/x ; đk đầu : y = 1 ( x = 1) , tính y = ? khi x = 5 .
Dựa vào công thức gần đúng để tính đạo hàm ở 7.10.3 , có các công thức sau :
y (i)  y (i  1)
y '(i  1)  f ( x(i  1), y (i  1))  (1)
h
y (i )  y (i  1)  f ( x(i  1), y (i  1))h (2)

Giữa x0 = 1 và x = 5 , chia làm 1000 đoạn . Sử dụng công thức (2) để lập dãy số y(i) với i
= 0..1000 . Giá trị y (1000 ) tương ứng với y (5) .
program Euler
implicit none
integer,parameter :: n = 1000, r = 8
integer :: i
real (kind = r),dimension (0:n):: x,y
real ::a,b,h 0.24990401D+02
a = 1._r ; b = 5._r ; h = (b-a)/n
x = (/ 1._r, (1._r+ i*h , i=1,n) /) ! Day so x Kết quả y tại x = 5 .
y(0) = 1._r ! gia tri dau (1,1)
do i = 1,n
y(i) = y(i-1) + f(x(i-1),y(i-1))*h ! Cong thuc Euler
end do
print ‘(d18.8)’ , y(n) ! Tri so nghiem rieng tai x = 5
read*
stop
contains
function f(x,y)
real (kind = r) :: f
real ( kind = r),intent(in) :: x,y
f = 3._r*x –y/x
end function f
end program Euler

Ghi chú : nghiệm riêng của phương trình vi phân trên là y = x2 ; tại x = 5 , y = 25 .

+ Bạn đọc dùng phương pháp Euler như trên tính trị gần đúng tại x = 2 của nghiệm riêng
phương trình vi phân y’ = xy2 +1 với đk đầu y = 0 ( x = 0) .

7.10.5 Lập trình tính 3 góc của một tam giác ABC có các cạnh a = BC , b = AC, c= AB bằng
cách lập function goc(b,c,a) tính góc A kẹp giữa hai cạnh b ,c ( lưu ý thứ tự các cạnh )
a2 = b2 + c2 – 2bc cos (A)  A = acos ((b2 + c2 –a2) /(2bc)) . Góc được tính ra độ .

Bạn đọc phân tích và chạy test chương trình sau :

program tinh_goc_tam_giac
implicit none
real :: a,b,c,gocA,gocB,gocC
print*,"***CHUONG TRINH TINH GOC CUA TAM GIAC***"
print*,"***Canh a/b/c doi dien gocA/gocB/gocC***"
print*
print '(a,$)',"Vao ba canh cua tam giac a,b,c :"
read*,a,b,c

113
! Tinh cac goc , don vi la do
gocA = goc(b,c,a)
gocB = goc(a,c,b)
gocC = goc(a,b,c)
print '(a,f7.3)',"Goc A : ",gocA
print '(a,f7.3)',"Goc B : ",gocB
print '(a,f7.3)',"Goc C : ",gocC
read*
contains
real function goc(x,y,z)
! Tra ve goc giua hai canh x,y
intrinsic :: acos,max,atan ! khai bao su dung ham chuan cua Fortran (optional ) .
real ,intent(in) :: x,y,z
real :: gocr ,pi
pi = 4.*atan(1.)
! Kiem tra 3 canh x,y,z co tao thanh tam giac hay khong ?
if (2*max(x,y,z) < (x+y+z) ) then
gocr = acos ((x**2+y**2-z**2)/(2.*x*y)) ! goc tinh bang radian
goc = gocr*180./pi ! Doi ra do
else ! Khong phai tam giac
goc = 0.0
end if
end function goc
end program tinh_goc_tam_giac

Kết quả :
***CHUONG TRINH TINH GOC CUA TAM GIAC***
***Canh a/b/c doi dien gocA/gocB/gocC***
A
Vao ba canh cua tam giac a,b,c :5. 4. 3.
Goc A : 90.000
Goc B : 53.130 c b
Goc C : 36.870
a

B C

Ghi chú : +Trường hợp các cạnh a,b,c không tạo thành tam giác , hàm goc sẽ trả về trị số 0.0 :
***CHUONG TRINH TINH GOC CUA TAM GIAC***
***Canh a/b/c doi dien gocA/gocB/gocC***
Vao ba canh cua tam giac a,b,c :5. 2. 1.
Goc A : 0.000
Goc B : 0.000
Goc C : 0.000

+ Câu lệnh khai báo intrinsic :: <danh sách các hàm > nhằm giúp cho người đọc chương trình
biết các hàm chuẩn được sử dụng trong chương trình . Ngoài ra nếu trình biên dịch không hỗ trợ một hàm
nào đó trong danh sách thì sẽ có lỗi khi chạy chương trình .

*****

114
CHƯƠNG 8 : ĐƠN VỊ CHƯƠNG TRÌNH MODULE
8.1 Khái niệm về module .
Trong Fortran , module là một đơn vị chương trình độc lập dùng để chia sẻ thông tin , các
chương trình sử dụng chung … , người lập trình có thể đưa vào module các nội dung như sau :
_ Khai báo các hằng thể hiện độ chính xác của kiểu biến số học ( lưu số kind ) .
_ Khai báo các hằng số vật lý, toán học , hệ số chuyển đổi thông dụng .
_ Khai báo các dữ liệu như biến, mảng sử dụng chung cho nhiều chương trình ( global
data ) .
_ Khai báo các giao diện ( interface ) của các chương trình .
_ Các chương trình tiện ích được sử dụng chung ( gồm các hàm ,thủ tục : gọi là module
procedures ) . Các module procedure này lại có thể chứa các internal procedures bên trong qua từ
khóa contains , giống như cấu trúc main program đã biết ở chương 7 .
_ Các chương trình định nghĩa hàm, phép tính mới do người lập trình thiết lập .
Module có thể được xem như là một đơn vị chương trình được đóng gói (encapsulation ) .
Thông tin trong một module sẽ được chia sẻ cho các đơn vị chương trình khác nhau có yêu cầu sử
dụng toàn bộ hay một phần các nội dung chứa trong module này . Một số tổ chức phát triển Fortran
đã xây dựng nhiều module chứa các chương trình tiện ích nhằm giúp người lập trình có thêm công
cụ giải quyết nhanh các vấn đề thường gặp .

8.2 Cấu trúc tổng quát một module .


Cấu trúc một module có dạng như sau :
Module < tên_module > ! đặt tên module theo quy tắc tên biến.
use < tên_module_tham_chiếu > ! khai báo sử dụng các module khác nếu có.
implicit none
! Các chỉ thị khai báo : các hằng , biến , dữ liệu kiểu bản ghi ...
! Có tính chất toàn cục (global) đối với chương trình sử dụng module này ,
Contains
! Các chương trình của module . ! module procedures (subroutine ,function )
End Module <tên_module>

Khi một đơn vị chương trình muốn sử dụng một module nào đó thì đặt chỉ thị như sau
ngay dưới từ khóa Program hoặc Module nhưng trước từ khóa implicit none :
Program/Module < Tên >
Use < tên_module >
Implicit none
Chúng ta sẽ tìm hiểu module qua các ví dụ .
8.3 Module chứa các hằng số , dữ liệu dùng chung, khai báo interface .
8.3.1 Ví dụ 01 .
module kinds ! lưu số kind của số thực chính xác kép qua hằng r .
implicit none
integer,parameter :: r = selected_real_kind (15,307) Cho biet so feet va so inch :
end module kinds 30 10
module doi_dv ! lưu các hệ số chuyển đổi đơn vị Chieu dai (m): 0.939800D+01
use kinds ! khai báo sử dụng module kinds
implicit none
real(kind = r), parameter :: in = 0.0254_r , ft = 0.3048_r ,& Kết quả .
& yd = 0.9144_r, lb = 0.4536_r
end module doi_dv

115
program main
use kinds
use doi_dv , only : in,ft ! Chỉ sử dụng hằng in , ft trong mudule doi_dv
implicit none
real (kind = r) :: so_ft,so_in,chieudai
print*,'Cho biet so feet va so inch :'
read*, so_ft, so_in
chieudai = so_ft*ft + so_in*in
print '(a,d15.6)','Chieu dai (m): ',chieudai ;
read*
end program main

Trong ví dụ 01 trên chúng ta khai báo hai module :


Module kinds : khai báo hằng nguyên r là số kind của kiểu số thực có độ chính xác kép .
Các đơn vị chương trình có sử dụng module kinds sẽ dùng hằng r làm số kind cho dữ liệu kiểu số
thực có độ chính xác kép .
Module doi_dv : trong module này có khai báo sử dụng module kinds và khai báo các hằng
số thực kiểu chính xác kép là in , ft , yd , lb là các hệ số dùng để chuyển đổi đơn vị chiều dài , trọng
lượng từ hệ đơn vị Anh Mỹ sang hệ SI.
Trong chương trình chính , sau từ khóa Program main , chúng ta khai báo sử dụng hai
module kinds và module doi_dv bằng từ khóa use . Riêng module doi_dv , chúng ta khai báo thêm
option only : in, ft nhằm giới hạn các hằng số thực sự sử dụng trong chương trình chính .Trong
chương trình chính , chúng ta sử dụng các hằng r , in, ft đã khai báo trong các module nêu trên để
phục vụ tính toán .
Ngoài ra ,chúng ta có thể sử dụng tên trong chương trình chính khác với tên trong module
như sau :
Use doi_dv , only : f => ft ! Dùng tên f trong chương trình chính thay cho ft trong module
Lưu ý : văn bản các module phải được bố trí trước văn bản chương trình chính . Trong ví dụ
trên các module được biên dịch đồng thời với chương trình chính và tạo thành tập tin thi hành *.exe
Khi biên dịch và liên kết chương trình nêu trên , ngoài tập tin thi hành *.exe , chúng ta còn
có hai tập tin module kinds.mod và doi_dv.mod nằm cùng thư mục .

Đơn vị module có thể được biên dịch riêng và phải được biên dịch trước đơn vị chương
trình có sử dụng module này .Khi sử dụng trình biên dịch g95 , chúng ta sắp xếp như sau để tạo
file ketqua.exe từ một file module01 và một file chương trình chính main có khai báo sử dụng
module01 :

c:\> g95 – o d:\work\ketqua d:\work\module01.f90 d:\work\main.f90

8.3.2 Ví dụ 02.

module kinds ! Khai báo hằng r là số kind của số thực chính xác kép .
implicit none
integer,parameter :: r = selected_real_kind (15,307)
end module kinds
module const ! chứa hằng số pi
use kinds
implicit none
real (kind = r), parameter :: pi = 3.141592654_r
end module const
116
module common_data ! sử dụng dữ liệu chung là hai biến bk,cao
use kinds
implicit none
real(kind = r), save :: bk,cao ! được gán thuộc tính save .
end module common_data

program main
use kinds
use common_data Trong luong (kgf) : 0.565487D+06
implicit none
interface Kết quả .
function TL(gamma)
use kinds
real(r) :: TL
real(r),intent(in) ::gamma
end function TL
end interface
real (r) :: gamma = 1000_r ! Trọng lượng riêng của nước .
bk = 6._r ; cao = 5._r ! sử dụng hai biến bk, cao có trong module common_data
print '(a,d14.6)','Trong luong (kgf) : ', TL(gamma)
read*
end program main
! Khai báo external function .Sử dụng các module đã khai báo .
function TL(gamma)
use kinds
use const
use common_data
implicit none
real(r) :: TL
real(r),intent(in) ::gamma
TL = gamma*pi*bk*bk*cao ! Trọng lượng khối chất lỏng hình trụ tròn.
end function TL

Chương trình ví dụ 02 dùng để tính trọng lượng khối nước hình trụ tròn có 03 module ,
ngoài module kinds đã biết , còn có module const khai báo hằng số pi và module common_data
khai báo 2 biến kiểu số thực chính xác kép là bk ( bán kính ) , cao ( chiều cao ) được sử dụng
chung cho các đơn vị chương trình có sử dụng module này . Lưu ý hai biến này phải được gán
thuộc tính save .Trong chương trình chính có khai báo interface cho hàm external TL(gamma) ,
với đối số gamma là trọng lượng riêng của chất lỏng .
Như vậy module còn có thể chứa các dữ liệu ( biến, mảng ,…) dùng chung cho các đơn vị
chương trình có khai báo sử dụng module này .

8.3.3 Ví dụ 03.


Chúng ta viết lại chương trình ví dụ 02 , phần khai báo interface cho external function
TL(gamma) trong chương trình chính được đưa vào một module có tên list_interface .
module kinds
implicit none
integer,parameter :: r = selected_real_kind (15,307)
end module kinds
module const
use kinds
117
implicit none
real (kind = r),parameter :: pi = 3.141592654_r
end module const
module common_data
use kinds
implicit none
real(kind = r), save :: bk,cao
end module common_data
module list_interface ! khai báo interface cho các chương trình external có sử dụng trong
implicit none ! chương trình chính .
interface
function TL(gamma)
use kinds
real(r) :: TL
real(r),intent(in) ::gamma
end function TL
end interface
end module list_interface
program main
use kinds Trong luong (kgf) : 0.565487D+06
use common_data
use list_interface
real (r) :: gamma = 1000_r Kết quả .
bk = 6._r ; cao = 5._r
print '(a,d14.6)','Trong luong (kgf) : ',TL(gamma)
read*
end program main
! Khai báo external function.
function TL(gamma)
use kinds
use const
use common_data
implicit none
real(r) :: TL
real(r),intent(in) ::gamma
TL = gamma*pi*bk*bk*cao
end function TL

8.4 Module chứa các chương trình .


8.4.1 Ví dụ 04.
Tiện lợi nhất là dùng module để chứa các chương trình kiểu subroutine và function được
lập trình sẵn để sử dụng chung cho nhiều đơn vị chương trình , các chương trình này ( gọi là
module procedures ) được khai báo bên trong một module với từ khóa contains ( tương tự internal
subprograms trong chương trình chính ) .
module kinds
implicit none
integer,parameter :: r = selected_real_kind (15,307)
end module kinds
module const
use kinds
implicit none

118
real (kind = r),parameter :: pi = 3.141592654_r
end module const
module circle
use kinds
use const
implicit none
contains
subroutine HinhTron(ra,dt,cv)
real(r),intent(in) :: ra
real(r),intent(out) :: dt,cv
dt = pi*ra*ra
cv = 2._r*ra*pi
end subroutine HinhTron
function KhoiTru(ra,h)
real(r):: KhoiTru
real(r),intent(in) :: ra,h
KhoiTru = pi*ra*ra*h
end function KhoiTru
end module circle
program main
use kinds
use circle ! sử dụng các chương trình của module circle
implicit none
real(r) :: bk,dt,cv
print*,'Cho biet ban kinh :' ; read*,bk
call HinhTron(bk,dt,cv) ! Gọi subroutine HinhTron trong module circle
print '(a,2d16.8)','Dien tich & Chu vi hinh tron : ',dt,cv
print '(a,d16.9)' ,'The tich khoi tru : ', KhoiTru(5._r,8._r) ! bán kính 5,chiều cao 8
read*
end program main
Kết quả :
Cho biet ban kinh :
5.
Dien tich & Chu vi hinh tron : 0.78539816D+02 0.31415927D+02
The tich khoi tru : 0.628318531D+03

Trong chương trình chính chúng ta có thể gọi subroutine HinhTron ( ) hoặc dùng hàm
KhoiTru ( ) là các chương trình đã có khai báo chi tiết trong module circle . Sử dụng module có
chứa các chương trình tiện ích là một giải pháp rất linh hoạt trong lập trình Fortran . Bản thân một
module procedure cũng có thể có các internal procedures nằm bên trong nó được bố trí dưới từ
khóa contains .
Các module này có thể được biên dịch riêng hoặc biên dịch chung với chương trình chính .
Trong phần mềm FTN95 , chúng ta tạo một project chứa file main riêng và các file module có
liên quan nằm trong một thư mục , đưa các file này vào phần source code , xong build và run sẽ
tạo file *.exe của project , tương tự như chúng ta đặt văn bản các module ở trước program main
trong phần mềm Force ( giống các ví dụ nêu trên ) , sau đó biên dịch và run .

8.4.2 Ví dụ 05.


Trong chương trình sau , chúng ta lập một module integral dùng tính tích phân xác định
bằng công thức hình thang . Module này chứa chương trình function tichphan (f,a,b,n) , trong đó
đối hình thức f là một hàm nào đó cần được khai báo interface .
119
Lưu ý : trong chương trình chính , các hàm dưới dấu tích phân g(x), h(x) là các external
function và phải được khai báo đầy đủ phần interface cho từng hàm .
module integral
implicit none
contains
function tichphan(f,a,b,n)
real:: tichphan
real,intent(in) :: a,b
integer,intent(in) :: n

interface
function f(x)
real :: f
real ,intent(in) :: x ! khai báo interface cho đối hình thức là chương trình con .
end function f
end interface
real :: s,h
integer :: i
h = (b-a)/n ; s = 0.
do i = 1,n-1
s = s + f(a+i*h)
end do
tichphan = h*( (f(a)+f(b))/2. + s)
end function tichphan
end module integral
!!
program main
use integral
implicit none
interface ! khai báo giao diện cho các hàm sử dụng trong main program .
function g(x)
real:: g
real,intent(in) :: x
end function g
function h(x)
real:: h
real,intent(in) :: x
end function h
end interface
intrinsic :: sin ! khai báo sử dụng hàm chuẩn của Fortran
print '(a,e15.6)','Ket qua : ',tichphan(g,0.,1.,1000 )
print '(a,e15.6)','Ket qua : ',tichphan(h,0.,1.,1000 )
print '(a,e15.6)','Ket qua : ',tichphan(sin ,0.,3.14159,1000 )
read*
end program main
! external function
function g(x)
real :: g
real,intent(in) :: x
g = sqrt(1. + x*x)
end function g Kết quả .
120
function h(x)
real :: h
real,intent(in) :: x
h = exp(x*x)
end function h

Chúng ta viết lại ví dụ 05 bằng cách thêm module hamso , chứa khai báo các hàm g(x),
h(x) dưới dấu tích phân . Chương trình chính lúc này rất gọn , không cần phải viết phần external
function và khai báo interface cho các hàm này . Lưu ý : trong chương trình chính chúng ta cần
thêm chỉ thị use hamso .
module integral
implicit none
contains
function tichphan(f,a,b,n)
real:: tichphan
real,intent(in) :: a,b
integer,intent(in) :: n
interface
function f(x)
real :: f
real ,intent(in) :: x
end function f
end interface
real :: s,h
integer :: i
h = (b-a)/n ; s = 0.
do i = 1,n-1
s = s + f(a+i*h)
end do
tichphan = h*( (f(a)+f(b))/2. + s)
end function tichphan
end module integral
! *****
module hamso
implicit none
contains ! khai báo các hàm dưới dấu tích phân .
function g(x)
real :: g
real,intent(in) :: x
g = sqrt(1. + x*x)
end function g
function h(x)
real :: h
real,intent(in) :: x
h = exp(x*x)
end function h
end module hamso
program main
use integral
use hamso
intrinsic :: sin
print '(a,e15.6)','Ket qua : ',tichphan(g,0.,1.,1000 )
121
print '(a,e15.6)','Ket qua : ',tichphan(h,0.,1.,1000 )
print '(a,e15.6)','Ket qua : ',tichphan(sin ,0.,3.14159,1000 )
read*
end program main

8.5 Lập hàm có tính chất generic do người lập trình định nghĩa.
Trong Fortran 90, nhiều hàm có tính chất generic , nghĩa là một tên hàm dùng chung cho
các chương trình khác nhau tùy thuộc vào kiểu , số kind , rank (vectơ, ma trận …) của đối số đầu
vào . Ví dụ như hàm sin ( ) qua ví dụ sau có đối số là số thực chính xác đơn và số thực chính xác
kép sẽ cho kết quả khác nhau :
program testsin
implicit none
print*,sin(1._4) sin(1._4) = 0.84147096
print*,sin(1._8) sin(1._8) = 0.8414709848078965
read*
end
Chúng ta sẽ viết một module cho hàm exsin (x) có tên là esin (x) có tính chất generic , tên
hàm esin (x) sẽ dùng chung cho hai chương trình tùy thuộc vào đối số đầu vào là số thực có độ
chính xác đơn ( chương trình esin_sp ) hay số thực có độ chính xác kép ( chương trình esin_dp ) .
Bạn đọc cần phân tích kỹ cấu trúc của module này .
module rkind ! Lưu số kind
implicit none
integer,parameter :: rs = selected_real_kind (6,37) ! so thuc chinh xac don .
integer,parameter :: rd = selected_real_kind (15,307) ! so thuc chinh xac kep .
end module rkind
module mod_esin
use rkind
implicit none
interface esin ! Khai báo interface cho hàm generic esin
module procedure esin_sp ,esin_dp ! Khai báo tên hai chương trình trong module
end interface esin
contains
function esin_sp(x) ! dùng cho đối số là số thực chính xác đơn .
real(rs),intent(in) :: x
real(rs) :: esin_sp
esin_sp = exp(x)*sin(x)
end function esin_sp
function esin_dp(x) ! dùng cho đối số là số thực chính xác kép .
real(rd),intent(in) :: x
real(rd) :: esin_dp
esin_dp = exp(x)*sin(x)
end function esin_dp 2.2873552
end module mod_esin 2.2873552871788423
program main
use rkind Kết quả .
use mod_esin
implicit none
print*,esin(1._rs) ; print*, esin(1._rd) ! Xuất kết quả hàm exsin(x)
read*
end program main

122
8.6 Định nghĩa phép tính mới .
Trong chương trình sau , chúng ta sẽ viết module TichCoHuong dùng để định nghĩa một
phép tính có tên là .cross. dùng tính tích có hướng của hai vectơ như sau :
module TichCoHuong
implicit none
interface operator (.cross.) ! .cross. là tên phép tính .
module procedure TCH ! TCH là tên của chương trình nằm trong module
end interface
contains ! Khai báo interface cho phép tính .cross.

function TCH (a,b)


real,dimension(3),intent(in) :: a,b
real,dimension(3) :: TCH
TCH(1) = a(2)*b(3) - a(3)*b(2) ! Chương trình tính kết quả trả về.
TCH(2) = a(3)*b(1) - a(1)*b(3)
TCH(3) = a(1)*b(2) - a(2)*b(1)
end function TCH
end module TichCoHuong
program main Ket qua z : 0. 0. 1.
use TichCoHuong Suat cua z : 1.
implicit none
real,dimension(3) :: x,y,z
x = (/ 1.,0.,0. /)
y = (/ 0.,1.,0. /)
z = x.cross.y ! Sử dụng phép tính .cross. đã định ngĩa trong module Tich Co Huong.
print*,'Ket qua : ', z
print*, 'Suat cua z : ',sqrt (dot_product(z,z))
read*
end program main

+ Tên phép tính phải có dấu chấm trước và sau : .cross.
+ Trong module , phép tính .cross. được khai báo qua phần interface :
interface operator (.cross.)
module procedure TCH
end interface
operator (.cross.)  cho biết tên của phép tính là .cross.
module procedure TCH là function TCH (a,b)  cho biết tên của chương trình trong
module là TCH dùng để tính kết quả trả về của phép tính a.cross.b
+ Trong chương trình chính sau khi có khai báo use TichCoHuong , chúng ta được quyền
sử dụng tên phép tính trong các biểu thức như z = x .cross.y

8.7 Biến có thuộc tính public và private .


Theo quy tắc ngầm định ( implicit ) , tất cả các biến , hằng trong một module đều mang
thuộc tính public , điều này có nghĩa là tất cả các đơn vị chương trình có khai báo sử dụng module
này đều có quyền truy cập đến tất cả các biến , hằng có trong module . Trong trường hợp nếu một
hay vài biến trong module có khai báo thuộc tính private , thì những biến này chỉ có giá trị trong
nội bộ module mà thôi , các đơn vị chương trình khác không truy cập được các biến private này ,
mặc dù có khai báo sử dụng module . Thông thường các biến có thuộc tính private dùng để tính

123
toán trong nội bộ module .Chúng ta xem chương trình đổi đơn vị áp suất tính bằng psi ( lb/in 2) ra
kPa hoặc kgf/cm2 như sau :
module kinds
implicit none
integer,parameter :: r = selected_real_kind (15,307)
end module kinds
module doi_apsuat
! Doi ap suat tu lb/in2 ( psi ) ra kPa va kgf/cm2
use kinds
implicit none
real(kind = r), parameter , private :: in = 2.54_r , ft = 30.48_r ,&
& lb = 0.4536_r
real(kind = r),parameter :: k_kpa = (lb/(in*in))*98._r ! biến public
real(kind = r),parameter :: k_kg_cm2 = (lb/(in*in)) ! biến public
end module doi_apsuat
program main
use kinds
use doi_apsuat
implicit none
real(r) :: p
print '(a,$)','Cho biet ap suat ( lb/in2) : ' ;read*,p
print'(a,d16.6)','Ap suat (kPa) : ', p*k_kpa
print'(a,d16.6)','Ap suat (kgf/cm2) : ', p*k_kg_cm2
read*
end
Kết quả :
Cho biet ap suat ( lb/in2) : 100.
Ap suat (kPa) : 0.689020D+03
Ap suat (kgf/cm2) : 0.703081D+01

Trong module doi_apsuat , các hằng in, ft, lb được khai báo thuộc tính private , chỉ có giá
trị trong phạm vi module này ( dùng tính toán trong nội bộ module ,không chia sẻ các giá trị này
ra bên ngoài module ) . Nếu trong chương trình chính chúng ta khai báo câu lệnh : print*, in thì
chương trình sẽ báo lỗi .

8.8 Thực hành .


8.8.1 Lập công thức thực nghiệm bằng phương pháp bình phương bé nhất .
Cho hai dãy số thực x , y và chúng ta dự đoán chúng có quan hệ đường thẳng y = ax + b ,
trong đó các hệ số a, b là các ẩn số của hệ hai phương trình sau ( áp dụng phương pháp bình phương
bé nhất ) .

n n n
a  xi2  b xi   xi yi
i 1 i 1 i 1
n n
a  xi  bn   yi
i 1 i 1

Bạn đọc lập các module cần thiết để tính các hệ số a,b .

124
Hướng dẫn : Chúng ta sẽ lập các module sau :
 Module chứa hàm tính định thức cấp 2 .
 Module chứa subroutine nhận biến đầu vào là hai mảng x,y và xuất biến đầu ra là hai hệ
số a,b .Trong module này sẽ thiết lập các hệ số của hệ hai phương trình tuyến tính nêu trên ( sử
dụng các hàm dot_product , sum đã biết ) và giải hệ thống bằng phương pháp định thức .

Chương trình tham khảo :

module DinhThuc
implicit none
contains
function det2(a)
real :: det2
real,dimension(2,2),intent(in) :: a
det2 = a(1,1)*a(2,2) - a(1,2)*a(2,1)
end function det2
end module DinhThuc

module BPBN
use DinhThuc
implicit none
contains
subroutine linear(x,y,k,m)
! Quan he y = kx+m
real,dimension(:),intent(in) :: x,y
real,intent(out) :: k,m
real,dimension(1:2,1:3) :: c ! ma trận mở rộng các hệ số 2 hàng 3 cột .
c(1,1) = dot_product(x,x)
c(1,2) = sum(x)
c(1,3) = dot_product(x,y)
c(2,1) = c(1,2)
c(2,2) = size(x)
c(2,3) = sum(y)
k = det2(c(:,3:2:-1))/ det2(c(:,1:2))
m = det2(c(:,1:3:2))/ det2(c(:,1:2))
end subroutine linear
end module BPBN

program ppbpbn
use BPBN
implicit none Quan he y = ax+b
integer , parameter :: n = 5 a = 0.425 b = 1.175
real,dimension(n) :: x,y
real :: a,b Kết quả .
x = (/-2.,0.,1.,2.,4. /) ! vào dãy số x
y = (/0.5,1.,1.5,2.,3. /) ! vào dãy số y

125
call linear(x,y,a,b)
print*,'Quan he y = ax+b '
print '(a,f6.3,a,f6.3)','a = ',a,' b = ',b
read*
end

Nhận xét : trong module BPBN , kích thước các mảng ( vectơ ) x, y được để tự do dùng ký
hiệu ( : ) ; để lấy kích thước của các mảng thực sự, chúng ta dùng hàm size ( ) .

8.8.2 Ứng dụng tích có hướng để tính diện tích tam giác ABC :
Công thức : s  0.5* AB ^ AC
Bạn đọc sử dụng module TichCoHuong với phép tính .cross. để tính diện tích tam giác ABC
khi biết tọa độ các điểm A,B,C .
Chương trình tham khảo :

module TichCoHuong
implicit none
interface operator (.cross.)
module procedure TCH
end interface
contains
function TCH (a,b)
real,dimension(3),intent(in) :: a,b y
real,dimension(3) :: TCH A
TCH(1) = a(2)*b(3) - a(3)*b(2)
TCH(2) = a(3)*b(1) - a(1)*b(3)
TCH(3) = a(1)*b(2) - a(2)*b(1)
end function TCH B C
end module TichCoHuong x
program main O
use TichCoHuong
implicit none
real,dimension(3) :: a,b,c,z
real :: s
a = (/3.,3.,0. /)
b = (/1.,1.,0. /)
c = (/7.,1.,0. /)
z = (b-a) .cross. (c-a)
s = 0.5*sqrt(dot_product(z,z))
print*,'Dien tich tam giac : ', s ! Dien tich tam giac : 6.
read*
end

8.8.3 Công thức tính trọng tâm .


Cho n chất điểm có tọa độ (xi, yi ) và có trọng lượng pi gắn với chất điểm . Tọa độ
trọng tâm Xg và Yg ( bài toán phẳng ) được tính qua công thức sau :

126
n n

 pi xi py i i
Xg  i 1
n
; Yg  i 1
n

p
i 1
i p
i 1
i

Bạn đọc lập một module chứa subroutine center (x,y,p,xg,yg) tính tọa độ trọng tâm của dãy
chất điểm có tọa độ (xi,yi) có kèm theo trọng lượng pi . Các mảng đầu vào x,y,p có cùng số phần
tử . Tọa độ trọng tâm ( xg,yg) được tính theo công thức nêu trên .
Chương trình tham khảo :

module TrongTam
implicit none
contains
subroutine center(x,y,p,xg,yg)
real,dimension ( : ) ,intent(in) :: x,y,p ! kích thước mảng được để tự do .
real,intent(out) :: xg,yg
xg = (dot_product(p,x))/sum(p)
yg = (dot_product(p,y))/sum(p) ! Công thức tính trọng tâm .
end subroutine center
end module TrongTam
program main
Trong tam Xg Yg : 6.061 12.239
use TrongTam
implicit none
integer , parameter :: n = 10 Kết quả ngẫu nhiên .
real,dimension(n) :: x,y,p,r
real :: xg,yg
call random_seed ! Gọi thủ tục gieo số mầm ( seed value) .
call random_number ( r ) ! Gọi thủ tục tạo dãy số ngẫu nhiên 0.  r < 1.
x = 10.*r ! Dãy x phân bố từ 0.  x < 10.
call random_number ( r )
y = 20.*r ! Dãy y phân bố từ 0.  y < 20.
call random_number ( r )
p = 5.*r ! Dãy p phân bố từ 0.  p < 5.
call center(x,y,p,xg,yg)
print ‘(a,2f10.3)’,”Trong tam Xg Yg :”,xg,yg
read*
end

Trong chương trình trên , để tạo dãy số thực ngẫu nhiên cho các dãy số x, y , p ,dùng tính
toán trong chương trình chính , chúng ta gọi đến hai thủ tục :
random_seed là thủ tục dùng để gieo số mầm ( seed value ) và khởi động bộ tạo số ngẫu
nhiên .
random_number ( r ) là thủ tục tạo dãy số ngẫu nhiên nằm giữa 0.  r < 1.
Các dãy số x,y,p được phân bố ngẫu nhiên trong phạm vi [0,10) , [0,20), [0,5) dựa vào giá
trị của số ngẫu nhiên r nhân với các cận của phạm vi phân bố .
Do sử dụng dữ liệu đầu vào thay đổi nên tọa độ trọng tâm Xg,Yg sẽ khác nhau ở mỗi lần
chạy chương trình .

127
8.8.4 Bạn đọc phân tích và chạy chương trình đổi tọa độ từ tọa độ cực sang tọa độ Descartes ( và
ngược lại ) như sau :
module changecoor
implicit none
contains
subroutine cart_to_polar( x, y, rad, phi )
real, intent(in) :: x, y
real, intent(out) :: rad, phi
rad = sqrt( x ** 2 + y ** 2 )
if ( x /= 0.0 .or. y /= 0.0 ) then
phi = atan2(y,x)
else
phi = 0.0
endif
end subroutine cart_to_polar
subroutine polar_to_cart( rad, phi, x, y )
real, intent(in) :: rad, phi
real, intent(out) :: x, y
x = rad * cos(phi)
y = rad * sin(phi)
end subroutine polar_to_cart
end module changecoor
program doitoado
use changecoor
implicit none
real, parameter :: pi = 3.14159
real :: rad,phi,x,y
rad = 1. ; phi = pi/4.
call polar_to_cart (rad,phi,x,y)
print '(2f7.4)', x,y
x = 1. ; y = 1.
call cart_to_polar ( x,y,rad,phi)
print '(2f7.4)', rad ,phi
read*
end program doitoado

Kết quả :
0.7071 0.7071
1.4142 0.7854

+ Bạn đọc tự viết module chuyển đổi từ tọa độ cầu sang tọa độ Descartes và ngược lại , sử dụng
các đoạn subroutine như sau :

subroutine spherical_to_cart ( rad, phi, theta, x, y, z )


real, intent(in) :: rad, phi, theta
real, intent(out) :: x, y, z
x = rad * cos(phi) * cos(theta)
y = rad * sin(phi) * cos(theta)
z = rad * sin(theta)
end subroutine spherical_to_cart

128
subroutine cart_to_spherical ( x, y, z, rad, phi, theta )
real, intent(in) :: x, y, z
real, intent(out) :: rad, phi, theta
rad = sqrt ( x ** 2 + y ** 2 + z ** 2 )
theta = asin(z/rad)
if ( x /= 0.0 .or. y /= 0.0 ) then
phi = atan2(y,x)
else
phi = 0.0
endif
end subroutine cart_to_spherical

*****

129
CHƯƠNG 9 : KHÁI NIỆM VỀ CON TRỎ ( POINTER )

9.1 Khái niệm chung .


Trong Fortran , biến con trỏ ( pointer ) được khai báo kèm theo thuộc tính pointer , có khả
năng trỏ đến địa chỉ trong bộ nhớ của một đối tượng mục tiêu . Đối tượng được trỏ đến có thể là
một biến , một mảng , một dữ liệu kiểu hỗn hợp ( bản ghi ) … và được gán thuộc tính target . Biến
pointer và biến target liên kết phải được khai báo có cùng kiểu dữ liệu , cùng số kind và cùng số
chiều ( rank ) . Sử dụng chỉ một con trỏ , chúng ta có thể tác động lên những đối tượng mục tiêu
khác nhau trong chương trình .
Con trỏ có thể có các tình trạng sau : được liên kết ( associated ) ; không được liên kết (
disassociated ) ; không xác định ( undefined ) .
Nếu p là biến con trỏ và t là biến mục tiêu thì để chỉ thị cho con trỏ p trỏ đến biến t, chúng
ta viết như sau : p => t , lúc này p và t cùng có một giá trị chung .
Trong tài liệu này, chúng ta không đi sâu vào việc khai thác biến con trỏ trong lập trình .
Chúng ta tìm hiểu về con trỏ qua các ví dụ đơn giản ở phần sau .

9.2 Các ví dụ .


+ Ví dụ 01:
1 program testpointer1
2 implicit none
3 integer, pointer :: a => null() , b => null()
4 integer, target :: c
5 integer :: d
6c=5
7 a => c
8 print*,'a : ',a !a: 5
10 c = 10
11 b => c
12 print*,'a : ',a,' b : ',b ! a : 10 b : 10
13 d = a + b
14 print*,' a b c d : ',a,b,c,d ! ‘ a b c d : ‘ 10 10 10 20
15 read*
16 end

Giải thích :
+ Dòng số 3 : khai báo hai biến integer a , b được gán thuộc tính pointer là các con trỏ . Hai
biến này có thể trỏ đến các biến mục tiêu là số nguyên . Các chỉ thị a => null( ) , b => null ( ) đặt
tình trạng ban đầu cho các con trỏ không trỏ đến bất kỳ đối tượng nào .
+ Dòng số 4 : khai báo biến integer c được gán thuộc tính target .
+ Dòng số 7 : a => c , con trỏ a trỏ đến biến mục tiêu c , cả hai biến a và c cùng có một giá
trị là số 5 . Con trỏ a lúc này có thể được truy xuất như một biến nguyên thông thường .
+ Dòng 8 : in ra giá trị hiện thời của a là số 5 .
+ Dòng 10 : biến mục tiêu c được gán giá trị mới c = 10 .
+ Dòng 11 : b => c , con trỏ b trỏ đến biến c , lúc này cả hai biến con trỏ a, b và biến mục
tiêu c cùng có giá trị giống nhau là số 10 .
+ Dòng 12 : in ra giá trị hiện thời của a và b đều là số 10 .
+ Dòng 13 : d = a + b , hai con trỏ a , b đang trỏ đến biến c, hoạt động như hai biến nguyên
thông thường . Biến d sẽ có giá trị 10 + 10 = 20 .
+ Dòng 14 : in ra các giá trị a,b,c,d .
130
+ Ví dụ 02 : Fortran cung cấp hàm định nghĩa sẵn associated (biến_con_trỏ) có kiểu logical
dùng để test xem con trỏ đã được liên kết với đối tượng nào chưa ? . Chúng ta xem ví dụ sau :

program testpointer2
implicit none
integer, pointer :: a => null() , b => null()
integer, target :: c
integer :: d
print*,associated (a)
print*,associated (b) F
c=5 F
a => c a b c d : 10 10 10 20
c = 10 T
b => c T
d=a+b
print*,' a b c d : ',a,b,c,d Kết quả .
print*,associated (a)
print*,associated (b)
read*
end

Lúc đầu , các con trỏ a, b ở tình trạng null , không liên kết đến bất cứ đối tượng mục tiêu
nào nên các hàm associated ( ) trả về giá trị .false.
Sau khi các con trỏ a, b trỏ đến biến c , các hàm associated ( ) trả về giá trị .true.
Lưu ý : khi biến con trỏ a ở tình trạng null , a => null ( ) , nếu chúng ta cố tình truy xuất biến
a ( ví dụ bằng lệnh print*, a ) thì trình biên dịch sẽ báo lỗi .
Hàm nullify (biến_con_trỏ) có tác dụng đưa con trỏ về tình trạng null .

+ Ví dụ 03 : chúng ta có thể cấp phát vùng nhớ cho biến con trỏ a bằng lệnh allocate (a) ,
sau đó có thể sử dụng biến a trong các câu lệnh thông thường , lệnh deallocate (a ) phóng thích
vùng nhớ đã cấp phát cho biến a .

program testpointer3
implicit none
integer, pointer :: a => null() , b => null()
integer, target :: c
integer :: d
allocate (a) ! cấp phát vùng nhớ cho biến con trỏ a
a=5 ! sử dụng a như một biến nguyên bình thường .
c = 10
b => c
d=a+b
print*,' a b c d : ',a,b,c,d
deallocate (a) ! phóng thích vùng nhớ cho biến a .
read*
end

Kết quả :
a b c d : 5 10 10 15

131
Lưu ý : khi khai báo các biến con trỏ , chúng ta cần đặt các biến này vào tình trạng null và
tiến hành cấp phát ( allocate ) vùng nhớ một cách tường minh trước khi xử lý các biến này như
các biến thông thường . Lệnh deallocate ( ) thu hồi các vùng nhớ cấp phát cho con trỏ .
Chúng ta xem ví dụ sau :
program testpointer32
integer , pointer :: a => null () , b => null ()
integer, target :: c
integer :: d
allocate (a)
allocate (b)
a = 100
b = 200
print *,a,b ! kết quả in ra 100 và 200
c=1
a => c
c=2
b => c
d=a+b
print*,a,b,c,d ! in ra 2 2 2 4
read*
end

Nhận xét : Khi các biến con trỏ a, b trỏ đến biến mục tiêu c , các biến a, b này từ bỏ các giá
trị 100 , 200 khi chúng được gán ở phần đầu chương trình .

+ Ví dụ 04 : Bạn đọc phân tích chương trình dưới đây có con trỏ list là mảng ( một chiều )
số thực và các biến mục tiêu là các mảng số thực được cấp phát động .

program testpointer4
implicit none
real,dimension ( : ), pointer :: list ! mảng con trỏ kiểu số thực .
real,dimension ( : ), allocatable,target :: list1,list2 ! mảng mục tiêu được cấp phát động .
allocate ( list1 (1:100) , list2 (1:200) )
list => list1 ; print*,size (list) 100
list => list2 ; print*,size (list) 200
if (associated(list)) then Con tro da duoc lien ket .
print*,'Con tro da duoc lien ket .' Con tro chua duoc lien ket .
else
print*,'Con tro chua duoc lien ket .'
end if
nullify (list) ! tương đương list => null ( )
deallocate (list1,list2) ! phóng thích vùng nhớ .
if (associated (list)) then
print*,'Con tro da duoc lien ket .'
else
print*,'Con tro chua duoc lien ket .'
end if
read*
end

132
+ Ví dụ 05 : Bạn đọc phân tích chương trình sau và có nhận xét về các phép tính , phép gán
có toán hạng là các con trỏ :
program testpointer5
implicit none
integer,target :: i = 2,j = 3
integer,pointer :: pi,pj,pk
pi => i
pj => j
pk => i
print*,'i j pi pj pk : ',i,j,pi,pj,pk ! i j pi pj pk : 2 3 2 3 2
pi = 4.6 ! pi nhận giá trị int(4.6) cùng với i là 4 .
pj = pi + 10 ! pj nhận giá trị 14 cùng với j .
pk => j ! pk trỏ đến j nhận giá trị 14
print*,'i j pi pj pk : ',i,j,pi,pj,pk ! i j pi pj pk : 4 14 4 14 14
pi => pj ! pi trỏ đến pj , pj => j , pi trỏ đến số 14
print*,'pi pj pk : ',pi,pj,pk ! pi pj pk : 14 14 14
read*
end

+ Ví dụ 06 : Một số trình biên dịch Fortran cung cấp hàm loc ( biến ) dùng để in ra địa chỉ (
trong bộ nhớ ) của các biến . Chúng ta xem ví dụ sau :
program testloc
integer , pointer :: a => null () , b => null ()
integer, target :: c
integer :: d
allocate (a)
allocate (b) 100 200
a = 100 3150048
b = 200 3150088
print *,a,b 2293452
print*,loc (a) 2293456
print*,loc (b)
2224
print*,loc (c) 2293452
print*,loc (d) 2293452 a,b,c có cùng một địa chỉ ( của biến c).
c=1 2293452
a => c 2293456
c=2
b => c Kết quả chương trình testloc .
d=a+b
print *
print*,a,b,c,d
print*,loc (a)
print*,loc (b)
print*,loc (c)
print*,loc (d)
read*
end

*****

133
PHỤ LỤC A : SỬ DỤNG GNUPLOT ĐỂ VẼ ĐỒ THỊ 2D / 3D

1) Giới thiệu phần mềm gnuplot .


Kết quả tính toán từ một chương trình Fortran có thể là một quan hệ hàm x  y = f(x) hoặc
quan hệ hàm hai biến (x,y)  z = f(x,y) được trình bày theo dạng cột trong một file có tên , ví
dụ :

# dataout1.dat # dataout2.dat # dataout3.dat


# y = f(x) # y1= f(x) ; y2 = g(x) # z = f(x,y)
#x y #x y1 y2 #x y z
1. 1. 1. 1. 1. 1. 1. 2.
2. 1.414 2. 1.414 4. 2. 2. 8.
… … … … … … … …

Để có thể vẽ được các đồ thị thể hiện các mối quan hệ này , chúng ta có thể sử dụng các thư
viện ( các module ) đồ họa của các trình biên dịch Fortran ( thường là phần mềm có bản quyền )
hoặc một giải pháp đơn giản là dùng phần mềm đồ họa miễn phí gnuplot để thể hiện các đồ thị
2D/3D với các định dạng khác nhau . Chúng ta có thể download phần mềm này tại địa chỉ như sau
:
https://sourceforge.net/projects/gnuplot/files/latest/download

File dùng cài đặt : gp522-win64-mingw.exe ( 25,2 MB ) ; chúng ta chạy file này và chọn
thư mục chứa chương trình ( gnuplot có thể có các phiên bản mới hơn phiên bản nói trên ) .
Sau khi cài đặt xong , để khởi động chương trình gnuplot , chúng ta nhấn kép lên icon của
gnuplot hoặc vào thư mục gnuplot/bin/ chạy file wgnuplot.exe

Icon của Gnuplot .

Gnuplot hoạt động theo hai kiểu :


+ Kiểu tương tác : người sử dụng nhập lệnh theo từng dòng lệnh theo đúng cú pháp của
gnuplot và nhấn <Enter> sau mỗi câu lệnh , gnuplot sẽ đáp ứng theo yêu cầu . Nếu lệnh nhập sai ,
gnuplot sẽ báo lỗi.
+ Kiểu sử dụng tập tin kịch bản (script file) : người sử dụng soạn sẵn các câu lệnh theo cú
pháp của gnuplot trong một tập tin dạng text ( có thể dùng Notepad ) , xong tại dấu nhắc của
gnuplot , nạp ( load ) nội dung tập tin vào chương trình để thực thi .
+ Để thoát khỏi gnuplot , vào lệnh exit hoặc quit xong nhấn <Enter> .

Hai lệnh vẽ cơ bản là :


 plot : dùng để vẽ các đồ thị 2D .
 splot : dùng để vẽ các đồ thị ( mặt ) 3D .

134
Tập lệnh của gnuplot rất phong phú , cung cấp rất nhiều kiểu đồ thị với các định dạng
khác nhau , ở đây chúng ta chỉ thực hành những nội dung cơ bản nhất .

2) Cửa sổ gnuplot :

Cửa sổ gnuplot sau khi khởi động .


3. Một số ví dụ .
3.1 Tại dấu nhắc lệnh gnuplot> _ ,chúng ta vào dòng lệnh như sau và <Enter> :
plot [0:2*pi] sin(x)

Hàm sin(x)

Dòng lệnh trên ra lệnh vẽ đồ thị hàm sin(x) với phạm vi thay đổi của đối số hình thức x từ
0 đến 2 .Cách viết các hàm , phép tính , hằng số  trong gnuplot tương tự như trong văn bản
Fortran . Lưu ý : Gnuplot có phân biệt x và X là khác nhau .
3.2 Giả sử chúng ta muốn vẽ hàm sin2x , với -2  x  2 :
Nhấn phím mũi tên lên  ( up arrow key ) trên bàn phím để lấy lại câu lệnh vừa mới vào ,
dùng các phím mũi tên phải, trái di chuyển con trỏ và sửa ( edit) lại nội dung như sau :
plot [-2*pi :2*pi] sin (2*x) xong <Enter>
135
Các phím mũi tên lên, xuống cho phép lấy lại các dòng lệnh đã vào , giúp tiết kiệm thời
gian vào lệnh .

Hàm sin (2x)


3.3 Chúng ta sẽ vẽ đồ thị hàm y  x 2  1 với phạm vi x  [-4 : 4] và y  [0 : 6] . dùng
lệnh set để đặt tiêu đề của đồ thị , nhãn của trục x , y .
gnuplot> set title 'Do thi y = sqrt(x*x+1)’ # sau một lệnh nhấn Enter .
gnuplot> set xlabel 'X'
gnuplot> set ylabel 'Y'
gnuplot> plot [-4:4] [0:6] sqrt(x*x +1)  xuất hình vẽ ra màn hình .
gnuplot>_
Ý nghĩa : set title ‘ ‘ : đặt tiêu đề cho đồ thị ; set xlabel ‘ ‘ : đặt nhãn cho trục x ; set
ylabel ‘ ‘ : đặt nhãn cho trục y . Xâu kí tự được đặt trong cặp dấu nháy ‘ ‘ . Sau lệnh vẽ plot là
các phạm vi thay đổi của biến x , biến y và biểu thức số học sqrt (x*x +1) .
Nếu cần chỉnh sửa lại nội dung của mục nào chúng ta nhấn phím  để lấy lại dòng lệnh cần
chỉnh sửa . Chúng ta cũng có thể bổ sung tiếp vào các lệnh đã có ,ví dụ ra lệnh vẽ ô lưới bằng lệnh
set grid .Sửa xong , vào lệnh replot ( vẽ lại ) thì các nội dung cập nhật sẽ thể hiện lên trên đồ thị .

136
Để đặt lại phạm vi thay đổi của x hoặc y , dùng các lệnh sau :
set xrange [x1: x2] hoặc set yrange [y1:y2]
Lệnh replot vẽ lại đồ thị hiện hành với tất cả các lệnh , nội dung vừa mới cập nhật .

3.4 Để xuất một đồ thị ra một file ảnh , ví dụ file có phần đuôi là *.jpeg , thực hiện như
sau :
gnuplot> set title 'Do thi y = sqrt(x*x+1) ‘
gnuplot> set xlabel 'X'
gnuplot> set ylabel 'Y'
gnuplot> set grid
gnuplot> plot [-4:4] [0:6] sqrt(x*x +1)  xuất đồ thị ra màn hình để xem trước.
gnuplot> set terminal png ( chuyển hướng xuất đồ thị .)
gnuplot> set output ‘d:\work\dothi01.jpeg’ ( đặt tên file ảnh và đường dẫn)
gnuplot> replot ( xuất đồ thị ra tập tin ảnh )
gnuplot> set terminal windows ( trở về terminal windows , lệnh plot sẽ xuất kết quả ra
màn hình )

Lưu ý : terminal mặc định là windows ( xuất đồ thị ra màn hình máy tính ), ngoài ra có thể
set terminal postscript với file output có đuôi là *.ps . dãy lệnh như sau :
set terminal postscript
set output ‘d:\work\dothi02.ps’
replot

3.5 Xuất nhiều đồ thị trên cùng một cửa sổ .
Gnuplot cho phép vẽ nhiều đồ thị trên cùng một cửa sổ . Ví dụ :
gnuplot> set title 'Do thi ba ham : sinx, cosx, xsinx'
gnuplot> set grid
gnuplot> plot [-2*pi:2*pi] sin(x),cos(x),x*sin(x)
Lưu ý : các hàm viết cách nhau dấu phẩy .

137
4. Vẽ đồ thị từ kết quả được sắp xếp theo dạng cột trong một tập tin.
Chúng ta có file ‘d:\dataplot.dat’ từ chương trình Fortran sau :
program main
implicit none
real,dimension(10) :: x,y1,y2,y3
integer :: i
do i = 1,10
x(i) = real(i)
end do
y1 = x*x ! y1 = x2
y2 = x*x +3.*x ! y2 = x2 + 3x
y3 = sqrt(x*x +x ) ! y3 = x 2  x
open (10,file = 'd:\dataplot.dat')
do i = 1,10
write (10,'(4f10.2)') x(i),y1(i),y2(i),y3(i) ! ghi vào file d:\dataplot.dat
write (*,'(4f10.2)') x(i),y1(i),y2(i),y3(i) ! xuất ra màn hình để kiểm tra
end do
close(10)
read*
end

Ghi chú : các cột 1,2,3,4 trong file kết quả tương ứng với dãy số x,y1,y2,y3.

+ Để vẽ đồ thị x  y1(x) bằng cách nối các điểm (x,y1(x)) , dùng lệnh sau :
set grid
set title ‘ Quan he y1=x**2 ‘
plot ‘d:\dataplot.dat ‘ using 1:2 with lines

Nhận xét : sau lệnh plot là tên file trong dấu ngoặc đơn ( kể cả đường dẫn ) ; cụm từ using
1:2 cho biết đồ thị dùng cột 1 (dãy x) và cột 2 ( dãy y1) ; cụm từ with lines cho biết đồ thị dùng
các đoạn thẳng để nối các điểm (x,y) lại với nhau .

138
Tương tự chúng ta có đồ thị thể hiện mối quan hệ giữa cột 1 và cột 3 :
set grid
set title ‘ Quan he y2=x**2 + 3*x ‘
set ylabel ‘y2’
plot ‘d:\dataplot.dat ‘ using 1:3 with linespoints

Ở đồ thị này , cụm từ with linespoints vừa dùng đoạn thẳng để nối , vửa có ghi dấu điểm
tại các tọa độ trong tập tin .
Đồ thị dưới đây vẽ cả 3 hàm trên cùng một cửa sổ , dùng mẫu lệnh :
plot ‘tên_file’ using 1:3 with linespoints , ‘tên_file’ using 1:2 with lines , \
‘tên_file’ using 1:4 with linespoints < Enter >

139
Lưu ý : Trong trường hợp câu lệnh dài cần phải viết ở dòng tiếp theo , chúng ta đặt dấu \
ở cuối câu xong <Enter> và viết tiếp câu lệnh .

5. Soạn và chạy tập tin script .


Trong Notepad chúng ta soạn tập tin như sau và lưu lại với tên d:\dothi001.txt
# Tap tin d:\dothi001.txt
# Du lieu lay tu file d:\dataplot.dat
# Ket qua xuat ra man hinh va file anh d:\y2y4.jpeg
#
set title 'Quan he y2(x), y4(x)'
set ylabel 'y2 & y4'
set xlabel 'Thoi gian'
set grid
plot 'd:\dataplot.dat' using 1:2 with linespoints ,\
'd:\dataplot.dat' using 1:4 with linespoints
set terminal png
set output 'd:\y2y4.jpeg'
replot
set terminal windows

Ghi chú : dòng có dấu # ở đầu là dòng ghi chú , gnuplot sẽ bỏ qua các dòng này .
Các lệnh đã trình bày ở các phần trước được nhóm lại thành một tập lệnh ( file dạng text )
và đặt vào một tập tin có tên d:\dothi001.txt .
Để thi hành tập lệnh này , tại dấu nhắc của gnuplot , đánh lệnh sau :
gnuplot> load ‘d:\dothi001.txt’ <Enter>
Lúc này toàn bộ các lệnh của tập tin được thực thi lần lượt giống như chúng ta vào lệnh ở
dòng command line trong chế độ tương tác . Nếu câu lệnh có lỗi, gnuplot sẽ đưa ra câu thông báo.
Kết quả sẽ có một đồ thị trên màn hình và một file ảnh y2y4.jpeg trên ổ đĩa D .

Cú pháp nạp một tập tin vào gnuplot : > load ‘<tên tập tin >’ [Enter]

140
Trong chương trình Fortran ở mục 4 , chúng ta có thể gọi gnuplot và nạp tập tin
dothi001.txt , dồ thị sẽ hiển thi khi chạy chương trình :
…..
open (10,file = 'd:\dataplot.dat')
do i = 1,10
write (10,'(4f10.2)') x(i),y1(i),y2(i),y3(i) ! ghi dữ liệu vào tập tin ở dạng cột.
write (*,'(4f10.2)') x(i),y1(i),y2(i),y3(i) ! ghi ra màn hình
end do
close(10)
call system ('d:\gnuplot\bin\wgnuplot d:\dothi001.txt')
read*
end
Dùng thủ tục call system với đối số là tên tập tin chương trình wgnuplot cùng đường dẫn
và tập tin script d:\dothi001.txt chứa tập lệnh dùng để vẽ đồ thị . Lưu ý chúng ta cần phải đặt lệnh
pause 10 ( dừng lại 10 giây ) ngay sau lệnh plot trong file script này để có đủ thời gian quan sát
đồ thị hoặc lệnh pause -1 , lúc này sau khi quan sát xong , chúng ta nhấn <Enter> để chương trình
tiếp tục .
# Tap tin d:\dothi001.txt
# Du lieu laytu file d:\dataplot.dat
# Ket qua xuat ra file anh d:\func_y2y4.jpeg
set title 'Quan he y2(x), y4(x)'
set ylabel 'y2 & y4'
set xlabel 'Thoi gian'
set grid
plot 'd:\dataplot.dat' using 1:2 with linespoints ,\
'd:\dataplot.dat' using 1:4 with linespoints
# lenh pause 10 : dung 10 giay de xem hinh hoac dung lenh pause -1
pause 10
# Tao file anh jpeg de luu do thi
set terminal png
set output 'd:\func_y2y4.jpeg'
replot
# Tro ve terminal windows
set terminal windows

6. Vẽ đồ thị 3D z = f(x,y) bằng lệnh splot .


+Để vẽ đồ thị 3D thể hiện quan hệ z = f(x,y) , dùng lệnh :
splot <biểu thức hàm hai biến x,y >
Ví dụ : z = -x3 –y  Lệnh vẽ : splot (-x**3 – y)
+ Dùng lệnh set xrange [x1:x2] , set yrange [y1:y2] , set zrange [z1:z2] để thay đổi phạm
vi của x,y,z theo yêu cầu . Ví dụ :
set grid
set hidden3d ( dấu các đường khuất để dễ quan sát )
splot [-2: 2] [ -2:2 ] 2*(x**2 + y**2)*exp(-x**2 – y**2)
Để tăng độ phân giải của đồ thì 3D nói trên , vào lệnh :
set isosamples 40,40
replot

141
z = -x3-y

z = 2*(x**2 + y**2)*exp(-x**2 – y**2)

Đồ thị với độ phân giải 40,40

142
7. Vẽ mặt thể hiện mảng hai chiều .
+ Để vẽ mặt thể hiện mảng 2 chiều f(i,j) , với i,j : 1..n , ví dụ hàm z = x2 + y2 với x,y lấy
các giá trị 1,2,3,4 , chúng ta sắp xếp file dữ liệu như sau , lưu ý sau khi ghi các giá trị của cột thứ
1 của ma trận f (i,j) , chúng ta phải để một dòng trống .

# z = x**2 + y **2
# d:examsp.dat
1 1 2
2 1 5
3 1 10
4 1 17
! Dòng này phải để trống.
1 2 5
2 2 8
3 2 13
4 2 20
! Dòng này phải để trống.

1 3 10
2 3 13
3 3 18
4 3 25
! Dòng này phải để trống.

1 4 17
2 4 20
3 4 25
4 4 32

Các câu lệnh trong gnuplot :


gnuplot > set surface
gnuplot > set hidden3d
gnuplot > set grid
gnuplot > splot ‘d:examsp.dat’ u 1:2:3 w lines

Ghi chú : gnuplot cho phép viết tắt trong câu lệnh , ví dụ u thay cho using , w thay cho with .

143
8. Vẽ véc tơ trong mặt phẳng 2D .
Để vẽ một trường véc tơ , tập tin dữ liệu phải bố trí số liệu thành 04 cột : cột 1 và 2 cho
biết tọa độ điểm gốc của véc tơ , cột 3 và 4 cho biết thành phần số ( tọa độ ) của véc tơ theo
phương X và phương Y theo định dạng như sau :

Tọa độ x Tọa độ y Thành phần số X Thành phần số Y

Lệnh vẽ các véc tơ trong tập tin ‘d:\vevecto.dat’ như sau :
plot ‘d:\vevecto.dat’ using 1:2:3:4 with vectors head filled lt 3 lw 3

Vẽ vectơ .

Ngoài các đồ thị vẽ theo hệ tọa độ Descartes thông thường , gnuplot còn cho phép vẽ các
đường cong tham số , đường cong trong hệ tọa độ cực , biểu diễn số liệu có kèm theo sai số , biểu
diễn số liệu theo dạng thống kê, tạo ảnh động ( animation ) ... Bạn đọc quan tâm chi tiết , có thể
download các tài liệu mới nhất về gnuplot trên internet để nghiên cứu , tham khảo thêm .

*****

144
PHỤ LỤC B : CHƯƠNG TRÌNH LINPACK GIẢI HỆ n
PHƯƠNG TRÌNH TUYẾN TÍNH Ax = b BẰNG PP KHỬ GAUSS

1. Văn bản chương trình :


Chương trình có hai module : module kinds định nghĩa hằng r dùng để khai báo số thực có độ
chính xác kép và module ppgauss có chứa subroutine gauss dùng để giải hệ phương trình .
Trong chương trình chính , các ma trận A , vectơ b , vectơ nghiệm số x được khai báo là các mảng
được cấp phát động . Các dữ liệu đầu vào :
_ Khai báo số phương trình n .
_ Khai báo các hệ số của ma trận A , dữ liệu được vào theo từng dòng .
_ Khai báo các phần tử của vectơ b , vế bên phải
_ Trường hợp ma trận A suy biến ( singular) , chương trình sẽ có thông báo .

Văn bản chương trình :

module kinds
implicit none
integer,parameter :: r = selected_real_kind (15,307)
end module kinds
module ppgauss
implicit none
contains
subroutine gauss(a,n,b,x,suybien)
use kinds
implicit none
!Khai bao cac doi so (arguments )
integer,intent(in) :: n
real(r),intent(inout) :: a(:,:),b(:)
real(r),intent(out) :: x(:)
logical,intent(out) :: suybien
!Khai bao bien cuc bo
integer :: i,j,k,pivot_row
real(r):: pivot,sum,trunggian
real(r),parameter :: eps = 1.e-13_r
! Xu ly theo cot
do k = 1,n-1
! Tim dong co phan tu lon nhat cua cot k de pivot
pivot_row = maxval(maxloc(abs(a(k:n,k)))) + k - 1
! Test xem ma tran a co suy bien
! Neu co thi tro lai main program
if (abs(a(pivot_row,k)) <= eps) then
suybien = .true.
return
else
suybien = .false.
end if
! Doi cho phan tu o cot k neu phan tu
! lon nhat khong tren duong cheo chinh
if (pivot_row /= k) then
trunggian = a(pivot_row,k)
a(pivot_row,k) = a(k,k)
a(k,k) = trunggian
145
trunggian = b(pivot_row)
b(pivot_row) = b(k)
b(k) = trunggian
end if
! Xu ly cac phan tu duoi duong cheo
a(k+1:n,k) = a(k+1:n,k) /a(k,k)
! Khu theo hang duoc thuc hien theo cot
do j = k+1,n
pivot = a(pivot_row,j)
if (pivot_row /= k) then
a(pivot_row,j) = a(k,j)
a(k,j) = pivot
end if
a(k+1:n,j) = a(k+1:n,j) - pivot*a(k+1:n,k)
end do
b(k+1:n) = b(k+1:n) - a(k+1:n,k)*b(k)
end do
! Tinh nghiem : thay the theo chieu nguoc
do i = n,1,-1
sum = 0.0
do j = i + 1,n
sum = sum + a(i,j)*x(j)
end do
x(i) = (b(i)-sum)/a(i,i)
end do
end subroutine gauss
end module ppgauss
program main
use kinds
use ppgauss
implicit none
integer :: i,n
real(r),allocatable :: a(:,:), b(:),x(:)
logical :: suybien
print*,'Cho biet so phuong trinh : '
read*,n
allocate (a(1:n,1:n),b(1:n),x(1:n))
do i = 1,n
print*,'Vao phan tu dong so ',i,' cua ma tran A :'
read*,a(i,1:n)
print*,'Vao phan tu so ',i,' cua vecto b :'
read*,b(i)
end do
call gauss(a,n,b,x,suybien)
if (suybien) then
print*,'Matran a suy bien.'
else
print*,'Nghiem so x : '
print*,x(1:n) ! Xuat ket qua n nghiem so
end if
read*
end program main
146
2. Chạy test chương trình .

Kết quả chạy test :

Cho biet so phuong trinh :


3
Vao phan tu dong so 1 cua ma tran A :
2. 3. 5.
2x1 + 3x2 + 5x3 = 2
Vao phan tu so 1 cua vecto b :
3x1 -2.5x2 + 4x3 = 10
2.
-4x1 + 3x2 + 2x3 = 2
Vao phan tu dong so 2 cua ma tran A :
3. -2.5 4.
Vao phan tu so 2 cua vecto b :
10.
Vao phan tu dong so 3 cua ma tran A :
-4. 3. 2.
Vao phan tu so 3 cua vecto b :
2.
Nghiem so x :
-1. -2. 2.

*****

147
TÀI LIỆU THAM KHẢO
1.Alexandre Mayer . Cours de programmation Fortran 90. Université de Namur - FUNDP ,
Bruxelles – Belgique .

2. Jacques Lefrere . Introduction au Fortran 90/95/2003/2008 . Université Pierre et Marie Curie


Paris VI . 2016 .

3. Ian Chivers – Jane Sleightholme . Introduction to programming with Fortran , with coverage of
Fortran 90,95,2003,2008 and 77 . Springer .2012 .

4. Hans Lee – Paul Munsell .The design and implementation of programs in FORTRAN 77 .
Prentice Hall International Editions. 1990.

5. Võ Văn Hoàng . Ngôn ngữ lập trình Fortran .Nhà xuất bản Giáo dục . 2007.

6. Thomas Williams – Colin Kelley . Gnuplot 5.0 : An interactive Plotting Program . 2017 .

7. College of Natural Sciences Computing Laboratories – University of Northern Iowa .


Introduction to GnuPlot .2017.

*****

148
MỤC LỤC

LỜI NÓI ĐẦU .


CHƯƠNG 1 : GIỚI THIỆU CHUNG VỀ NGÔN NGỮ LẬP TRÌNH FORTRAN
1.1 Sơ lược về lịch sử ngôn ngữ lập trình Fortran .
1.1.1 Nguồn gốc.
1.1.2 Phạm vi ứng dụng .
1.1.3 Các phiên bản của ngôn ngữ Fortran .
1.2 Bộ kí tự của Fortran. Văn bản chương trình Fortran.
1.2.1 Bộ kí tự của Fortran .
1.2.2 Soạn thảo văn bản chương trình .
1.3 Phương thức chung tạo tập tin thi hành từ văn bản chương trình .
1.4 Một ví dụ về chương trình Fortran.
1.4.1 Văn bản chương trình tính chu vi và diện tích hình tròn .
1.4.2 Biên dịch, liên kết và chạy chương trình hinhtron.f90
1.5 Giải thích sơ lược văn bản chương trình hinhtron.f90 .
1.5.1 Cấu trúc tổng quát chương trình chính .
1.5.2 Câu lệnh implicit none.
1.5.3 Khai báo biến .
1.5.4 Khai báo hằng.
1.5.5 Viết dữ liệu ra màn hình .
1.5.6 Nhập dữ liệu từ bàn phím .
1.5.7 Câu lệnh gán .
1.6 Thực hành .
1.6.1 Phân tích câu lệnh .
1.6.2 Thực hành biên dịch, liên kết và chạy chương trình trong môi trường Force .
1.7 Trình biên dịch Fortran G95 ( phần tham khảo ).
1.7.1 Cài đặt.
1.7.2 Sử dụng G95.
1.8 Trình biên dịch Silverfrost FTN95 Personal Edition (phần tham khảo ) .
1.9 Văn bản chương trình Fortran 90 viết theo kiểu định dạng cố định ( tham khảo ).

CHƯƠNG 2 : CÁC KIỂU DỮ LIỆU CỦA FORTRAN


2.1 Các kiểu dữ liệu của Fortran .
2.1.1 Khái niệm .
2.1.2 Khai báo biến và hằng .
2.2 Dữ liệu kiểu số nguyên (integer)
2.2.1 Tổng quát về kiểu số nguyên .
2.2.2 Phân loại và khai báo các kiểu số nguyên .
2.2.3 Ví dụ .
2.3 Dữ liệu kiểu số thực (real)
2.3.1 Tổng quát về kiểu số thực .
2.3.2 Kiểu số thực có độ chính xác đơn .
2.3.3 Kiểu số thực có độ chính xác kép .
2.3.4 Khai báo kiểu số thực có kèm tham số kind .
2.3.5 Ví dụ .
2.4 Dữ liệu kiểu số phức (complex)
2.4.1 Tổng quát về kiểu số phức.
2.4.2 Khai báo kiểu số phức .
2.5 Dữ liệu kiểu logic ( logical ) .
2.6 Dữ liệu kiểu kí tự ( character ) .
2.6.1 Tổng quát về kiểu kí tự .
149
2.6.2 Khai báo kiểu kí tự .
2.7 Dữ liệu kiểu hỗn hợp .
2.8 Một số nội dung bổ sung .
2.8.1 Cách khai báo kiểu dữ liệu theo phiên bản FORTRAN 77 .
2.8.2 Một số hàm truy vấn dữ liệu kiểu số thực, số nguyên .
2.8.3 Mẫu đầy đủ khai báo biến có kiểu số học, logical , xâu kí tự .
2.9 Thực hành .

CHƯƠNG 3 : PHÉP TOÁN VÀ BIỂU THỨC


3.1 Mở đầu .
3.2 Các phép toán số học và biểu thức số học .
3.2.1 Các phép toán số học .
3.2.2 Quy tắc ưu tiên của các phép toán số học .
3.2.3 Quy tắc chuyển đổi kiểu dữ liệu trong biểu thức số học và trong phép gán.
3.3 Biểu thức logic . Các phép toán so sánh (quan hệ). Các phép toán logic.
3.3.1 Các phép toán so sánh .
3.3.2 Các phép toán logic .
3.4 Biểu thức kí tự. Phép nối xâu kí tự. Phép so sánh .
3.5 Bảng tổng hợp các phép toán và mức độ ưu tiên trong Fortran .
3.6 Một số hàm thông dụng có sẵn trong Fortran .
3.7 Thực hành .

CHƯƠNG 4 : CÁC CẤU TRÚC ĐIỀU KHIỂN


4.1 Cấu trúc lựa chọn với câu lệnh if .
4.1.1 Lệnh if dạng đơn giản .
4.1.2 Cấu trúc lựa chọn dạng if then else .
4.1.3 Cấu trúc if tổng quát .
4.2 Cấu trúc lựa chọn select case .
4.3 Cấu trúc vòng lặp do .
4.3.1 Cấu trúc vòng lặp xác định .
4.3.2 Cấu trúc vòng lặp không xác định .
4.4 Lệnh goto .
4.5 Lệnh stop .
4.6 Thực hành .

CHƯƠNG 5 : CÁC LỆNH NHẬP / XUẤT VÀ ĐỊNH DẠNG DỮ LIỆU .
5.1 Các lệnh xuất dữ liệu ra màn hình .
5.2 Các lệnh nhập dữ liệu từ bàn phím .
5.3 Cách viết phần định dạng trong các lệnh xuất / nhập dữ liệu .
5.3.1 Định dạng cho số nguyên
5.3.2 Định dạng cho số thực dạng dấu chấm tĩnh .
5.3.3 Định dạng cho số thực dạng bậc .
5.3.4 Định dạng cho giá trị kiểu logical .
5.3.5 Định dạng cho giá trị kiểu xâu kí tự .
5.3.6 Các nội dung định dạng khác .
5.4 Ghi / đọc dữ liệu sử dụng tập tin ( file ).
5.4.1 Giới thiệu chung về tập tin .
5.4.2 Ví dụ về dữ liệu dạng tập tin .
5.4.3 Quản lý các lỗi khi mở tập tin .
5.4.4 Các tham số đi kèm với lệnh mở tập tin open .
5.4.5 Đọc dữ liệu từ tập tin .
5.4.6 Ghi dữ liệu ra tập tin .
150
5.4.7 Lệnh rewind .
5.4.8 Lệnh đóng tập tin .
5.4.9 Khái niệm về tập tin trong (internal file ) .
5.4.10 Tập tin không được định dạng ( unformatted file )
5.4.11 Tập tin truy cập trực tiếp .
5.4.12 Sử dụng namelist để chứa dữ liệu .
5.4.13 Sử dụng lệnh inquire để truy vấn thông tin .
5.5 Thực hành .

CHƯƠNG 6 : CẤU TRÚC DỮ LIỆU KIỂU MẢNG ( ARRAY )


6.1 Một số khái niệm .
6.2 Khai báo mảng theo dạng tĩnh .
6.2.1 Khai báo mảng một chiều .
6.2.2 Khai báo mảng hai chiều .
6.3 Thứ tự vị trí các phần tử của mảng trong bộ nhớ .
6.4 Khai báo mảng theo dạng cấp phát động .
6.5 Nhập giá trị cho các phần tự của mảng .
6.6 Sử dụng công cụ array constructor để nhập giá trị cho mảng .
6.7 Mảng và phép gán .
6.8 Mảng và cách sử dụng chỉ số để truy cập các bộ phận của mảng .
6.9 Biểu thức có chứa biến mảng . Phép gán .
6.10 Lệnh where .
6.11 Một số hàm chuẩn có đối số là mảng .
6.12 Thực hành .

CHƯƠNG 7 : CHƯƠNG TRÌNH CON ( SUBPROGRAM )


7.1 Khái niệm về chương trình con .
7.2 Cấu trúc chương trình Fortran .
7.2.1 Cấu trúc tổng quát một chương trình Fortran .
7.2.2 Vị trí của các chương trình con internal và external .
7.3 Chương trình con dạng subroutine ( kiểu thủ tục ) .
7.3.1 Ví dụ về internal subroutine .
7.3.2 Cú pháp của chương trình con subroutine .
7.3.3 Ví dụ về external subroutine .
7.3.4 So sánh internal và external subroutine .
7.3.5 Sử dụng lệnh call .
7.3.6 Thuộc tính save áp dụng cho các biến cục bộ .
7.4 Chương trình con kiểu function (hàm ).
7.4.1 Ví dụ về chương trình con internal function .
7.4.2 Cú pháp của chương trình con function .
7.4.3 Ví dụ về chương trình con external function .
7.4.4 Cấu trúc khối giao diện interface .
7.4.5 Chương trình con dạng external không cần khai báo giao diện .
7.5 Chương trình con có đối hình thức là mảng .
7.5.1 Khai báo đối số là mảng có kích thước rõ ràng trong chương trình con.
7.5.2 Khai báo đối số là mảng có kích thước thể hiện bằng một biến nguyên .
7.5.3 Khai báo đối số là mảng có kích thước tự do .
7.5.4 Hàm có kết quả trả về là một mảng .
7.6 Chương trình con kiểu đệ qui .
7.7 Chương trình con có đối hình thức là xâu kí tự .
7.8 Chương trình con có đối hình thức được gán thuộc tính optional .
7.9 Chương trình con có đối hình thức là subroutine hoặc function .
151
7.10 Thực hành .

CHƯƠNG 8 : ĐƠN VỊ CHƯƠNG TRÌNH MODULE


8.1 Khái niệm về module .
8.2 Cấu trúc tổng quát một module .
8.3 Module chứa các hằng số, dữ liệu dùng chung , khai báo interface .
8.3.1 Ví dụ 01 .
8.3.2 Ví dụ 02 .
8.3.3 Ví dụ 03 .
8.4 Module chứa các đơn vị chương trình .
8.4.1 Ví dụ 04 .
8.4.2 Ví dụ 05 .
8.5 Lập hàm có tính chất generic do người lập trình định nghĩa .
8.6 Định nghĩa phép tính bằng module .
8.7 Biến có thuộc tính public và private .
8.8 Thực hành .

CHƯƠNG 9 : KHÁI NIỆM VỀ CON TRỎ ( POINTER )


9.1 Khái niệm chung .
9.2 Các ví dụ .

PHỤ LỤC A : SỬ DỤNG GNUPLOT ĐỂ VẼ ĐỒ THỊ 2D/3D


1. Giới thiệu phần mềm gnuplot .
2. Cửa sổ gnuplot .
3. Một số ví dụ .
4. Vẽ đồ thị từ kết quả được sắp xếp theo dạng cột trong một tập tin .
5. Soạn và chạy tập tin script .
6. Vẽ đồ thị 3D z = f(x,y ) bằng lệnh splot .
7. Vẽ mặt thể hiện mảng hai chiều .
8. Vẽ véc tơ trong mặt phẳng 2D .

PHỤ LỤC B : CHƯƠNG TRÌNH LINPACK GIẢI HỆ n PHƯƠNG TRÌNH TUYẾN TÍNH
Ax = b BẰNG PP KHỬ GAUSS .
1.Văn bản chương trình .
2.Chạy test chương trình .

TÀI LIỆU THAM KHẢO .

MỤC LỤC .

*****

152

You might also like