【问题标题】:Is there a Fortran equivalent of Python's for-else statement?是否有与 Python 的 for-else 语句等效的 Fortran 语句?
【发布时间】:2017-04-15 15:28:26
【问题描述】:

是否有与 Python 的 for-else 语句等效的 Fortran 语句?

例如,下面将一个数字列表排序到不同的范围内。在 Python 中是:

absth = [1, 2, 3, 4, 5]
vals = [.1, .2, .5, 1.2, 3.5, 3.7, 16.8, 19.8, 135.60]


counts = [0] * len(absth)
for v in vals:
    for i, a in enumerate(absth):
        if v < a:
            counts[i] += 1
            break
    else:
        counts[-1] += 1

在 Fortran 中,这也是一样的:

do iv = 1, nvals

  is_in_last_absth = .true.

  do ia = 1, nabsth - 1
    if vals(iv) < absth(ia) then
      counts(ia) = counts(ia) + 1
      is_in_last_absth = .false.
      exit
    end if
  end do

  if (is_in_last_absth) then
    counts(nabsth) = counts(nabsth) + 1
  end if

end do

但是有没有办法不必使用 is_in_last_absth 并将其替换为 Python 中的 else 之类的东西?

【问题讨论】:

  • Python 与 NumPy 和 Fortran 在表现力和功能方面非常相似。这颗罗塞塔石碑展示了如何同时在两种语言中实现许多常见的习语。 fortran90.org/src/rosetta.html
  • 这个问题应该和equivalent question for C++联系起来,特别是因为Fortran和C++都是编译的,OO语言,所以建议的解决方案是相似的。

标签: python fortran for-else


【解决方案1】:

有时 GOTO 也不错。 WHERE ELSEWHERE 可能有用...

do iv = 1, nvals

  is_in_last_absth = .true.
  Mask = .FALSE.
  Mask(1:(nabsth - 1)) = .TRUE.)
  Mask2 = .FALSE.
  WHERE(MASK)
    WHERE( vals(iv) < absth)
      mask2 = .TRUE.
    ENDWHERE

    WHERE(Mask2)
      Count = Count + 1
    ELSE
      LastCount = LastCount + 1
    ENDWHERE

  END WHERE
end do

count(2:(n-1)) = count(2:(n-1))+ lastcount(1:(n))

