In chapter 2, we covered the basic declaration of arrays when implementing the minimal working version of our tsunami simulator:
real :: h(grid_size) ❶
❶ grid_size is the integer size of array h.
When you specify the size of the array in the declaration line, you tell the compiler to declare a static array. The array size is known at compile time, and the compiler can use this information to generate more efficient machine code. Effectively, when you declare a static array, it’s allocated in memory when you run the program.
However, you won’t always know the size of the arrays ahead of time. It just so happens that each stock data CSV file has the same number of records (4,620), but this may not always be the case, as some companies may have a much longer presence in the public markets than others. Furthermore, if you chose to later work on a different or larger stock price dataset, it would be unwieldy to have to hardcode the size of the arrays every time. This is where dynamic, or, in Fortran lingo, allocatable, arrays come in. Whenever the size of the array is unknown ahead of time, or you anticipate that it will change at some time during the life of the program, declare it as allocatable:
real, allocatable :: h(:) ❶
❶ The colon in parentheses indicates that the size is to be determined.
Writing more general and flexible apps will also require allocating arrays at runtime.
Notice that there are two key changes here relative to the basic declaration. We added the allocatable attribute, and we used the colon (:) as a placeholder for the array size. At this point in the code, we didn’t allocate this array in memory but simply stated, “We’ll use a real, two-dimensional array h, whose size is yet to be determined.”
When do we use dynamic over static arrays?
Use dynamic over static arrays whenever you don’t know the size of the arrays ahead of time, or know that it will change. A few examples come to mind:
- Storing user-input data, either entered by standard input (keyboard) or read from an input file
- Reading data from multiple files of different lengths
- Arrays that will be reused across datasets
- Arrays that may grow or shrink during the lifetime of the program
Dynamic arrays will help you write more general and flexible code but may carry a performance penalty, as allocation of memory is a slow operation compared to, say, floating-point arithmetic.
It does seem that for our use case we should use dynamic arrays. Following the data description from the previous section, we’ll need the following:
- An array of character strings to hold stock symbols (
AAPL,AMZN, etc.) - An array of character strings to hold the timestamps (
2018-05-15,2018-05-14, etc.) - Arrays of real numbers to hold the actual stock data, such as opening and closing prices and others
We can apply the syntax from the allocatable declaration earlier to declare these arrays, as shown in the following listing.
Listing 5.2 Declaring the arrays for stock symbols, timestamps, and stock price data
program stock_gain
implicit none
character(len=4), allocatable :: symbols(:) ❶
character(len=:), allocatable :: time(:) ❷
real, allocatable :: open(:), high(:), low(:), & ❸
close(:), adjclose(:), volume(:) ❸
end program stock_gain
❶ Dynamic array of stock symbols
❸ Dynamic arrays for stock price data
Notice that for symbols I declared an array of character strings of length 4, whereas for the time array I didn’t specify the length ahead of time (len=:). This is because we’ll determine the length of timestamps in the subroutine that’s in charge of reading the data files, and we don’t need to hardcode the length here. For the rest of the data, I declared real (floating point) arrays. Even though the volume is an integer quantity (number of shares traded), real will work just fine for typical volume values and will help simplify the code. You can compile and run this program, but it won’t do anything useful yet, since it only declares the arrays that we’ll use. To loop over stock symbols and print each one to the screen, we’ll use an array constructor to initialize the array symbols.
Specifying the length of character strings
The keyword argument len in the character data type declaration isn’t required, and you can just type character(4) instead of character(len=4). Likewise for character(:). The value can also be omitted entirely (character), in which case it defaults to character(1), which is a single character. Feel free to type out len if you want to help a casual reader of your code, or omit it to make your code less verbose.