Can I overload entry access operators [], () or {} for derived data types in FORTRAN 2003? In the following example, I want to define access scheme for the derived data type "custom".
type custom
integer, dimension(:), allocatable :: a
end type custom
type(custom) :: t
! after some initialization
! .....
! .....
! .....
!
t%a( (/ 1, 2, 5 /) ) ! return entries located in positions 1, 2, and 5
t{ (/ 1, 2, 5 /) } ! ?????? I want to define what stuff it should return
How can I do that?
Update:
Note that I don't want to use the array "t%a" directly and do conventional sub-array operation on that. Instead, I want to redefine array operation for data type "custom" such that t{'first'} should return a pointer the first entry in t%a or t%a(1) so I can say
t['first']= 18
or
print *, t['first'].
Also with additional overloading I want to get a functionality like t[1] = 18 works like t['first'] = 18.
This rather depends on what you mean by "return".
By itself the example offered
t%a([1,2,5]) ! Using syntax offered by Fortran 2003
doesn't return anything: it's a subobject. With a reference to that subobject we can do various things:
print *, t%a([1,2,5])
t%a([1,2,5]) = 27
t%a([1,2,5]) = sin(real(t%a([1,2,5])))
but there's still no concept of "returning". Crucially, as we shall see, these are not expressions.
Coming to the question, can t[], t(), t{} mean something, then the answer is, simply, "no".* You may want, for example, to say:
t[1,2,5] = 1
to mean
t%a[1,2,5] = 1
but that is not something to consider.
It would be possible to create an expression like
print *, t%ref([1,2,5])
but we're quite in the non-definable territory.
However, as you now mention pointers, there's more to say. Whilst the preferred syntax t[1] or t["first"] is not available we still have the option of type-bound procedures. For example, a function call t%ref("first") may well be able to return a pointer to the first element of t%a. For example, t%ref(1) could be like
module reference
implicit none
type custom
integer, dimension(:), allocatable :: a
contains
procedure ref
end type custom
contains
function ref(t, idx)
class(custom), target, intent(in) :: t
integer, intent(in) :: idx
integer, pointer :: ref
ref => t%a(idx)
end function ref
end module reference
use reference
implicit none
type(custom), target :: t
integer, pointer :: b
t%a = [1, 2, 3, 4, 5]
print *, t%a
b => t%ref(1) ! Fortran 2008 allows direct assignment
b = 8 ! but compiler support is very limited.
print *, t%a
end
If desired ref can be made generic so that t%ref("first") (etc.) is acceptable.
* I'm basing that on the fact that here t is a scalar. However, as mentioned by Vladimir F in a comment () and [] potentially do mean things. The first relates to arrays and the second to co-arrays. Syntax, then, is an issue, but this answer looks more at the mechanism than syntax.
Related
I define a type named MATRIX and a variable of this type named A as the following
TYPE MATRIX
REAL, ALLOCATABLE, DIMENSION(:,:) :: MAT
END TYPE
TYPE(MATRIX) :: A
the usual way of constructing A and then using it is
ALLOCATE(A%MAT(2,2))
A%MAT(1,:) = [1,2]
A%MAT(2,:) = [3,4]
PRINT*, A%MAT
I'd like to know that if it's possible to work with variable A without having to write A%MAT. In other words, is there any workaround to rewrite the former block of code in the following form (using A instead of A%MAT)
ALLOCATE(A(2,2))
A(1,:) = [1,2]
A(2,:) = [3,4]
PRINT*, A
Unfortunately, the syntax a(1,:) = [1,2] where a is a derived type is not currently allowed by the Fortran standard (Fortran 2018 at the time of writing).
There is a proposal to allow this in a future Fortran standard.
You can use the associate statement to bind certain expressions to a shorter name. This can be used to give a name to a component of a variable.
allocate(A%mat(2, 2))
associate(B => A%mat(:, :))
B(1,:) = [1, 2]
B(2,:) = [3, 4]
write(*, *) B
end associate
Note that you can also use reshape and automatic allocation to get rid of some slices.
! No allocate statement necessary.
A%mat = reshape([1, 2, 3, 4], [2, 2])
Is there possibility to use indexing directly on a function's return value? Something like this:
readStr()(2:5)
where readStr() is a function which returns a character string or an array. In many other languages it is quite possible, but what about Fortran? The syntax in my example of course does not compile. Is there any other syntax to be used?
No, that is not possible in Fortran. You could, however, alter your function to take an additional index array that determines which elements are returned. This example illustrates this possibility using an interface to allow for an optional specification of the indices (simplified greatly thanks to the comment by IanH):
module test_mod
implicit none
contains
function squareOpt( arr, idx ) result(res)
real, intent(in) :: arr(:)
integer, intent(in), optional :: idx(:)
real,allocatable :: res( : )
real :: res_( size(arr) )
integer :: stat
! Calculate as before
res_ = arr*arr
if ( present(idx) ) then
! Take the sub-set
allocate( res(size(idx)), stat=stat )
if ( stat /= 0 ) stop 'Cannot allocate memory!'
res = res_(idx)
else
! Take the the whole array
allocate( res(size(arr)), stat=stat )
if ( stat /= 0 ) stop 'Cannot allocate memory!'
res = res_
endif
end function
end module
program test
use test_mod
implicit none
real :: arr(4)
integer :: idx(2)
arr = [ 1., 2., 3., 4. ]
idx = [ 2, 3]
print *, 'w/o indices',squareOpt(arr)
print *, 'w/ indices',squareOpt(arr, idx)
end program
No.
But if it bothers you, you can write your own user defined functions and operators to achieve a similar outcome without having to store the result of the function reference in a separate variable.
You can avoid declaring another variable if you use associate. Whether it is any better or clearer than a temporary variable must be decided by the user. The result has to be stored somewhere anyway.
associate(str=>readStr())
print *, str(2:5)
end associate
It will not be very useful for this specific case with a potentially long string but might be more useful for other similar cases that get linked here as duplicates.
In the program below, there are two methods presented for passing an array:
program main
integer, dimension(4) :: x = [9, 8, 7, 6]
call print_x(x(2:3)) ! Method 1
call print_x(x(2)) ! Method 2
end program
subroutine print_x(x)
integer, dimension(2), intent(in) :: x
print *, x
end subroutine
Both methods produce the same result: the numbers 8 and 7 are printed. Personally, I would never code this using Method 2 because it looks like a single value is being passed rather than an array.
Can you give an example of when Method 2 MUST be used instead of Method 1?
Consider the program
implicit none
integer :: x(2,2)=0
call set(x(2,1))
print*, x
contains
subroutine set(y)
integer y(2)
y = [1,2]
end subroutine set
end program
The dummy argument y in this subroutine call is argument associated with the elements x(2,1) and x(1,2). There is no array section of x which consists of exactly these two elements.
I would like to access the the elements of an array in a an arrayed derived type using the subroutine sum_real. That is: sum over first entry in the weight for all people.
type my_type
real, dimension(:), allocatable :: weight
real :: total_weight
end type my_type
type (my_type), dimension (:), allocatable :: people
type (my_type) :: answer
allocate (people (2))
allocate (people (1)%weight(2))
allocate (people (2)%weight(2))
people (1) % weight(1) = 1
people (2) % weight(1) = 1
people (1) % weight(2) = 3
people (2) % weight(2) = 3
call sum_real ( people (:) % weight(1), answer % total_weight )
What I want to do is similar to the answer found in Array of derived type: select entry, except for the fact that I have an allocated array in an arrayed derived type instead of an single element.
But, I get a compiler error:
error #7828: The part-name to the right of a part-ref with nonzero rank has the ALLOCATABLE attribute (6.1.2). [WEIGHT]
What you try is not possible if your component is allocatable. The reference (6.1.2) is actually a reference to the official standard documents, which prohibits this.
The reason is simple, the allocatable components (scalar or arrays) are stored in a different part of memory than the derived type itself. Therefore if you write
sum(people%total_weight)
or
people%total_weight = 0
it is no problem, total_weight is not allocatable, it is stored within the derived type and the compiler just goes in a simple loop and sets one field after another to zero. You can know the address of each %totalweight beforehand.
On the other hand
sum(people%weight)
or
people%weight = 0
each %weight is stored elsewhere and you don't have any simple formula to compute where is each %weight(i).
The solution is either, to fix the size of the array, if possible
real, dimension(2) :: weight
or use a do loop
s = 0
do i = 1, size(people)
S = S + sum(people(i)%weight)
end do
If you have a F2003 compiler, and the bounds of the component array are the same for a particular parent array object, a third approach to the size specified by constant expression/use a do loop methods specified by VladimirF is to parameterize the type.
type my_type(n) ! This type has one parameter - n
integer, len :: n ! The parameter n is a length parameter.
real :: weight(n) ! This component depends on that parameter.
end type my_type
type (my_type(:)), dimension(:), allocatable :: people
! This integer is the size of the people component. Because
! people is allocatable it can be determined at runtime.
number_of_people = 2
! This integer is the size of the weight component that we want
! in the array people. Other arrays and scalars of type
! my_type can have different sizes for that component.
! Because people is allocatable this can be determined at
! runtime.
number_of_weights = 2
allocate( my_type(number_of_weights) :: people(number_of_people) )
! Define people%weight here.
people(1)%weight(1) = 1
...
! Using sum intrinsic for the sake of example
do i = 1, people%n
! The argument to sum is an array section.
print *, sum(people%weight(i))
! ^ ^ Reference to an element of a component
! | Reference to the entire people array
end do
Every element in an array of parameterized type has the same type parameters, hence every weight component in people has the same bounds, hence references such as people%weight become "regular".
Code using this approach (or the constant component size specification approach) still has to follow the restriction for component references that only one part of the reference can have non-zero rank (you can't work with people%weight as a whole as both people and the weight component have rank one).
In the allocatable component case some components in some elements might not be allocated and where they are allocated the component might have different bounds, making a "regular" reference to the data in the component across the elements of the array conceptually difficult.
Let A and B be matrices of size 1 times n and n times 1, respectively.
Then the multiplication of A with B is a 1 times 1 matrix.
Which is the better way to assign the value of MATMUL(A,B) to a real number x?
I would like to write:
x=MATMUL(A,B) ! <<--- but this is wrong.
The above expression is wrong because I'm trying to assign a 1 times 1 matrix to a real number.
My solution is to define a 1 times 1 matrix C and with this:
C=MATMUL(A,B)
x=C(1,1) ! <--- this solution is ok, but is too long
But, there exists a better way to assign MATMUL(A,B) to the real number x?
The entire code with my question is as follow:
PROGRAM testing
!
IMPLICIT NONE
REAL :: A(1,2),B(2,1),C(1,1),x
!
A(1,1)=1.0; A(1,2)=3.5
B(1,1)=2.0; B(2,1)=5.0
C=MATMUL(A,B) ! it is ok
x=MATMUL(A,B) ! it is wrong
x=C(1,1) ! it is ok <--- exists a better way ??
!
END PROGRAM testing
You have noticed that it is not possible to do intrinsic assignment of an array to a scalar (and C is a rank-2 array of size 1). x=C(1,1) is the correct way to do such assignment from the single element of C to the scalar x.
There are other ways to abstract that correct assignment statement, but probably little value in doing so.
In your specific case, however, there is alternative. Rather than matmul, consider dot_product.
x = DOT_PRODUCT(A(1,:), B(:,1)) ! Scalar result, intrinsic assignment allowed.
per my comment, you can write a very simple function to extract the first element of an array:
real function first(matrix) !return the (1,1,1,..) element of an array
real, intent(in) :: matrix(*)
first=matrix(1)
end function
simply use as:
real :: a(1,2),b(2,1),x
...
x=first(matmul(a,b))
note if you want to make sure this is only used for a dimension(1,1) array you need to use an explicit interface and do:
real function first(matrix)
real, intent(in) :: matrix(:,:)
if(.not.all(shape(matrix).eq.[1,1]))reporterror()
first=matrix(1,1)
end function