屏蔽 Fortran 数组的更好方法?

Better way to mask a Fortran array?

我想屏蔽 Fortran 数组。这是我目前正在做的方式...

where (my_array <=15.0)
    mask_array = 1
elsewhere
    mask_array = 0
end where

然后我得到我的掩码数组:

masked = my_array * mask_array

有更简洁的方法吗?

使用 MERGE 内部函数:

masked = my_array * merge(1,0,my_array<=15.0)

或者,坚持 where

masked = 0
where (my_array <=15.0) masked = my_array

我预计 where 的使用和 merge 的使用在速度和内存消耗方面存在差异,但我不知道它们是什么是。

这里已经给出了两种不同的方法:一种是保留 and one using 。首先,High Performance Mark 提到在速度和内存使用方面可能存在差异(考虑临时数组)。我将指出另一个潜在的考虑因素(不进行价值判断)。

subroutine work_with_masked_where(my_array)
  real, intent(in) :: my_array(:)
  real, allocatable :: masked(:)

  allocate(masked(SIZE(my_array)), source=0.)
  where (my_array <=15.0) masked = my_array
  ! ...
 end subroutine

subroutine work_with_masked_merge(my_array)
  real, intent(in) :: my_array(:)
  real, allocatable :: masked(:)

  masked = MERGE(my_array, 0., my_array<=15.)
  ! ...
end subroutine

merge方案可以使用自动分配。当然,有时人们不希望这样做(例如当处理大量相同大小的 my_array 时:在这些情况下检查数组大小时通常会产生开销):使用 masked(:) = MERGE(...) 处理分配后(这甚至可能与问题代码相关)。

我发现定义一个函数 where 很有用,它采用 logical 数组和 returns integer 值的 .true. 索引, 例如

x = where([.true., .false., .false., .true.]) ! sets `x` to [1, 4].

这个函数可以定义为

function where(input) result(output)
  logical, intent(in) :: input(:)
  integer, allocatable :: output(:)
  
  integer :: i
  
  output = pack([(i, i=1, size(input))], input)
end function

有了这个where功能,你的问题就可以解决

my_array(where(my_array>15.0)) = 0

这可能不是执行此操作的最高效方法,但我认为它非常可读且简洁。这个 where 函数也可以比 where 内在函数更灵活,因为它可以用于例如对于多维数组的特定维度。

限制:

但是请注意(正如@francescalus 指出的那样)这不适用于非 1 索引的数组。这种限制无法轻易避免,因为对此类数组执行比较操作会丢弃索引信息,例如

real :: my_array(-2,2)
integer, allocatable :: indices(:)

my_array(-2:2) = [1,2,3,4,5]
indices = my_array>3
write(*,*) lbound(indices), ubound(indices) ! Prints "1 5".

对于非 1 索引的数组,为了使用此 where 函数,您需要相当丑陋的

my_array(where(my_array>15.0)+lbound(my_array)-1) = 0