Fortran – then versus now

If you don’t quite understand the difference between old and new Fortran, consider the following program. This (slightly modified) program comes from a 1968 book titled “Fortran Programming”, by Fredric Stuart. Likely written in Fortran IV, it basically just counts words and sentences. The program reads the standard input (via a redirected file), storing each of the 80 characters in an array, and then examines each one by one using a loop. The first comparison tests for the end-of-text terminator “/”. The next comparison checks for a period and increases the sentence count. The third comparison checks for black characters and increments the word count. The code isn’t exactly perfect as it will count every blank as a though a word appeared before it.

C     WORD AND SENTENCE COUNT
      DIMENSION LETT(80)
      DATA LPER, LBLANK, LSLASH/1H.,1H ,1H//,NWORD, NSENT/0,0/
1     READ(*,10) LETT
      DO 6 K = 1,80
      IF (LETT(K)-LSLASH) 2,7,2
2     IF (LETT(K)-LPER) 4,3,4
3     NSENT = NSENT + 1
      GO TO 6
4     IF (LETT(K)-LBLANK) 6,5,6
5     NWORD = NWORD + 1
      write(*,*) k, " "
6     CONTINUE
      GO TO 1
7     WRITE(*,11) NWORD, NSENT
10    FORMAT (80A1)
11    FORMAT (I16,' WORDS',10X,I16,' SENTENCES')
      CALL EXIT
      END

Now if we feed this program the following piece of text:

Cooking is a craft, I like to think, and a good cook is a craftsman -- not an
artist. There's nothing wrong with that: The great cathedrals of Europe were
built by craftsmen -- though not designed by them. Practicing your craft in
expert fashion is noble, honorable, and satisfying. /

We get:

              59 words                         3 sentences

Which isn’t exactly true. Maybe the original program ran on a specific type of text? Count the words and we get 48. So why is the program not working? Because it increments the word counter when a blank character is encountered. So three blank characters at the end of a sentence implies 3 words are counted. It will work properly with simple sentences only containing periods as punctuation. So when this program gets compiled in a modern Fortran compiler, it will choke of a series of deleted or less-than-palatable features – although it will compile the program. Let’s first morph this program to a more modern Fortran 2003 (F03) version:

!  Word and sentence count
   program wscount
   implicit none
   character, dimension(80) :: lett
   integer :: k, nword=0, nsent=0

10 format (80A1)
11 format (I16,' words',10X,I16,' sentences')

mloop: do
      read(*,10) lett
      do k = 1, 80
         if (lett(k) .eq. '/') then
            exit mloop
         elseif (lett(k) .eq. '.') then
            nsent = nsent + 1
         elseif (lett(k) .eq. ' ') then
            nword = nword + 1
         end if
      end do
   end do mloop
   write(*,11) nword, nsent

end program wscount

This updated version replaces the goto with a loop, rationalizes the do loop, and replaces the three arithmetic if statements with a nested if statement. The Hollerith constants (in the DATA statement) representing the slash, period and blank are replaced with characters within the logic statements, and all the variables are declared. The new program works exactly the same as the old one, i.e. it gives the same incorrect answer. So to fix that we have to add some functionality to deal with counting the words. Here is the modified program:

!  Word and sentence count

program wscount
      implicit none
      character, dimension(80) :: lett
      integer :: k, nword=0, nsent=0

10    format (80a)
11    format (I16,' words',10X,I16,' sentences')

mloop: do
         read(*,10) lett
         do k = 1, 80
            if (lett(k) .eq. '/') then
               exit mloop
            elseif (lett(k) .eq. '.') then
               nsent = nsent + 1
            elseif (lett(k) .eq. ' ' .and. (is_alpha(lett(k-1)) &
                    .or. is_punct(lett(k-1)))) then
               nword = nword + 1
            end if
         end do
      end do mloop
      write(*,11) nword, nsent

contains

   pure logical function is_alpha(c)
      character(len=1), intent(in) :: c
      is_alpha = (c >= 'a' .and. c <= 'z') &
                 .or. (c >= 'A' .and. c <= 'Z')
   end function

   pure logical function is_punct(c)
      character(len=1), intent(in) :: c
      is_punct = (c == ',') .or. (c == '.') .or. (c == '?') &
                 .or. (c == '!') .or. (c == ":") .or. (c == ';')
   end function

end program wscount

The program now checks for a blank, and also if the character before the blank is a character (a-z, A-Z), or a punctuation mark (, . ? ! : ;). This requires the addition of two new functions is_alpha(), and is_punct(). Now when run it gives the correct count for words. Of course this snippet of a program will still not work on everything. What happens if a sentence has a real number in it, like 3.14159? It will count as an additional sentence.

But the point of this post is not really to illustrate how well the program works, but rather the difference between a Fortran program written in 1968 and one written in 2022. They are vastly different languages, with F03 having non of the previous spaghetti-like idiosyncrasies of its predecessors.

Advertisement