【讨论】:

    【解决方案2】:

    由于 Python 的 for-else 块中的 else 部分仅在处理完所有元素后才执行,那么简单地对最后一个元素使用 if 语句怎么样?例如,

    program main
        implicit none
        integer i, n
        print *, "n = ?" ; read(*,*) n
    
        do i = 1, 10
            if ( i <= n ) then
                print *, i
            else
                exit
            endif
            if ( i == 10 ) print *, "reached the final index"
        enddo
    
        print *, "done"
    end program
    

    大概对应

    n = int( input( "n = ? \n" ) )
    
    for i in range( 1, 11 ):
        if i <= n:
            print( i )
        else:
            break
    else:
        print( "reached the final index" )
    
    print( "done" )
    

    另一种方法可能是使用带标签的block 构造,例如:

    program main
        implicit none
        integer i, n
        print *, "n = ?" ; read(*,*) n
    
        loop_i : block
    
          do i = 1, 10
              if ( i <= n ) then
                  print *, i
              else
                  exit loop_i
              endif
          enddo
          print *, "reached the final index"
    
        endblock loop_i
    
        print *, "done"
    end program
    

    根据第 20.1.7 章:Modern Fortran Explained(Metcalf 等人)以及 F2008 标准 Chap.8.1.10(从 here 获得)中的“从几乎任何构造中退出” ,可以退出任何带标签的结构,如block、if、associate 等,但我们可能需要一个相对较新的编译器(gfortran-6 对我有用)。 exit 的 IBM 手册页也很有用。

    【讨论】:

    • 非常感谢,我已经确认了文件。虽然到目前为止我从未使用过“do concurrent”或“critical”,但我稍后会检查用法:)
    【解决方案3】:

    如果问题是关于对一系列数字进行分箱,absth 是每个分箱的上限(最后一个没有上限),那么我可能会这样写:

    PROGRAM test
    
      IMPLICIT NONE
    
      INTEGER :: ix   
      INTEGER, DIMENSION(5) :: absth = [1, 2, 3, 4, 5]   
      REAL, DIMENSION(9) :: vals = [.1, .2, .5, 1.2, 3.5, 3.7, 16.8, 19.8, 135.60]   
      INTEGER, DIMENSION(SIZE(absth)+1) :: bins
    
      bins = 0
    
      DO ix = 1, SIZE(bins)-1
         bins(ix) = COUNT(vals<absth(ix))   
      END DO   
      bins(ix) = COUNT(vals)
    
      bins = bins-EOSHIFT(bins,-1)
      WRITE(*,*) 'bins = ', bins
      ! which writes  3  1  0  2  0  3
    
    END PROGRAM test
    

    然后,当我对逻辑正确感到高兴时,我会将其变成一个函数并添加一些错误检查。

    如果问题更笼统,并询问重现 Python 的 for-else 结构的惯用 Fortran(90 后)方法是什么,这里也有答案。

    【讨论】:

      【解决方案4】:

      没有直接等效于该 python 构造。

      但请注意,可以通过检查循环后 do 变量的值来检测带有计数循环控制的 do 循环的提前终止。

      do iv = 1, nvals
        do ia = 1, nabsth - 1
          if (vals(iv) < absth(ia)) then
            counts(ia) = counts(ia) + 1
            exit
          end if
        end do
      
        ! If the loop terminates because it completes the iteration 
        ! count (and not because the exit statement is executed) then 
        ! the do variable is one step beyond the value it had 
        ! during the last iteration.
        if (ia == nabsth) then
          counts(nabsth) = counts(nabsth) + 1
        end if
      end do
      

      exit 语句不仅可以跳出 do 循环:

      do iv = 1, nvals
        outer_block: block
          do ia = 1, nabsth - 1
            if (vals(iv) < absth(ia)) then
              counts(ia) = counts(ia) + 1
              exit outer_block
            end if
          end do
      
          counts(nabsth) = counts(nabsth) + 1
        end block outer_block
      end do
      

      并且循环语句可以循环该语句嵌套在其中的任何 do 构造:

      outer_loop: do iv = 1, nvals
        do ia = 1, nabsth - 1
          if (vals(iv) < absth(ia)) then
            counts(ia) = counts(ia) + 1
            cycle outer_loop
          end if
        end do
      
        counts(nabsth) = counts(nabsth) + 1
      end do outer_loop
      

      【讨论】:

        【解决方案5】:

        GO TO 语句允许任意跳转。特别是,您编写了 for 循环,然后是 else 块,然后是标记的 continue。在循环内,如果条件为真,则跳转到标记的继续。否则 for 循环将正常终止,else 块将被执行然后继续,完全匹配 python 的 for...else 构造的语义。

        例如:

                INTEGER nabsth, nvals
                PARAMETER (nabsth=5, nvals=9)
                INTEGER absth(nabsth), counts(nabsth)
                REAL vals(nvals)
                DATA absth/1, 2, 3, 4, 5/
                DATA counts/0, 0, 0, 0, 0/
                DATA vals/.1, .2, .5, 1.2, 3.5, 3.7, 16.8, 19.8, 135.60/
        
                do iv = 1, nvals
        
                  do ia = 1, nabsth - 1
                    if (vals(iv) < absth(ia)) then
                      counts(ia) = counts(ia) + 1
                      goto 10
                    end if
                  end do
                  counts(nabsth) = counts(nabsth) + 1
        10        continue
                end do
                WRITE (*,*), counts
                end
        

        生产

               3           1           0           2           3
        

        【讨论】:

        • 有时 GOTO 是值得的。在 placeEXIT 和 CYCLE 中使用它们是有区别的。但偶尔使用并不是世界上最大的罪过
        • 如果你把你的 VAX 模拟器掸掉,你就可以在不改变任何东西的情况下运行它!
        【解决方案6】:

        据我所知,Python 是唯一(或极少数)具有 for-else 语句的语言。不,Fortran 没有。

        【讨论】:

        • 那么 WHERE ELSEWHERE 与 python 的“for else”有何不同?
        • 如果循环没有中断,Python 的else 在循环之后执行。 elsewhere,据我所知,在每次迭代时都在循环中执行。
        猜你喜欢
        • 1970-01-01
        • 2022-06-30
        • 2012-01-10
        • 2023-04-02
        • 2012-12-01
        • 1970-01-01
        • 2019-11-24
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多