Существует ли эквивалент Fortran для оператора Python for-else?

Существует ли эквивалент Fortran для оператора Python for-else?

Например, следующий пример сортирует список чисел по разным диапазонам. В Питоне это:

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

В Фортране это работает так же:

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 и заменить его чем-то вроде else в Python?


person qAp    schedule 01.12.2016    source источник
comment
Python с NumPy и Fortran очень похожи с точки зрения выразительности и возможностей. Этот розеттский камень показывает, как использовать множество общих идиом в обоих языках одновременно. fortran90.org/src/rosetta.html   -  person jlokimlin    schedule 01.12.2016
comment
Этот вопрос должен быть связан с эквивалентным вопросом для C++, тем более что и Fortran, и C++ компилируются, языки OO, поэтому предлагаемые решения похожи.   -  person knia    schedule 29.03.2018


Ответы (6)


Прямого эквивалента этой конструкции Python нет.

Обратите внимание, однако, что досрочное завершение цикла do с помощью элемента управления counting loop можно обнаружить, проверив значение переменной 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

Оператор выхода также может выходить за пределы цикла 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
person IanH    schedule 01.12.2016

Если вопрос касается именно объединения в бины ряда чисел, где 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

затем, когда я был счастлив, что логика была правильной, я превращал ее в функцию и добавлял некоторую проверку ошибок.

Если вопрос более общий и спрашивает, каков идиоматический способ Fortran (post 90) для воспроизведения структуры for-else Python, здесь также есть ответы на этот вопрос.

person High Performance Mark    schedule 01.12.2016

Операторы GO TO допускают произвольные переходы. В частности, вы пишете свой цикл for, за которым следует блок else, за которым следует помеченное продолжение. В цикле, если условие истинно, перейти к отмеченному продолжению. В противном случае цикл 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
person Neapolitan    schedule 01.12.2016
comment
Иногда GOTO имеет смысл. Есть разница между их использованием вместо EXIT и CYCLE. Но случайное употребление - не самый большой грех в мире - person Holmz; 02.12.2016
comment
И если вы стряхнете пыль со своего эмулятора VAX, вы сможете запустить его, ничего не меняя! - person Neapolitan; 02.12.2016

Поскольку часть else блока for-else Python выполняется только тогда, когда все элементы обработаны, как насчет того, чтобы просто использовать оператор 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: «Выход практически из любой конструкции» в Объяснении современного Фортрана (автор Меткалф и др.), а также в стандартах F2008 Chap.8.1.10 (получено из здесь), можно выйти из любой помеченной конструкции, такой как block, if, associate и т. д., но нам может понадобиться относительно новый компилятор (у меня работал gfortran-6). Справочная страница IBM для выход также полезен.

person roygvib    schedule 01.12.2016
comment
Большое спасибо, я подтвердил документы. Хотя до сих пор я никогда не использовал do concurrent или Critical, я проверю использование позже :) - person roygvib; 01.12.2016

Насколько мне известно, Python — единственный (или один из очень немногих) языков, в которых есть оператор for-else. Нет, в Фортране его нет.

person DYZ    schedule 01.12.2016
comment
Итак, чем WHERE ELSEWHERE отличается от python for else? - person Holmz; 02.12.2016
comment
Python else выполняется после цикла, если цикл не прервался. elsewhere, насколько я понимаю, выполняется в цикле на каждой итерации. - person DYZ; 02.12.2016

Иногда GOTO полезен. A 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))
person Holmz    schedule 01.12.2016