我有一个使用forall
制定的数组赋值。一个数组的索引必须从循环变量计算,所以我尝试以类似ii
的方式使用临时变量这个简单的例子:
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
警告我“带有索引'i'的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
正确地执行此类操作?
现在,正如您可以阅读某些other questions,似乎不再推荐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
版本不支持它。
答案 0 :(得分: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
和最新版本的英特尔Fortran编译器,并且没有发现任何非标准行为。
在Vladimir F的评论之后:
是的,重写的forall
结构可能需要幕后的临时数组,但我相信我所写的内容在语法上是正确的,并且在语义上等同于OP的原始代码。关于do concurrent
构造的合法性,以下
do concurrent (i=1:20)
b(i) = a((i+1)/2)
end do
编译并执行得很好。同样,我认为这是符合标准的代码和行为。
答案 1 :(得分: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
和其他全数组赋值背后的想法是编译器会知道如何更好地优化整个事物,但在实践中(好吧,至少从上次我亲自检查过),它通常会导致代码较慢。
编辑:
只是想提一下,直接将计算出的整数分配给它可能会更好 b中的数组值,而不是通过使用:
再次从内存中获取相同的内容do i=1,20
b(i)=(i+1)/2
end do
但我希望这只是因为这个例子过于简化了? :)
答案 2 :(得分:2)
正如Mark指出的那样,在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