diff options
author | ZUO Zhihua <zuo.zhihua@qq.com> | 2023-12-26 02:22:45 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-12-25 19:22:45 +0100 |
commit | e561b5bb54c0f31c5e8c4f77136637bbd4a0cd20 (patch) | |
tree | d4ec04db04fd103fb796e291498de69fb6d416cd /fortran.html.markdown | |
parent | a91d6131c6294f624c44497665f8b5086ebb3dfe (diff) |
[Fortran/en] [Fortran/zh-cn] Format the code, introduce the latest standard Fortran 2023 (#4814)
* Fortran: format code style and improve description
* Fortran: rename filenames, update Chinese version
Diffstat (limited to 'fortran.html.markdown')
-rw-r--r-- | fortran.html.markdown | 457 |
1 files changed, 457 insertions, 0 deletions
diff --git a/fortran.html.markdown b/fortran.html.markdown new file mode 100644 index 00000000..316f8c87 --- /dev/null +++ b/fortran.html.markdown @@ -0,0 +1,457 @@ +--- +language: Fortran +contributors: + - ["Robert Steed", "https://github.com/robochat"] +filename: learnfortran.f90 +--- + +Fortran is one of the oldest computer languages. It was developed in the 1950s +by IBM for numeric calculations (Fortran is an abbreviation of "Formula +Translation"). Despite its age, it is still used for high-performance computing +such as weather prediction. However, the language has changed considerably over +the years, although mostly maintaining backwards compatibility; well known +versions are FORTRAN 77, Fortran 90, Fortran 95, Fortran 2003, Fortran 2008, +Fortran 2018 and Fortran 2023. + +This overview will discuss the features of Fortran 2008 since it is the most +widely implemented of the more recent specifications and the later versions are +largely similar (by comparison FORTRAN 77 is a very different language). + +```fortran + +! This is a comment. + +program example ! declare a program called example. + + ! Code can only exist inside programs, functions, subroutines or modules. + ! Using indentation is not required but it is recommended. + + ! Declaring Variables + ! =================== + + ! All declarations must come before statements and expressions. + + implicit none ! prevents dynamic declaration of variables (recommended!) + ! Implicit none must be redeclared in every function/program/module... + + ! IMPORTANT - Fortran is case insensitive. + real z + REAL Z2 + + real :: v, x ! WARNING: default initial values are compiler dependent! + real :: a = 3, b = 2E12, c = 0.01 + integer :: i, j, k = 1, m + real, parameter :: PI = 3.1415926535897931 ! declare a constant. + logical :: y = .TRUE., n = .FALSE. ! boolean type. + complex :: w = (0, 1) ! sqrt(-1) + character(len=3) :: month ! string of 3 characters. + + real :: array(6) ! declare an array of 6 reals. + real, dimension(4) :: arrayb ! another way to declare an array. + integer :: arrayc(-10:10) ! an array with a custom index. + real :: array2d(3, 2) ! multidimensional array. + + ! The '::' separators are not always necessary but are recommended. + + ! many other variable attributes also exist: + real, pointer :: p ! declare a pointer. + + integer, parameter :: LP = selected_real_kind(20) + real(kind=LP) :: d ! long precision variable. + + ! WARNING: initialising variables during declaration causes problems + ! in functions since this automatically implies the 'save' attribute + ! whereby values are saved between function calls. In general, separate + ! declaration and initialisation code except for constants! + + ! Strings + ! ======= + + character :: a_char = 'i' + character(len=6) :: a_str = "qwerty" + character(len=30) :: str_b + character(len=*), parameter :: a_long_str = "This is a long string." + !can have automatic counting of length using (len=*) but only for constants. + + str_b = a_str//" keyboard" ! concatenate strings using // operator. + + ! Assignment & Arithmetic + ! ======================= + + Z = 1 ! assign to variable z declared above (case insensitive). + j = 10 + 2 - 3 + a = 11.54/(2.3*3.1) + b = 2**3 ! exponentiation + + ! Control Flow Statements & Operators + ! =================================== + + ! Single-line if statement + if (z == a) b = 4 ! condition always need surrounding parentheses. + + if (z /= a) then ! z not equal to a + ! Other symbolic comparisons are < > <= >= == /= + b = 4 + else if (z .GT. a) then ! z greater than a + ! Text equivalents to symbol operators are .LT. .GT. .LE. .GE. .EQ. .NE. + b = 6 + else if (z < a) then ! 'then' must be on this line. + b = 5 ! execution block must be on a new line. + else + b = 10 + end if ! end statement needs the 'if' (or can use 'endif'). + + if (.NOT. (x < c .AND. v >= a .OR. z == z)) then ! boolean operators. + inner: if (.TRUE.) then ! can name if-construct. + b = 1 + end if inner ! then must name endif statement. + end if + + i = 20 + select case (i) + case (0, 1) ! cases i == 0 or i == 1 + j = 0 + case (2:10) ! cases i is 2 to 10 inclusive. + j = 1 + case (11:) ! all cases where i>=11 + j = 2 + case default + j = 3 + end select + + month = 'jan' + ! Condition can be integer, logical or character type. + ! Select constructions can also be named. + monthly:select case(month) + case ("jan") + j = 0 + case default + j = -1 + end select monthly + + do i = 2, 10, 2 ! loops from 2 to 10 (inclusive) in increments of 2. + innerloop: do j = 1, 3 ! loops can be named too. + exit ! quits the loop. + end do innerloop + cycle ! jump to next loop iteration. + end do + + ! Goto statement exists but it is heavily discouraged though. + goto 10 + stop 1 ! stops code immediately (returning specified condition code). +10 j = 201 ! this line is labeled as line 10 + + ! Arrays + ! ====== + array = (/1, 2, 3, 4, 5, 6/) + array = [1, 2, 3, 4, 5, 6] ! using Fortran 2003 notation. + arrayb = [10.2, 3e3, 0.41, 4e-5] + array2d = reshape([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], [3, 2]) + + ! Fortran array indexing starts from 1. + ! (by default but can be defined differently for specific arrays). + v = array(1) ! take first element of array. + v = array2d(2, 2) + + print *, array(3:5) ! print all elements from 3rd to 5th (inclusive). + print *, array2d(1, :) ! print first column of 2d array. + + array = array*3 + 2 ! can apply mathematical expressions to arrays. + array = array*array ! array operations occur element-wise. + ! array = array*array2d ! these arrays would not be compatible. + + ! There are many built-in functions that operate on arrays. + c = dot_product(array, array) ! this is the dot product. + ! Use matmul() for matrix maths. + c = sum(array) + c = maxval(array) + print *, minloc(array) + c = size(array) + print *, shape(array) + m = count(array > 0) + + ! Loop over an array (could have used Product() function normally). + v = 1 + do i = 1, size(array) + v = v*array(i) + end do + + ! Conditionally execute element-wise assignments. + array = [1, 2, 3, 4, 5, 6] + where (array > 3) + array = array + 1 + elsewhere(array == 2) + array = 1 + elsewhere + array = 0 + end where + + ! Implied-DO loops are a compact way to create arrays. + array = [(i, i=1, 6)] ! creates an array of [1,2,3,4,5,6] + array = [(i, i=1, 12, 2)] ! creates an array of [1,3,5,7,9,11] + array = [(i**2, i=1, 6)] ! creates an array of [1,4,9,16,25,36] + array = [(4, 5, i=1, 3)] ! creates an array of [4,5,4,5,4,5] + + ! Input/Output + ! ============ + + print *, b ! print the variable 'b' to the command line + + ! We can format our printed output. + print "(I6)", 320 ! prints ' 320' + print "(I6.4)", 3 ! prints ' 0003' + print "(F6.3)", 4.32 ! prints ' 4.320' + + ! The letter indicates the expected type and the number afterwards gives + ! the number of characters to use for printing the value. + ! Letters can be I (integer), F (real), E (engineering format), + ! L (logical), A (characters) ... + print "(I3)", 3200 ! print '***' since the number doesn't fit. + + ! we can have multiple format specifications. + print "(I5,F6.2,E6.2)", 120, 43.41, 43.41 + print "(3I5)", 10, 20, 30 ! 3 repeats of integers (field width = 5). + print "(2(I5,F6.2))", 120, 43.42, 340, 65.3 ! repeated grouping of formats. + + ! We can also read input from the terminal. + read (*, *) v + read (*, "(2F6.2)") v, x ! read two numbers + + ! To write a file. + open (unit=12, file="records.txt", status="replace") + ! The file is referred to by a 'unit number', an integer that you pick in + ! the range 9:99. Status can be one of {'old','replace','new'}. + write (12, "(F10.2,F10.2,F10.2)") c, b, a + close (12) + + ! To read a file. + open (newunit=m, file="records.txt", status="old") + ! The file is referred to by a 'new unit number', an integer that the compiler + ! picks for you. + read (unit=m, fmt="(3F10.2)") a, b, c + close (m) + + ! There are more features available than discussed here and alternative + ! variants due to backwards compatibility with older Fortran versions. + + ! Built-in Functions + ! ================== + + ! Fortran has around 200 functions/subroutines intrinsic to the language. + ! Examples - + call cpu_time(v) ! sets 'v' to a time in seconds. + k = ior(i, j) ! bitwise OR of 2 integers. + v = log10(x) ! log base 10. + i = floor(b) ! returns the closest integer less than or equal to x. + v = aimag(w) ! imaginary part of a complex number. + + ! Functions & Subroutines + ! ======================= + + ! A subroutine runs some code on some input values and can cause + ! side-effects or modify the input values. + + call routine(a, c, v) ! subroutine call. + + ! A function takes a list of input parameters and returns a single value. + ! However the input parameters may still be modified and side effects + ! executed. + + m = func(3, 2, k) ! function call. + + ! Function calls can also be evoked within expressions. + print *, func2(3, 2, k) + + ! A pure function is a function that doesn't modify its input parameters + ! or cause any side-effects. + m = func3(3, 2, k) + +contains ! Zone for defining sub-programs internal to the program. + + ! Fortran has a couple of slightly different ways to define functions. + + integer function func(a, b, c) ! a function returning an integer value. + ! implicit none ! subvariable fields can no longer declare implicit none + integer, intent(in) :: a, b, c ! type of input parameters defined inside the function. + + if (a >= 2) then + func = a + b + c ! the return variable defaults to the function name. + return ! can return the current value from the function at any time. + end if + func = a + c + + ! Don't need a return statement at the end of a function. + end function func + + function func2(a, b, c) result(f) ! return variable declared to be 'f'. + integer, intent(in) :: a, b ! can declare and enforce that variables + !are not modified by the function. + integer, intent(inout) :: c + integer :: f ! function return type declared inside the function. + integer :: cnt = 0 ! GOTCHA - initialisation implies variable is + !saved between function calls. + + f = a + b - c + c = 4 ! altering the value of an input variable. + cnt = cnt + 1 ! count number of function calls. + + end function func2 + + pure function func3(a, b, c) ! a pure function can have no side-effects. + integer, intent(in) :: a, b, c + integer :: func3 + + func3 = a*b*c + + end function func3 + + subroutine routine(d, e, f) + real, intent(inout) :: f + real, intent(in) :: d, e + + f = 2*d + 3*e + f + + end subroutine routine + +end program example ! End of Program Definition ----------------------- + +! Functions and Subroutines declared externally to the program listing need +! to be declared to the program using an Interface declaration (even if they +! are in the same source file!) (see below). It is easier to define them within +! the 'contains' section of a module or program. + +elemental real function func4(a) result(res) +! An elemental function is a Pure function that takes a scalar input variable +! but can also be used on an array where it will be separately applied to all +! of the elements of an array and return a new array. + real, intent(in) :: a + + res = a**2 + 1.0 + +end function func4 + +! Modules +! ======= + +! A module is a useful way to collect related declarations, functions and +! subroutines together for reusability. + +module fruit + + real :: apple + real :: pear + real :: orange + +end module fruit + +module fruity + ! Declarations must be in the order: modules, interfaces, variables. + ! (can declare modules and interfaces in programs too). + + use fruit, only: apple, pear ! use apple and pear from fruit module. + implicit none ! comes after module imports. + + private ! make things private to the module (default is public). + ! Declare some variables/functions explicitly public. + public :: apple, mycar, create_mycar + ! Declare some variables/functions private to the module (redundant here). + private :: func4 + + ! Interfaces + ! ========== + ! Explicitly declare an external function/procedure within the module + ! (better in general to put functions/procedures in the 'contains' section). + interface + elemental real function func4(a) result(res) + real, intent(in) :: a + end function func4 + end interface + + ! Overloaded functions can be defined using named interfaces. + interface myabs + ! Can use 'module procedure' keyword to include functions already + ! defined within the module. + module procedure real_abs, complex_abs + end interface + + ! Derived Data Types + ! ================== + ! Can create custom structured data collections. + type car + character(len=100) :: model + real :: weight ! (kg) + real :: dimensions(3) ! i.e. length-width-height (metres). + character :: colour + contains + procedure :: info ! bind a procedure to a type. + end type car + + type(car) :: mycar ! declare a variable of your custom type. + ! See create_mycar() routine for usage. + + ! Note: There are no executable statements in modules. + +contains + + subroutine create_mycar(mycar) + ! Demonstrates usage of a derived data type. + type(car), intent(out) :: mycar + + ! Access type elements using '%' operator. + mycar%model = "Ford Prefect" + mycar%colour = 'r' + mycar%weight = 1400 + mycar%dimensions(1) = 5.0 ! default indexing starts from 1! + mycar%dimensions(2) = 3.0 + mycar%dimensions(3) = 1.5 + + end subroutine create_mycar + + subroutine info(self) + class(car), intent(in) :: self + ! 'class' keyword used to bind a procedure to a type here. + + print *, "Model : ", self%model + print *, "Colour : ", self%colour + print *, "Weight : ", self%weight + print *, "Dimensions: ", self%dimensions + + end subroutine info + + real pure function real_abs(x) + real, intent(in) :: x + + if (x < 0) then + real_abs = -x + else + real_abs = x + end if + + end function real_abs + + real pure function complex_abs(z) + complex, intent(in) :: z + ! long lines can be continued using the continuation character '&' + + complex_abs = sqrt(real(z)**2 + & + aimag(z)**2) + + end function complex_abs + +end module fruity + +``` + +### More Resources + +For more information on Fortran: + ++ [wikipedia](https://en.wikipedia.org/wiki/Fortran) ++ [Fortran-lang Organization](https://fortran-lang.org/) ++ [Fortran_95_language_features](https://en.wikipedia.org/wiki/Fortran_95_language_features) ++ [fortranwiki.org](http://fortranwiki.org) ++ [www.fortran90.org/](http://www.fortran90.org) ++ [list of Fortran 95 tutorials](http://www.dmoz.org/Computers/Programming/Languages/Fortran/FAQs%2C_Help%2C_and_Tutorials/Fortran_90_and_95/) ++ [Fortran wikibook](https://en.wikibooks.org/wiki/Fortran) ++ [Fortran resources](http://www.fortranplus.co.uk/resources/fortran_resources.pdf) ++ [Mistakes in Fortran 90 Programs That Might Surprise You](http://www.cs.rpi.edu/~szymansk/OOF90/bugs.html) |