Task: Word frequency








VB: Option Explicit

Private Const PATHFILE As String = "C:\HOME\VBA\ROSETTA"

Sub Main()
Dim arr
Dim Dict As Object
Dim Book As String, temp As String
Dim T#
T = Timer
   Book = ExtractTxt(PATHFILE & "\les miserables.txt")
   temp = RemovePunctuation(Book)
   temp = UCase(temp)
   arr = Split(temp, " ")
   Set Dict = CreateObject("Scripting.Dictionary")
   FillDictionary Dict, arr
   Erase arr
   SortDictByFreq Dict, arr
   DisplayTheTopMostUsedWords arr, 10

Debug.Print "Words different in this book : " & Dict.Count
Debug.Print "-------------------------"
Debug.Print ""
Debug.Print "Optionally : "
Debug.Print "Frequency of the word MISERABLE : " & DisplayFrequencyOf("MISERABLE", Dict)
Debug.Print "Frequency of the word DISASTER : " & DisplayFrequencyOf("DISASTER", Dict)
Debug.Print "Frequency of the word ROSETTA_CODE : " & DisplayFrequencyOf("ROSETTA_CODE", Dict)
Debug.Print "-------------------------"
Debug.Print "Execution Time : " & Format(Timer - T, "0.000") & " sec."
End Sub

Private Function ExtractTxt(strFile As String) As String

