Не один индексированный массив от ассоциированного

Я хотел бы иметь ассоциацию, которая указывает на массив (или его части) и не индексируется. Следующая программа иллюстрирует проблему:

program test_associate
    implicit none(type, external)
    integer, parameter :: N = 10
    integer :: i, A(0 : N - 1)

    A = [(i, i = lbound(A, 1), ubound(A, 1))]
    write(*, *) A(0), A(9)

    associate(B => A(0 : N - 1))
        write(*, *) B(9)    ! This writes 8 but should write 9
    end associate
end program

Я старался

    associate(B(0 : N - 1) => A(0 : N - 1))
        write(*, *) B(9)
    end associate

но это недопустимый синтаксис. (по крайней мере, в моем компиляторе gfortran 9.3)


person mcocdawc    schedule 15.01.2021    source источник


Ответы (2)


Синтаксис

associate (B(0:N-1) => ...)
end associate

недействителен в Фортране: слева от элемента ассоциации должно быть имя. Только с именем (которое здесь будет B) невозможно указать такие свойства, как границы.

Границы ассоциированного массива сущностей (опять же, здесь B) задаются результатами использования LBOUND в правой части (селектор) (Fortran 2018, 11.1.3.3 p.1) :

Нижняя граница каждого параметра является результатом применения встроенной функции LBOUND (16.9.109) к соответствующему параметру селектора.

Ссылочное описание LBOUND объясняет, как в этом случае рассчитывается граница.

Поскольку A(0:N-1) не является целым массивом, LBOUND возвращает 1, поэтому нижняя граница B в этом случае сама равна 1.

Возможно, что нижняя граница B будет чем-то отличным от 1: пусть селектор будет целым массивом. В

associate(B => A)
end associate

B будет иметь нижнюю границу, чем A.

В заключение: ассоциированный объект может иметь нижнюю границу, отличную от 1, но только тогда, когда он связан с целым массивом. В частности, при связывании с частью массива (и это может включать весь массив, например, B => A(:), A(:) не являющийся целым массивом) ассоциированный объект всегда имеет нижнюю границу 1.

Как говорит Владимир Ф. в другом ответе, указатель может иметь границы, контролируемые как часть назначения указателя.

person francescalus    schedule 15.01.2021
comment
Из вашего ответа я вижу, что мой минимальный пример слишком минимален. В моем фактическом коде у меня есть ранг выше 1, и я использую associate для указания на одну строку. Подобно этому утверждению associate(row => A(0 : N, i, j)) В таких случаях мои выражения rhs никогда не представляют собой целый массив, поэтому я всегда буду получать массив ассоциированных сущностей с одним индексом? (Если, конечно, я не использую указатели, как в ответе Владимира.) - person mcocdawc; 15.01.2021
comment
Действительно: невозможно выбрать только по одному измерению из многих и при этом иметь целый массив. В этом случае вы застряли бы с 1-индексацией. Связывание с целым массивом по причинам, отличным от присвоения нового удобного имени для какой-то ужасной ссылки, встречается довольно редко, поэтому, как правило, у вас будет 1-индексация. - person francescalus; 15.01.2021
comment
Вместо использования указателей вместо связанной конструкции вы можете найти call work_on_row(row=A(0:N,i,j),lbound=0) более полезным. Если то, что должно быть в конструкции ассоциирования, усложнено тем, что приходится иметь дело с границами, отличными от значений по умолчанию, то есть аргумент в пользу того, чтобы поместить эту работу в процедуру. - person francescalus; 15.01.2021

Я не думаю, что это возможно. A(0 : N - 1) — это подмассив, это выражение, а не исходный массив. Нижняя граница A(0 : N - 1) равна 1, а не 0.

Можешь попробовать

dimension A(0:9)
print *,lbound(A(0:8))
end

Он напечатает 1.

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

Если вы сопоставитесь с => B, A будет правильно писать 9.

Вы можете использовать указатели, чтобы указать на такие разделы

program test_associate
    implicit none(type, external)
    integer, parameter :: N = 10
    integer, target :: A(0 : N - 1)
    integer, pointer :: B(:)
    integer :: i

    A = [(i, i = lbound(A, 1), ubound(A, 1))]
    write(*, *) A(0), A(9)
    
    B(0:N-1) => A(0:N-1)
    write(*, *) B(9)    ! This writes 9

end program
person Vladimir F    schedule 15.01.2021
comment
Должен ли i быть целью? PS: мне жаль, что я не принял ваш ответ официально, если бы я мог принять оба, я бы с радостью это сделал. - person mcocdawc; 15.01.2021
comment
@mcocdawc Так и есть. Вы можете принять любой ответ, какой захотите. Я не рассматривал причину ошибки компилятора (просто неверный синтаксис), а только проблему с границами и как это сделать. - person Vladimir F; 15.01.2021
comment
Но на i никогда не указывает какой-либо указатель, и он компилируется как минимум с gfortran и всеми включенными предупреждениями, если i не является целью. - person mcocdawc; 15.01.2021
comment
@mcocdawc Конечно, я не проверял имя символа, о котором вы спрашивали. Указанный массив должен быть целевым. Ничего больше. Возможно мое быстрое изменение вашего кода вас смутило. - person Vladimir F; 15.01.2021