Intuitively, one might assume that if one wants to initialize a small array by rows that something like the following will work:
! DOES NOT WORK
integer :: xx(3,5)= [ 1, 2, 3, 4, 5], &
[10,20,30,40,50], &
[11,22,33,44,55]
or perhaps
! DOES NOT WORK
integer :: xx(3,5)= [ [ 1, 2, 3, 4, 5], &
[10,20,30,40,50], &
[11,22,33,44,55] ]
Someday something simple but inconvinient might work:
integer ar1(3,5) /1,10,11, & ! column 1
2,20,22, & ! column 2
3,30,33, & ! column 3
4,40,44, & ! column 4
5,50,55/ ! column 5
Although someday this simple one might work, but currently the following syntax is required to specify the values in an intuitive row-column sequence using an array constructor:
integer,save :: xx(3,5)= reshape([&
1, 2, 3, 4, 5, &
10,20,30,40,50, &
11,22,33,44,55 &
],shape(xx),order=[2,1])
This is because an array constructor can be used to create and assign values only to rank-one arrays. To define arrays of more than one dimension with an array constructor, you must use the RESHAPE(3f) intrinsic function.
Note that the ORDER= option on RESHAPE(3f) is used to allow the values to be specified in row-column order instead of the default behavior, which fills columns first.
Also note that if the expressions are of type character, Fortran 95/90 requires each expression to have the same character length (there is a common compiler extension that extends all strings to the length of the longest value specified, but depending on it reduces portability).
When working with small arrays the issue that there is no default Fortran routine for printing an array in row-column order becomes apparent. So lets create a simple solution for integer arrays (PRINT_MATRIX_INT(3f)):
program demo_array_constructor ! initializing small arrays
implicit none
integer,save :: xx(3,5)= reshape([&
1, 2, 3, 4, 5, &
10,20,30,40,50, &
11,22,33,44,-1055 &
],shape(xx),order=[2,1])
call print_matrix_int('xx array:',xx)
contains
subroutine print_matrix_int(title,arr)
implicit none
character(len=*),parameter::ident= "@(#)print_matrix_int(3f) - print small 2d integer arrays in row-column format"
character(len=*),intent(in) :: title
integer,intent(in) :: arr(:,:)
integer :: i
character(len=:),allocatable :: biggest
write(*,*)trim(title) ! print title
biggest=' ' ! make buffer to write integer into
write(biggest,'(i0)')ceiling(log10(real(maxval(abs(arr)))))+2 ! find how many characters to use for integers
biggest='(" > [",*(i'//trim(biggest)//':,","))' ! use this format to write a row
do i=1,size(arr,dim=1) ! print one row of array at a time
write(*,fmt=biggest,advance='no')arr(i,:)
write(*,'(" ]")')
enddo
end subroutine print_matrix_int
end program demo_array_constructor
Results:
xx array:
> [ 1, 2, 3, 4, 5 ]
> [ 10, 20, 30, 40, 50 ]
> [ 11, 22, 33, 44, 55 ]
We could do a more robust version that handles REAL and COMPLEX values as well as NaN values, but it has already been done. If you need to print a variety of small matrices see:
dispmodule(3f), "A Fortran 95 module for pretty-printing matrices".
Kristjan Jonasson, Department of Computer Science,
School of Science and Engineering, University of Iceland,
Hjardarhaga 4, 107 Reykjavik, Iceland (jonasson@hi.is).
Note that DATA statements are very flexible, and allow for perhaps the most intelligible way of specifying small arrays row by row. For example:
! fill rows using DATA statements
integer,save,dimension(3,5) :: gg
data gg(1,:)/ 1, 2, 3, 4, 5 /
data gg(2,:)/ 10, 20, 30, 40, 50 /
data gg(3,:)/ 11, 22, 33, 44, 55 /
There are other ways to use a DATA statement to fill in row-column order, including use of the SIZE(3f) function and an implied-DO:
! use implied-DO so data can be declared in row-column order
integer, dimension(3,5) :: ff
DATA (( ff(J,I), I=1,size(ff,dim=2)), J=1,size(ff,dim=1)) / &
01,02,03,04,05, &
10,20,30,40,50, &
11,22,33,44,55 /
Sometimes instead of using RESHAPE(3f) you will see someone initialize a vector and then equivalence it to a multi-dimensional array; especially if the code has a reason to access the data as both a vector and a matrix:
! multi-dimensional row1, row2, .... by equivalence
integer,parameter :: d1=3,d2=5
integer :: ee(d1,d2)
! note that the DATA statements could be used to initialize the array instead
integer :: e(d1*d2) =[1,10,11, 2,20,22, 3,30,33, 4,40,44, 5,50,55]
equivalence (e(1),ee(1,1))
Remember that for simple initializations vector statements can be used
real :: arr(10,20)=0.0
array constructors can be used to define constants, not just vectors
integer,parameter :: ii(10,10)=reshape(&
& shape=shape(ii), order=[2,1], source=[(i,i=1,size(ii))] )
and that if things are too complicated you can just set the values in the executable body of the code.
program test_random_number
real :: r(5,5)
call random_number(r)
end program
Remember that a DATA statement does not require that all values be initialized, whereas an array constructor does; and that you cannot initialize values multiple times and be standard-conforming. So be very careful when using DATA statements that you initialized everything you wanted to.
DATA statements and initialization in the declarations creates an implicit SAVE and are only applied on the initial call which should be kept in mind if changing the values or creating threaded applications.