Dim i As Integer
   i = FreeFile
   Open strFile For Input As #i
       ExtractTxt = Input(LOF(1), #i)
   Close #i
End Function

Private Function RemovePunctuation(strBook As String) As String
Dim T, i As Integer, temp As String
Const PUNCT As String = """,;:!?."
   T = Split(StrConv(PUNCT, vbUnicode), Chr(0))
   temp = strBook
   For i = LBound(T) To UBound(T) - 1
      temp = Replace(temp, T(i), " ")
   Next
   temp = Replace(temp, "--", " ")
   temp = Replace(temp, "...", " ")
   temp = Replace(temp, vbCrLf, " ")
   RemovePunctuation = Replace(temp, "  ", " ")
End Function

Private Sub FillDictionary(d As Object, a As Variant)
Dim L As Long
   For L = LBound(a) To UBound(a)
      If a(L) <> "" Then _
         d(a(L)) = d(a(L)) + 1
   Next
End Sub

Private Sub SortDictByFreq(d As Object, myArr As Variant)
Dim K
Dim L As Long
   ReDim myArr(1 To d.Count, 1 To 2)
   For Each K In d.keys
      L = L + 1
      myArr(L, 1) = K
      myArr(L, 2) = CLng(d(K))
   Next
   SortArray myArr, LBound(myArr), UBound(myArr), 2
End Sub

Private Sub SortArray(a, Le As Long, Ri As Long, Col As Long)
Dim ref As Long, L As Long, r As Long, temp As Variant
   ref = a((Le + Ri) \ 2, Col)
   L = Le
   r = Ri
   Do
         Do While a(L, Col) < ref
            L = L + 1
         Loop
         Do While ref < a(r, Col)
            r = r - 1
         Loop
         If L <= r Then
            temp = a(L, 1)
            a(L, 1) = a(r, 1)
            a(r, 1) = temp
            temp = a(L, 2)
            a(L, 2) = a(r, 2)
            a(r, 2) = temp
            L = L + 1
            r = r - 1
         End If
   Loop While L <= r
   If L < Ri Then SortArray a, L, Ri, Col
   If Le < r Then SortArray a, Le, r, Col
End Sub

Private Sub DisplayTheTopMostUsedWords(arr As Variant, Nb As Long)
Dim L As Long, i As Integer
   i = 1
   Debug.Print "Rank Word    Frequency"
   Debug.Print "==== ======= ========="
   For L = UBound(arr) To UBound(arr) - Nb + 1 Step -1
      Debug.Print Left(CStr(i) & "    ", 5) & Left(arr(L, 1) & "       ", 8) & " " & Format(arr(L, 2), "0 000")
      i = i + 1
   Next
End Sub

Private Function DisplayFrequencyOf(Word As String, d As Object) As Long
   If d.Exists(Word) Then _
      DisplayFrequencyOf = d(Word)
End Function


COBOL:        IDENTIFICATION DIVISION.
       PROGRAM-ID. WordFrequency.
       AUTHOR.  Bill Gunshannon.
       DATE-WRITTEN.  30 Jan 2020.
      ************************************************************
      ** Program Abstract:
      **   Given a text file and an integer n, print the n most
      **   common words in the file (and the number of their
      **   occurrences) in decreasing frequency.
      **
      **   A file named Parameter.txt provides this information.
      **   Format is:
      **   12345678901234567890123456789012345678901234567890
      **   |------------------|----|
      **     ^^^^^^^^^^^^^^^^  ^^^^
      **          |              |
      **     Source Text File   Number of words with count
      **       20 Characters      5 digits with leading zeroes
      **
      **
      ************************************************************
       
       ENVIRONMENT DIVISION.
       
       INPUT-OUTPUT SECTION.
       FILE-CONTROL.
            SELECT Parameter-File ASSIGN TO "Parameter.txt"
                 ORGANIZATION IS LINE SEQUENTIAL.
            SELECT Input-File ASSIGN TO Source-Text
                 ORGANIZATION IS LINE SEQUENTIAL.
            SELECT Word-File ASSIGN TO "Word.txt"
                 ORGANIZATION IS LINE SEQUENTIAL.
            SELECT Output-File ASSIGN TO "Output.txt"
                 ORGANIZATION IS LINE SEQUENTIAL.
            SELECT Print-File ASSIGN TO "Printer.txt"
                 ORGANIZATION IS LINE SEQUENTIAL.
            SELECT Sort-File     ASSIGN TO DISK.
       
       DATA DIVISION.
       
       FILE SECTION.
       
       FD  Parameter-File
           DATA RECORD IS Parameter-Record.
       01  Parameter-Record.
           05 Source-Text               PIC X(20).
           05 How-Many                  PIC 99999.

       FD  Input-File
           DATA RECORD IS Input-Record.
       01  Input-Record.
           05 Input-Line                PIC X(80).

       FD  Word-File
           DATA RECORD IS Word-Record.
       01  Word-Record.
           05 Input-Word               PIC X(20).

       FD  Output-File
           DATA RECORD IS Output-Rec.
       01  Output-Rec.
           05  Output-Rec-Word         PIC X(20).
           05  Output-Rec-Word-Cnt     PIC 9(5).

       FD  Print-File
           DATA RECORD IS Print-Rec.
       01  Print-Rec.
           05  Print-Rec-Word          PIC X(20).
           05  Print-Rec-Word-Cnt      PIC 9(5).
       
       SD  Sort-File.
       01  Sort-Rec.
           05  Sort-Word               PIC X(20).
           05  Sort-Word-Cnt           PIC 9(5).
       
       
       WORKING-STORAGE SECTION.
       
       01 Eof                    PIC X     VALUE 'F'.
       01 InLine                 PIC X(80).
       01 Word1                  PIC X(20).
       01 Current-Word           PIC X(20).
       01 Current-Word-Cnt       PIC 9(5).
       01 Pos                    PIC 99
                 VALUE 1.
       01 Cnt                    PIC 99.
       01 Report-Rank.
          05  IRank              PIC 99999
                 VALUE 1.
          05 Rank                PIC ZZZZ9.
       
       PROCEDURE DIVISION.
       
       Main-Program.
      **
      **  Read the Parameters
      **
         OPEN INPUT Parameter-File.
         READ Parameter-File.
         CLOSE Parameter-File.

      **
      **  Open Files for first stage
      **
         OPEN INPUT  Input-File.
         OPEN OUTPUT  Word-File.

      **
      **  Pare\se the Source Text into a file of invidual words
      **
         PERFORM UNTIL Eof = 'T'
            READ Input-File 
               AT END MOVE 'T' TO Eof
            END-READ

         PERFORM Parse-a-Words

         MOVE SPACES TO Input-Record
         MOVE 1 TO Pos
         END-PERFORM.
     
      **
      **  Cleanup from the first stage
      **
         CLOSE Input-File Word-File

      **
      **  Sort the individual words in alphabetical order
      **
         SORT Sort-File
              ON ASCENDING KEY Sort-Word
              USING Word-File
              GIVING Word-File.

      **
      **  Count each time a word is used
      **
         PERFORM Collect-Totals.

      **
      **  Sort data by number of usages per word
      **
         SORT Sort-File
              ON DESCENDING KEY Sort-Word-Cnt
              USING Output-File
              GIVING Print-File.

      **
      **  Show the work done
      **
         OPEN INPUT Print-File.
            DISPLAY " Rank  Word               Frequency"
         PERFORM How-Many TIMES
            READ Print-File
            MOVE IRank TO Rank
            DISPLAY Rank "  " Print-Rec
            ADD 1 TO IRank
         END-PERFORM.

      **
      **  Cleanup
      **
         CLOSE Print-File.
         CALL "C$DELETE" USING "Word.txt" ,0
         CALL "C$DELETE" USING "Output.txt" ,0

         STOP RUN.
         

        Parse-a-Words.
          INSPECT Input-Record CONVERTING '-.,"();:/[]{}!?|' TO SPACE 
          PERFORM UNTIL Pos > FUNCTION STORED-CHAR-LENGTH(Input-Record) 


          UNSTRING Input-Record DELIMITED BY SPACE INTO Word1 
                    WITH POINTER Pos TALLYING IN Cnt 
          MOVE FUNCTION TRIM(FUNCTION LOWER-CASE(Word1)) TO Word-Record
          
          IF Word-Record NOT EQUAL SPACES AND Word-Record IS ALPHABETIC
             THEN WRITE Word-Record
          END-IF

          END-PERFORM.

       Collect-Totals.
          MOVE 'F' to Eof
          OPEN INPUT Word-File
          OPEN OUTPUT Output-File
             READ Word-File
             MOVE Input-Word TO Current-Word
             MOVE 1 to Current-Word-Cnt
          PERFORM UNTIL Eof = 'T'
             READ Word-File
                AT END MOVE 'T' TO Eof
             END-READ

             IF FUNCTION TRIM(Word-Record) 
                    EQUAL 
                           FUNCTION TRIM(Current-Word)
                THEN
                     ADD 1 to Current-Word-Cnt
                ELSE
                     MOVE Current-Word TO Output-Rec-Word
                     MOVE Current-Word-Cnt TO Output-Rec-Word-Cnt
                     WRITE Output-Rec
                     MOVE 1 to Current-Word-Cnt
                     MOVE Word-Record TO Current-Word
                     MOVE SPACES TO Input-Record
            END-IF 
           
          END-PERFORM.
          CLOSE Word-File Output-File.
       END-PROGRAM.