2013-09-11 3 views
0

У меня есть назначение массива, которое я сформулировал с помощью forall. Индексы в одном массиве должны быть вычислены из переменных цикла, так что я пытался использовать временные переменные, аналогично тому ii этот простой пример:Временные переменные внутри Fortran `forall` или` do concurrent`

program test 
    integer :: i, ii, a(10), b(20) 
    a=(/(i, i=1,10)/) 

    forall (i=1:20) 
    ii = (i+1)/2 

    b(i) = a(ii) 
    end forall 

    print '(20I3)', b 
end program test 

Но gfortran предупреждает меня, что «FORALL с индексом ' я»не используется на левой стороне задания на (1), и поэтому может вызвать многократное назначение к этому объекту», и на самом деле выход не то, что я хотел:

10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10

(ifort даже бросает ошибка.) Я понимаю, что могу просто повторять определение ii каждый раз, когда оно появляется, но это может утомиться в реальных случаях.

Q1: Есть ли способ сделать это, используя forall?


Теперь, как вы можете прочитать в некоторых otherquestions, forall больше не кажется, рекомендуется, и прямой do может быть даже быстрее.

Тем не менее, мое использование forall в этом случае было таким же удобным, как и скорость. В моей реальной программе у меня есть что-то вроде forall (i=1:10, j=1:10, i<=j), и мне нравится, как заголовок forall позволяет мне указать это без вложенных циклов.

Я также узнал о do concurrent в этих вопросах, и это, кажется, решить эту проблему, то есть замена forall выше do concurrent, выход становится:

1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10 10

и я все еще получаю использовать forall заголовок ,

Q2: ли это использование локальной переменной, которая не работает в forall, гарантированное стандартом для работы в do concurrent? (Вместо того, что это случайность, что он работал в gfortran.)

К сожалению, на данный момент я не могу использовать do concurrent в любом случае, потому что немного старше ifort и gfortran версии не поддерживают его.

ответ

2

Как указывает Марк, определение ii внутри конструкции forall не нужно, так как вы можете использовать a((i+1)/2) в своем определении. В качестве еще одной альтернативы, если вы полностью преданы используя forall, вы можете использовать pure function (или elemental function), чтобы установить показатели:

program forall_test 
    ... 
    forall(i=1:20) 
    b(i) = a(set(i)) 
    end forall 

contains 
    pure function set(i) 
    integer, intent(in) :: i 
    integer :: set 
    set = (i+1)/2 
    end function set 
end program 

Используя это с a=(/1, 2, 3, 4, 5, 6, 7, 8, 9, 10/), я получаю в качестве выходного

1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10 10 
+1

выскочил, самое смешное расширение сделать что-то просто сложное :) Конечно, в более реальном примере это может быть приятным прикосновением. – steabert

+0

@steabert: если кто-то хочет * намеренно * ограничивают их способность писать хороший код с помощью «более элегантного» рабочего процесса, а затем делать что-то сложное необходимо :) - Трудно сказать, если бы индексы были немного сложнее (например, с участием 'if's) , это, вероятно, действительно хороший способ сделать это (предполагая, конечно, что вы все еще ограничиваете себя «forall»). –

+0

Спасибо, это реальный ответ на мой вопрос :-) – xebtl

3

Для случая в вашем вопросе я не вижу необходимости для скалярного ii на всех, вы могли бы заменить

forall (i=1:20) 
    ii = (i+1)/2 

    b(i) = a(ii) 
    end forall 

с

forall (i=1:20) 
    b(i) = a((i+1)/2) 
    end forall 

, которая производит результат, который вы хотите, в Это дело. Что касается ваших вопросов:

A1: Да, как показано на рисунке.

A2:do concurrent является стандартной функцией в Fortran 2008, поэтому гарантируется работа; гарантируется, если у вас есть современный компилятор и этот компилятор был правильно реализован. До сих пор я использовал только do concurrent с недавней версией компилятора Intel Fortran и не нашел нестандартного поведения.

После Владимира F Замечание:

Да, переписаны forall конструкции может потребовать временный массив за кулисами, но я считаю, что то, что я написал синтаксический правильно и семантический эквивалентно исходный код ФПА в. Что касается законности do concurrent конструкции, следующий

do concurrent (i=1:20) 
    b(i) = a((i+1)/2) 
end do 

компилирует и выполняет очень хорошо. Опять же, я думаю, что это стандартно-совместимый код и поведение.

+0

Примечание. Это решение 'forall' потребует временного массива, но, вероятно, это не проблема. Для 'do concurrent' это было бы, вероятно, незаконным. Но в остальном +1 –

+0

@Mark Ad 1: Это то, чего я пытался избежать. Конечно, это просто вопрос удобства и удобочитаемости. Объявление 2: я отредактировал вопрос, чтобы быть более явным в этом вопросе. (Я полагаю, ваш ответ будет «да»). – xebtl

+0

@VladimirF: Почему это решение было бы незаконным в 'do concurrent'? – xebtl

2

Вы, кажется, использует forall, где я бы естественно использовать do цикл:

do i=1,20 
    ii=(i+1)/2 
    b(i)=a(ii) 
end do 

forall утверждения особенно полезно для условных острот, где вы хотите применить маску для присвоения целого массива , но даже тогда я лично предпочитаю петли do.

Ваш пример forall (i=1:10, j=1:10, i<=10) также не имеет большого смысла, поскольку окончательное выражение не маскирует никаких значений.

Идея, лежащая в основе forall и других назначений целых массивов, заключается в том, что компилятор будет знать, как оптимизировать все это лучше, но на практике (ну, по крайней мере, с тех пор, как я проверил лично), это обычно приводит к более медленному коду.

EDIT:

просто хотел бы отметить, что это, вероятно, лучше, чтобы непосредственно присвоить вычисленное целое число значение массива в б, вместо извлечения то же самое из памяти снова через использование:

do i=1,20 
    b(i)=(i+1)/2 
end do 

, но я надеюсь, что это просто потому, что пример чрезмерно упрощен? :)

+0

Что касается маски, я хотел написать 'forall (i = 1: 10, j = 1: 10, i <= j)'; Я исправил это в вопросе. В этой и примерной программе я попытался свести свою точку зрения на короткий, простой пример - извините, если я упростил. Что касается цикла do: Я знаю, но я считаю «foreach» более элегантным (см. Оригинальный вопрос). – xebtl

+1

До сегодняшнего дня я никогда не думал, что кто-нибудь подумает, что конструкция 'forall' будет« более элегантной », чем« do' loop. –

+0

@KyleKanos: Ну, вы можете видеть, что я не настоящий программист Фортран :-). Суть в том, что 'forall' может выражать две (или более) вложенные петли в одной строке. – xebtl

Смежные вопросы