Lập trình · Ngôn ngữ lập trình
Hướng dẫn Fortran cho người mới bắt đầu
Trả lời nhanh
Fortran là ngôn ngữ biên dịch, kiểu tĩnh, được tạo ra cho tính toán số và khoa học. Fortran hiện đại (Fortran 90 và các chuẩn sau đó) có mã nguồn dạng tự do, module, mảng động và các phép toán mạnh trên cả mảng, còn trình biên dịch tạo ra mã máy rất nhanh. Hãy cài trình biên dịch gfortran miễn phí, lưu mã vào tệp .f90 rồi biên dịch bằng gfortran hello.f90 -o hello.
Fortran là gì và ai dùng nó
Fortran ("formula translation") được một nhóm do John Backus dẫn đầu phát triển tại IBM vào thập niên 1950 và là một trong những ngôn ngữ lập trình lâu đời nhất vẫn còn được dùng tích cực. Ngôn ngữ này được ISO chuẩn hóa; các bản sửa đổi lớn gồm Fortran 90, 95, 2003, 2008, 2018 và 2023. Kiểu cố định cũ (FORTRAN 77 viết hoa, mã bắt đầu ở cột 7) vẫn biên dịch được, nhưng mã mới dùng cú pháp tự do như trong bài này.
Fortran được dùng ở những nơi cần tính toán số nặng: mô hình thời tiết và khí hậu, động lực học chất lưu tính toán, mô phỏng vật lý và hóa học, cùng nhiều chương trình chạy trên siêu máy tính. Các phiên bản tham chiếu của thư viện đại số tuyến tính BLAS và LAPACK, nằm bên dưới những công cụ như NumPy và MATLAB, được viết bằng Fortran.
Cài trình biên dịch và chạy Hello, world
gfortran, một phần của GCC, là trình biên dịch miễn phí và có trên Linux (gói gfortran), macOS (Homebrew gcc) và Windows (MSYS2 hoặc WSL). ifx của Intel và LLVM Flang là các trình biên dịch khác. Trình quản lý gói Fortran (fpm) có thể tạo và build dự án.
program hello
implicit none
print *, "Hello, world!"
end program hellogfortran hello.f90 -o hello
./hello
# Hello, world!print * dùng đầu ra theo danh sách (list-directed), tự chọn khoảng cách cho bạn (lưu ý dấu cách ở đầu). Fortran không phân biệt hoa thường: PRINT, Print và print là như nhau.
Biến và các kiểu cơ bản
Mọi khai báo nằm ở đầu một đơn vị chương trình, trước các câu lệnh thực thi. Luôn viết implicit none: nếu không, các tên chưa khai báo bắt đầu bằng chữ I đến N sẽ âm thầm là số nguyên, còn các tên khác là số thực.
| Kiểu | Ví dụ khai báo | Ghi chú |
|---|---|---|
| integer | integer :: count = 3 | Thường là 32 bit; dùng integer(int64) cho 64 bit |
| real | real :: x | Thường là độ chính xác đơn |
| real(real64) | real(real64) :: y | Độ chính xác kép, lựa chọn thông dụng trong khoa học |
| complex | complex :: z = (1.0, 2.0) | Số phức có sẵn |
| logical | logical :: done = .false. | .true. / .false., toán tử .and., .or., .not. |
| character | character(len=20) :: name | Chuỗi độ dài cố định, được đệm thêm dấu cách |
program types
use, intrinsic :: iso_fortran_env, only: int64, real64
implicit none
integer :: count = 3
integer(int64) :: big = 9000000000_int64
real(real64) :: third
logical :: done = .false.
character(len=:), allocatable :: name
third = 1.0_real64 / 3.0_real64
name = "Ana"
print *, count, big, third, done, name
end program typesHậu tố _real64 đặt kind (độ chính xác) cho một hằng số. Nếu thiếu, 1.0 là hằng độ chính xác đơn và sẽ mất chữ số khi gán cho biến độ chính xác kép.
Luồng điều khiển
program control
implicit none
integer :: i, total, score
total = 0
do i = 1, 10
if (mod(i, 2) == 0) cycle ! skip even numbers
total = total + i
end do
print *, "sum of odd numbers:", total ! 25
score = 85
if (score >= 90) then
print *, "A"
else if (score >= 80) then
print *, "B"
else
print *, "C"
end if
select case (score)
case (90:)
print *, "excellent"
case (80:89)
print *, "good"
case default
print *, "keep going"
end select
end program controldo i = 1, 10 bao gồm cả hai đầu, và giá trị thứ ba tùy chọn đặt bước nhảy (do i = 10, 1, -1). cycle nhảy sang lần lặp kế tiếp và exit thoát khỏi vòng lặp. Cũng có do while (condition) và do trơn kèm exit. Chú thích bắt đầu bằng !.
Hàm, subroutine và module
Fortran có hai loại thủ tục: hàm (function) trả về một giá trị và dùng trong biểu thức; subroutine được gọi bằng call và trả kết quả qua các đối số. Hãy đặt thủ tục trong một module để trình biên dịch kiểm tra được mọi lời gọi.
module geometry
implicit none
contains
function circle_area(r) result(a)
real, intent(in) :: r
real :: a
a = 3.14159 * r**2
end function circle_area
subroutine swap(a, b)
integer, intent(inout) :: a, b
integer :: tmp
tmp = a
a = b
b = tmp
end subroutine swap
end module geometry
program demo
use geometry
implicit none
integer :: x, y
x = 1
y = 2
print *, circle_area(2.0) ! about 12.566
call swap(x, y)
print *, x, y ! 2 1
end program demoĐối số được truyền theo tham chiếu, nên thủ tục có thể thay đổi biến của bên gọi. intent(in), intent(out) và intent(inout) ghi rõ và bắt buộc những gì mỗi đối số được phép làm. ** là toán tử lũy thừa.
Mảng: tính năng nổi bật của Fortran
Mảng là trung tâm của Fortran. Chỉ số mặc định bắt đầu từ 1 (cho phép mọi cận dưới), phép tính chạy trên cả mảng mà không cần vòng lặp, lát cắt dùng a(start:end), và nhiều hàm tích hợp thao tác trực tiếp trên mảng.
program arrays
implicit none
integer :: i
real :: a(5), b(5)
real, allocatable :: c(:)
integer :: m(2, 3)
a = [(real(i), i = 1, 5)] ! 1.0 2.0 3.0 4.0 5.0
b = 2.0 * a + 1.0 ! whole-array arithmetic
print *, b(2:4) ! 5.0 7.0 9.0
print *, sum(a), maxval(b) ! 15.0 11.0
allocate(c(10))
c = 0.0
print *, size(c) ! 10
deallocate(c)
m = reshape([1, 2, 3, 4, 5, 6], [2, 3])
print *, m(2, 1) ! 2 (column-major order)
end program arraysMảng nhiều chiều được lưu theo thứ tự ưu tiên cột (column-major): chỉ số đầu tiên thay đổi nhanh nhất trong bộ nhớ, ngược với C. Để chạy nhanh, hãy cho vòng lặp trong cùng chạy theo chỉ số đầu tiên. Các hàm tích hợp hữu ích khác là matmul, dot_product, transpose, minval và count. Fortran hiện đại còn có vòng lặp do concurrent và coarray cho chương trình song song.
Xử lý lỗi
Fortran không có ngoại lệ. Các câu lệnh có thể thất bại nhận một đối số trạng thái: iostat cho nhập xuất, stat cho allocate. Hãy kiểm tra giá trị và xử lý; nếu bỏ qua, lỗi sẽ dừng chương trình. error stop kết thúc chương trình với một lỗi.
program parse
implicit none
character(len=20) :: text
integer :: n, ios
text = "abc"
read(text, *, iostat=ios) n
if (ios /= 0) then
print *, "not a number: ", trim(text)
else
print *, "n =", n
end if
end program parseĐọc từ một biến ký tự như ở trên gọi là đọc nội bộ (internal read) và là cách thông dụng để chuyển chuỗi thành số.
Một chương trình hoàn chỉnh nhỏ: trung bình và độ lệch chuẩn
module stats
implicit none
integer, parameter :: dp = kind(1.0d0)
contains
pure function mean(x) result(m)
real(dp), intent(in) :: x(:)
real(dp) :: m
m = sum(x) / size(x)
end function mean
pure function std_dev(x) result(s)
real(dp), intent(in) :: x(:)
real(dp) :: s
s = sqrt(sum((x - mean(x))**2) / (size(x) - 1))
end function std_dev
end module stats
program main
use stats
implicit none
real(dp) :: values(5)
values = [2.0_dp, 4.0_dp, 4.0_dp, 5.0_dp, 7.0_dp]
print '(a, f6.3)', "mean = ", mean(values)
print '(a, f6.3)', "std dev = ", std_dev(values)
end program main
! mean = 4.400
! std dev = 1.817Khai báo x(:) là mảng có hình dạng suy ra (assumed-shape), nên các hàm nhận mảng có độ dài bất kỳ. pure cam kết hàm không có tác dụng phụ. Định dạng '(a, f6.3)' in một chuỗi rồi đến một số thực rộng 6 ký tự với 3 chữ số thập phân.
Học tiếp ở đâu
- fortran-lang.org: mục Learn với bài hướng dẫn nhanh và các thực hành tốt nhất.
- Sách hướng dẫn GNU Fortran (gfortran) về các tùy chọn biên dịch và thủ tục tích hợp.
- Thử Trình quản lý gói Fortran (fpm) để tạo dự án và dùng các thư viện như thư viện chuẩn Fortran (stdlib).
- Khi học, hãy biên dịch kèm cảnh báo và kiểm tra lúc chạy, ví dụ
gfortran -Wall -fcheck=all.
Câu hỏi thường gặp
Ngày nay Fortran còn được dùng không?
Có. Fortran vẫn được dùng rộng rãi trong tính toán khoa học và tính toán hiệu năng cao, nhất là mô phỏng khí hậu, thời tiết, vật lý và kỹ thuật, và chuẩn ngôn ngữ vẫn đang được cập nhật.
Khác nhau giữa tệp .f và .f90 là gì?
Theo quy ước, tệp .f và .for dùng bố cục kiểu cố định cũ của FORTRAN 77, trong đó vị trí cột có ý nghĩa, còn tệp .f90 dùng mã nguồn dạng tự do. Phần mở rộng .f90 dùng cho mã dạng tự do của mọi chuẩn hiện đại, không riêng Fortran 90.
Vì sao Fortran nhanh?
Ngôn ngữ được thiết kế cho tính toán số: mảng có sẵn, quy tắc aliasing cho phép trình biên dịch tối ưu mạnh tay, và hàng chục năm phát triển trình biên dịch nhắm vào nó. C hoặc C++ viết tốt cũng có thể đạt tốc độ tương tự.
Mảng Fortran bắt đầu từ 0 hay 1?
Mặc định bắt đầu từ 1, nhưng bạn có thể chọn cận dưới bất kỳ, ví dụ real :: a(0:9) khai báo mười phần tử với chỉ số từ 0 đến 9.