映射定义的子数组的指针 [英] Pointer to subarray defined by a map

查看:29
本文介绍了映射定义的子数组的指针的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想定义一个指向子数组的指针.对于简单的范围,可以通过 pointer =>轻松地完成此操作.array(i:j),但我不知道如何针对 k = [k1,k2,k3] 这样的地图执行此操作.如果我要定义另一个数组,则可以使用类似 array2 = [(array1(k(j)),j = 1,size(k,1))] 的循环.但是不可能以类似的方式分配指针( pointer => [(array1(k(j)),j = 1,size(k,1))] )因为rhs该表达式的似乎定义了另一个variabel,然后甚至没有target属性.对于简单的任务,解决此问题的技巧是首先为总数组an分配一个指针,以在读数上使用映射.但就我而言,这似乎是不可能的.

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.

最小示例:

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

仅作记录,我现在通过使用派生类型的数组(包括指针)并略微更改调用子例程来解决该问题.

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

推荐答案

您不能使用指针关联(例如 pointer1 => array1(vector_subscript)进行此操作.Fortran 2008,第7.2.2.2节不允许这样做的标准是:

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 data-pointer-object [( bounds-spec-list )] => 数据目标

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 数据目标 变量
C724(R737)变量必须具有TARGET或POINTER属性,并且不能是带有矢量下标的数组部分.

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 

这会将 p 分配为适当的大小,然后从 a 中分配一个矢量下标.这解决了将 p a 的映射直接关联的问题.此代码段假定根据您的示例代码声明和初始化变量.这表明您可以将数组的向量下标分配给指针,但是只能分配一个已经关联的指针,而不是在关联期间.

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.

如对您的Q的评论中所述,如果您有规律的步幅,则可以直接建立指针关联.对于您的第一个测试用例,这是等效的并且可行:

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

在这种情况下,可以在调用方中计算 p 了解" a 以及 a 中的元素映射.而不是将( => ) p 关联为 a 的映射(无法完成),而是将 p a 关联,并且地图也随之传递.

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.

此代码产生输出:

% ./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 

这篇关于映射定义的子数组的指针的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