Pointer to subarray defined by a map

本小妞迷上赌 提交于 2021-02-05 07:46:16

问题


I want to define a pointer to a subarray. For a simple range this is easily done by pointer => array(i:j), but I can't figure out how to do this for a map like k=[k1,k2,k3]. If I would define another array I could use a loop like array2=[(array1(k(j)),j=1,size(k,1))]. But it isn't possible to assign a pointer in a similar way (pointer => [(array1(k(j)),j=1,size(k,1))]) since the r.h.s. of the expression seems to define another variabel which then not even has the target attribute. For simple tasks, a trick around this, is to first assign a pointer to the total array an to use the map on the readout. But in my case this doesn't seem to be possible.

I will attach to examples: The first one shows what I described above. The second one is a more complicated example, where the trick doesn't work anymore. And in addition a two dimensional map is required.

Minimal example:

program test

integer, parameter :: n=10,n_k=3
real,target :: a(1:n)
real :: b(1:n_k)
integer :: k(1:n_k)
integer :: j
real,pointer :: p(:)

! fill array a and define map k:
a=[(real(j),j=1,n)]
k=[((j+1)*2,j=1,n_k)]

! can be used to print the arrays:
!write(*,*) a
!write(*,*) k
! can be used to write only the part of a defined by k:
!write(*,*) (a(k(j)),j=1,n_k)

! this an similar things didn't work:
!p(1:n_k) => [(a(k(j)),j=1,n_k)]

! works, but not generally:
p => a
write(*,*) (p(k(j)),j=1,n_k)

! works, only for arrays:
b=(/(a(k(j)),j=1,n_k)/)
write(*,*) b

end program

More complicated (but also kind of minimal) example which shows (hopefully) the problem I really have. For an easy understanding some explanation leads through it. There are plenty of write commands to print the arrays. I appreciate for the amount of code, but I really don't see how to make a shorter and understandable working example:

module mod1

type base
   real :: a
end type

type,extends(base) ::  type1
end type

type,extends(base) :: type2
   type(type1),allocatable :: b(:)
end type

type(type2),allocatable,target :: c(:)

contains

subroutine printer(z)
   class(*),pointer,dimension(:) :: z
   integer :: j,a_z,n_z
   character(len=40) :: f,ff='(F10.2,1x))',form_z

   ! define format for printing:
   a_z=lbound(z,1)
   n_z=ubound(z,1)
   write(f,'(I0)') (n_z-a_z+1)
   form_z="("//trim(adjustl(f))//ff

   ! writing:
   select type(z)
   class is (base)
      write(*,form_z) (z(j)%a,j=a_z,n_z)
   end select
end subroutine

end module

program test

use mod1

integer,parameter :: n_b=8,n_c=6,n_js=3,n_ls=2
integer :: js(1:n_js),ls(1:n_ls)
integer :: j,l
class(*),pointer :: p(:)
character(len=40) :: f,ff='(F10.2,1x))',form_c,form_b

! define format for printing:
write(f,'(I0)') n_b
form_b="("//trim(adjustl(f))//ff
write(f,'(I0)') n_c
form_c="("//trim(adjustl(f))//ff

! creating and filling the arrays:
allocate(c(n_c))
c%a=[(2d0*real(j),j=1,n_c)]
do j=1,n_c
   allocate(c(j)%b(n_b))
   c(j)%b%a=[(real(l)*1d1**(j-1),l=1,n_b)]
end do

! write arrays to compare later:
write(*,form_c) c%a
write(*,*)
write(*,form_b) (c(j)%b%a,j=1,n_c)
write(*,*)

! denfining two maps (size and entries will be input in the final program):
js=[1,4,6]
ls=[2,7]

! using the maps to print only the desired entries:
write(*,*) (c(js(j))%a,j=1,n_js)
write(*,*)
write(*,*) ((c(js(j))%b(ls(l))%a,j=1,n_js),l=1,n_ls)
write(*,*)

! !!! here I want to use the maps as well, but so far I only know how to use ranges:
p => c(1:4)
call printer(p)
write(*,*)
p => c(2)%b(3:6)
call printer(p)
write(*,*)

end program

Edit: Just for the record, I solved the problem now by using arrays of derived types including pointers and slightly changing the calling subroutines.


回答1:


You cannot do this with pointer association (e.g. pointer1 => array1(vector_subscript). Section 7.2.2.2 of the Fortran 2008 standard that disallows this is:

R733 pointer-assignment-stmt is data-pointer-object [ (bounds-spec-list) ] => data-target

There are two other forms, but they do not match your use, nor would they change the outcome. Reading further:

R737 data-target is variable
C724 (R737) A variable shall have either the TARGET or POINTER attribute, and shall not be an array section with a vector subscript.

This is why you cannot perform the pointer association your are attempting. You can however work around this and with pointer allocation. See this code:

n_k = 3
k = [((j+1)*2,j=1,n_k)] ! a vector subscript
p => a(k)               ! NOT OK. Violates C724
allocate(p(n_k))        ! Associate your pointer this way
p = a(k)                ! This is OK. 
write(*,*) p

Which yields (wrapped in your example program):

% ./ptrtest                      
   4.00000000       6.00000000       8.00000000 

This allocates p to be the proper size and then assigns from a with a vector subscript. This gets around the issue of directly associating p with a map of a. This snippet assumes the variables are declared and initialized per your example code. This shows that you can assign a vector subscript of an array to a pointer, but only one that is already associated, not during the association.


As noted in a comment to your Q, if you have a regular stride, you can make the pointer association directly. For your first test case, this would be equivalent and work:

p => a(4:2:8)            ! Allocation to a strided array is allowed

If however, you have an irregular vector subscript then the method in this answer will be what you need to use to accomplish the pointer association.


Another workaround you can use is passing a pointer and the map to a procedure. Consider the following code:

program test
  implicit none

  integer, parameter :: nx = 10, nx_m = 3
  integer,dimension(nx_m) :: x_map
  integer :: i
  real, dimension(nx),target :: a
  real, dimension(:), pointer :: p

! initialize array
  a = [(real(i*2),i=1,10)]
  write (*,'(10(f5.1 x))') a

!define a map
  x_map = [1, 9, 4]

! associate pointer
  p => a

  call print_map(p, x_map)

contains

subroutine print_map(apointer, map)
  implicit none
  real, dimension(:), pointer :: apointer
  integer, dimension(:) :: map

  write (*,*) apointer(map)

   end subroutine print_map
end program test

In this case, p "knows" about a and the map of elements in a can be calculated in the caller. Rather than associating (=>) p as a map of a (which cannot be done), p is associated to a and the map passed along with it.

This code produces the output:

% ./ptrtest3                       
2.0   4.0   6.0   8.0  10.0  12.0  14.0  16.0  18.0  20.0
2.00000000       18.0000000       8.00000000 


来源:https://stackoverflow.com/questions/24310495/pointer-to-subarray-defined-by-a-map

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!