12
\$\begingroup\$

I do most of my programming in C/C++ and Perl, but I currently learning Fortran. I began by coding up a simple program, something that took me < 5 minutes in Perl (see this thread from BioStar). After working for several hours on a solution in Fortran 95 (teaching myself as I go), I came to this solution.

implicit none

! Variable definitions
integer                 :: argc
integer                 :: num_digits
integer                 :: i, j
integer                 :: iocode
integer                 :: line_length
integer                 :: value
character ( len=256 )   :: infile
character ( len=16 )    :: int_format
character ( len=16 )    :: int_string
character ( len=2048 )  :: line

! Verify and parse command line arguments
argc = iargc()
if( argc < 1 ) then
  write(*, '(A, I0, A)') "Error: please provide file name (argc=", argc, ")"
  stop
endif
call getarg(1, infile)

! Open input file, croak if there is an issue
open( unit=1, file=infile, action='read', iostat=iocode )
if( iocode > 0 ) then
  write(*, '(A, A, A)') "Error: unable to open input file '", trim(infile), "'"
  stop
endif

! Process the file, print in CSV format
do while(1 == 1)
  ! Read the line, skip if it is empty
  100 read( unit=1, fmt='(A)', end=200 ) line
  line_length = len_trim(line)
  if( line_length == 0 ) then
    goto 100
  endif

  ! The first value in the line is a string
  ! Find string boundaries and print it out
  i = 0
  do while( line(i:i) == ' ' )
    i = i+1
  end do
  j = i
  do while( line(j:j) /= ' ' )
    j = j+1
  end do
  write(*, '(A)', advance="no") line(i:j-1)

  ! Now grab the rest of the integer values
  ! on the line, multiply them by 3, and print
  i = j
  j = 0
  do while( i < line_length)
    do while( line(i:i) == ' ' )
      i = i+1
    end do
    j = i
    do while( j < line_length .and. line(j:j) /= ' ' )
      j = j+1
    end do
    int_string = line(i:j-1)
    read(int_string, '(I)') value
    value = value*3
    write(*, '(A, I0)', advance="no") ",", value
    i = j
    j = 0
  end do

  print *
end do
200 close( 1 )

end program

There is a lot that I don't like about how I've written this program, but it's hard to separate my inexperience with the new syntax from bad practice. One thing in particular that I don't like is my use of labels and the infamous goto statement. I'm interested in any feedback whatsoever, but I'm particularly interested in better ways to handle the control structure of the program (without using the goto statement and end parameter in the read function if possible).

The compilers I have access to only support features through f95.

\$\endgroup\$
7
\$\begingroup\$

You could get rid of the GOTO statements by using labelled loops with EXIT and CYCLE statements. These allow you to do the sort of flow control that you've used the GOTOs for, but don't permit the less desirable features, such as computed GOTOs. Using these newer Fortran features also gives you a clearer idea of the flow control at the point at which the branching occurs. E.g., CYCLE or EXIT statements send you respectively further up or down the source code, and a well chosen label may indicate what the branching is trying to achieve.

Another suggestion, although this is more of a personal preference, is to avoid the potentially endless DO WHILE(1 == 1) loop by using a normal DO loop with a large upper limit and adding warning message if that limit is reached before the end of file is encountered. Plenty of people might find that overly fussy though.

Example code showing these two points:

PROGRAM so_goto

  IMPLICIT NONE

  INTEGER,PARAMETER :: line_max = 100000

  INTEGER :: i, ios, line_length
  CHARACTER( len=2048 ) :: line

  OPEN(UNIT=10,FILE="so_goto.txt",ACTION="read")

  fread: DO i=1,line_max
    READ(UNIT=10,FMT='(A)',IOSTAT=ios) line
    IF( ios < 0 ) EXIT fread
    line_length = LEN_TRIM(line)
    IF( line_length == 0 ) CYCLE fread

    IF( i == line_max ) THEN
      print*,"BTW, you've hit your read limit before the end of the file."
    ENDIF
  ENDDO fread
  CLOSE(10)

END PROGRAM so_goto
\$\endgroup\$
  • \$\begingroup\$ +1 I much prefer the EXIT/CYCLE syntax to the GOTO statements. It makes the intent of the statements much more explicit. \$\endgroup\$ – Daniel Standage Jan 27 '11 at 14:25
  • 1
    \$\begingroup\$ Either use a normal do or if an endless loop, than just do is enough. The while part is superfluous and should be only used when a real condition is needed. \$\endgroup\$ – Vladimir F Sep 29 '12 at 20:38
2
\$\begingroup\$

I've seen some pretty nasty Fortran in my time: Research departments seem to be the worse source! Your code is far, far superior to their code!

In those days we were using an extended form of Fortran 77 with some structured extensions and formatting niceties. There was no avoiding GOTOs and FORMATs and their labels. I tried to keep them to a minimum and to only use them for break/cycle types of jumps like you are doing. So although I lack modern Fortran experience, you look to be on the right track.

\$\endgroup\$
  • 1
    \$\begingroup\$ Thanks for the reassurance! I had to chuckle a little when at your mention of "modern Fortran experience" though. :) Being on the cutting edge is definitely NOT why I'm learning Fortran! \$\endgroup\$ – Daniel Standage Jan 23 '11 at 4:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.