Выделить динамический массив с взаимозависимыми размерами - PullRequest
5 голосов
/ 19 декабря 2011

Это немного сложно; Я бы приветствовал любые комментарии о том, как улучшить ясность вопроса.

Хорошо, скажите, у меня есть массив:

real, allocatable :: A(:,:,:)

и я хочу выделить его, прежде чем использовать его. Возможно ли, чтобы размер третьего измерения зависел от размера второго измерения?

1008 * Е.Г. *

do i=1,n
allocate(A(3,i,i**2))
end do

Очевидно, что вышеупомянутое не работает. Я хотел бы закончить массив (или набор массивов) с формой (ами)

(3,1,1), (3,2,4), (3,3,9), ... (3, n, n^2)

где размер третьего измерения - это квадрат размера второго измерения.

Мое правило для размера зависимого измерения немного сложнее, но если возможно возведение в квадрат, я могу сделать все остальное.

Возможно ли это? Если так, как я могу реализовать это в Фортране?

Что вернет shape(A)? Это было бы интересно.

Моя другая альтернатива состоит в том, чтобы выделить максимальный требуемый размер, и будьте осторожны, чтобы использовать только определенные элементы в вычислениях, т.е.

allocate(A(3,n,n**2))

Несмотря на то, что в данный момент у меня нет проблем с памятью, я бы хотел иметь хорошие методы программирования. В любом случае, это интересная проблема.

Спасибо.

EDIT:

Как насчет того, чтобы размер измерения зависел от значения элемента в другом измерении?

В приведенном ниже ответе размер массива в обоих измерениях зависел от индекса B. Я хотел бы что-то вроде

type myarray
    real :: coord(3)
    integer,allocatable :: lev(:)
    integer, allocatable :: cell(:)
endtype myarray

type(myarray), allocatable :: data

allocate(data(m))
allocate(data%lev(n))

forall (j=1:n) !simple now, for argument's sake
    lev(j)=j
endforall

! I was thinking of using a FORALL loop here, but the errors returned 
! suggested that the compiler (gfortran) didn't expect IF blocks and ALLOCATE 
! statements in a FORALL block
do i=1,m
    do j=1,n
        allocate(data(i)%cell(lev(j)**2))
    enddo
enddo

Вы понимаете, о чем я? Но программа падает, когда пытается выделить уже выделенные переменные, например, когда i=1 выделяет data(1)%cell(1), а затем пытается выделить data(1)%cell(2) ... э-э-э Я хочу что-то вроде:

Каждый data(i) имеет массив lev(j) значений, с j, работающим от 1 до n, и для каждого lev(j) значения мы имеем cell размера lev ^ 2. Обратите внимание, что эти cell уникальны для каждого data(i) и каждого lev, и что размер этого конкретного cell зависит от соответствующего значения lev и, возможно, от соответствующего data(i).

Должен ли я использовать производный тип в производном типе?

Ответы [ 2 ]

9 голосов
/ 19 декабря 2011

Да, вы можете использовать производный тип для этого:

TYPE array
  REAL,DIMENSION(:,:,:),ALLOCATABLE :: A
ENDTYPE array

INTEGER :: i
INTEGER,PARAMETER :: n=10

TYPE(array),DIMENSION(:),ALLOCATABLE :: B

ALLOCATE(B(n))

DO i=1,n
  ALLOCATE(B(i)%A(3,i,i*i))
  WRITE(*,*)SHAPE(B(i)%A)
ENDDO

END

Этот подход позволяет каждому элементу массива B быть многомерным массивом различной формы.

Вывод программы соответствует ожидаемому:

        3            1            1
        3            2            4
        3            3            9
        3            4           16
        3            5           25
        3            6           36
        3            7           49
        3            8           64
        3            9           81
        3           10          100

РЕДАКТИРОВАТЬ: Для дальнейшего ответа на отредактированный вопрос OP. Да, похоже, вам нужно сделать что-то вроде этого, использовать вложенный производный тип (сравните с примером кода, чтобы выяснить, что вы сделали неправильно):

* * 1010

Попробовал это с gfortran 4.6.2. Выдает ожидаемый результат:

       1           1           1
       2           2           4
       3           3           9
       4           4          16
       5           5          25
1 голос
/ 09 апреля 2016

Я думаю, что вы можете сделать это просто выделив / освободив массив

Program test
    Implicit none
    Real, dimension(:,:,:), allocatable :: A
    Integer  :: i,N
    Write(*,*)"Enter N"; Read(*,*)N
    Do i = 1, N
    if(Allocated(A)) then 
    deallocate(A);Allocate(A(i,i,i*i))
    else
    allocate(A(i,i,i*i))
    end if
    Write(*,*)Shape(A)
    End do
end program test

Компиляция программы с использованием gfortran дает:

 Enter N
5
           1           1           1
           2           2           4
           3           3           9
           4           4          16
           5           5          25
...