Clipper Tutorial: a Guide to Open Source Clipper(s)/Other Features (Not Present in the Old Clipper)

From Wikibooks, open books for an open world
Jump to navigation Jump to search

Other programming topics[edit | edit source]

Hash Arrays (Associative Arrays, Also Known as "Hash Tables")[edit | edit source]

In an associative array the elements are accessed using a string and not a number as in the "normal" array.

There are online "Calorie checkers" (which are also a nice idea for a web application for the last part) and I think it will be fun to make us a very small one (BEWARE: the information obtained from this program should not replace a varied, balanced diet and a healthy lifestyle).

   hFood := { => }
   hFood["Cornflakes"] := "370"
   hFood["Spaghetti"] := "101"
   hFood["Beef"] := "280"
   hFood["Ham"] := "240"
   hFood["Broccoli"] := "32"
   hFood["Grapefruit"] := "32"
   hFood["Spinach"] := "8"
   hFood["Chocolate"] := "500"
   ACCEPT "Enter a food: " TO cFood
   IF hb_HHasKey( hFood, cFood )
      ? "&cFood is " + hFood[cFood] + " calories per 100 grams"
   ELSE
      ? "&cFood: information not available"
   ENDIF

Now we will redo the example of the array of months, showing an alternative syntax for loading data into an hash array:

LOCAL month
hMonths := { "January" => 31, "February" => 28, "March" => 30,  "April" => 30, "May" => 31, "June" => 30, "July" => 31, "August" => 31, "September" => 30, "October" => 31, "November" => 30, "December" => 31 }

FOR EACH month in hMonths
   ? month:__ENUMKEY()
   ?? month:__ENUMVALUE()
NEXT

hb_HEval() is the AEval() equivalent for hash arrays.

See https://vivaclipper.wordpress.com/2013/01/18/hash-vs-table/.

Morse Code[edit | edit source]

Morse code was invented around 1840 by Samuel Finley Breese Morse and Alfred Vail to transmit messages across wires using devices called "telegraphs" they invented as well, revolutionizing long-distance communication. Initially the signals sent from one telegraph operated an electromagnet on the receiver's telegraph embossing the message on a strip of paper, later the receiver could also produce a sound. The first message transmitted was a citation of the book of Numbers from the King James Version: What hath God wrought!

Let us try to convert a text to Morse code: to begin we load the chart of Morse code in an associative array and define a code block which uses the function TONE() to sound a speaker tone for a dot or a dash - employing the IF() function - which is essentially the same function we encounter in spreadsheets - to select the right duration tone.

Then we prompts the user for a message and if none is supplied it uses the pangram "The quick brown fox jumps over a lazy dog" (a pangram is a sentence that contains all of the letters of the alphabet).

We show what message we will translate and starts two a for each loop: the first will scan the message showing which letter it will translate, one at a line (and will also use TONE() with zero frequency and appropriate lengths to make the pauses between letters and words) and then a nested for each loop will show whether it is about to sound a dot or a dash, running the code block to have the right sound.

LOCAL letter, ditdah
hMorseCode := { => }

hMorseCode[ "a" ] := ".-"
hMorseCode[ "b" ] := "-..."
hMorseCode[ "c" ] := "-.-."
hMorseCode[ "d" ] := "-.."
hMorseCode[ "e" ] := "."
hMorseCode[ "f" ] := "..-."
hMorseCode[ "g" ] := "--."
hMorseCode[ "h" ] := "...."
hMorseCode[ "i" ] := ".."
hMorseCode[ "j" ] := ".---"
hMorseCode[ "k" ] := "-.-"
hMorseCode[ "l" ] := ".-.."
hMorseCode[ "m" ] := "--"
hMorseCode[ "n" ] := "-."
hMorseCode[ "o" ] := "---"
hMorseCode[ "p" ] := ".--."
hMorseCode[ "q" ] := "--.-"
hMorseCode[ "r" ] := ".-."
hMorseCode[ "s" ] := "..."
hMorseCode[ "t" ] := "-"
hMorseCode[ "u" ] := "..-"
hMorseCode[ "v" ] := "...-"
hMorseCode[ "w" ] := ".--"
hMorseCode[ "x" ] := "-..-"
hMorseCode[ "y" ] := "-.--"
hMorseCode[ "z" ] := "--.."
hMorseCode[ "0" ] := "-----"
hMorseCode[ "1" ] := ".----"
hMorseCode[ "2" ] := "..---"
hMorseCode[ "3" ] := "...--"
hMorseCode[ "4" ] := "....-"
hMorseCode[ "5" ] := "....."
hMorseCode[ "6" ] := "-...."
hMorseCode[ "7" ] := "--..."
hMorseCode[ "8" ] := "---.."
hMorseCode[ "9" ] := "----."

nDotDuration := 4 && The dot duration is the basic unit of time measurement in Morse code transmission and we set it to 4/18 seconds
bPlayMorse := {| cCurrent| if ( cCurrent = ".", Tone( 480, nDotDuration ), Tone( 480, nDotDuration * 3 ) ) }

ACCEPT "Enter message: " TO message
IF message == "" && if no message we use a default one
  message := "The quick brown fox jumps over a lazy dog" && an English-language pangram — a sentence that contains all of the letters of the alphabet
ENDIF

? "Converting '"+Upper( message )+"' in Morse..."

FOR EACH letter in Lower( message )
  ? Upper(letter)+" "
    IF letter==" " 
      Tone( 0, nDotDuration * 7 ) && a long pause between words...
    ELSE
      FOR EACH ditdah in hMorseCode[ letter ]
        ?? ditdah
        Eval( bPlayMorse, ditdah )
      NEXT
    ENDIF
  Tone( 0, nDotDuration * 3 ) && ... and a short pause between letters
NEXT

Regular Expressions[edit | edit source]

Regular expressions can be considered an extension of wildcards. In a DOS or Windows prompt, for example, we could list all dbf files in the current directory with the command

dir *.dbf

where the asterisk, as they say, matches one or more characters. The other wildcard, the question mark, will match any character, but exactly one: the commands

dir ?.dbf
dir ??.dbf

will show, respectively, every dbf file in the current directory whose name is exactly one or two characters long.

Regular expressions are much more flexible. They were invented by the mathematician Stephen Cole Kleene (the asterisk wildcard derives from an operator he defined and which is called Kleene star). Their flexibility allows us to write an expression that can match dbf filenames one or two characters long in a single expression which looks like this:

.{1,2}\.dbf

There are different types of regular expressions: basic, extended, PCRE (Perl-compatible regular expressions, http://www.pcre.org/), and many other different implementations.

As far as we are concerned we can limit ourselves to PCRE and refer to the documentation at these URLs: https://github.com/Petewg/harbour-core/wiki/Regular-Expressions and https://github.com/zgamero/sandbox/wiki/X_RegularExpressions, https://www.pcre.org/original/doc/html/pcrepattern.html, https://www.debuggex.com/cheatsheet/regex/pcre. There are large books entirely devoted about regular expressions, such as "Mastering Regular Expressions" by Jeffrey E.F. Friedl, just to say there's so much to explore about this topic.

Here https://www.rexegg.com/regex-cookbook.html is a cookbook to see examples; and here https://www.regexpal.com/ and here https://regexr.com/ there are interactive tools to try online (and interactively) expressions.

Regular expressions are not a panacea that solves all data validation problems: for example, it is not possible to use them to validate dates, as the number of days allowed for each month depends on the month (and in the case of February also on the year).

As a first example, we will redo the program that receives a letter from the keyboard and reports if it is a vowel or not.

LOCAL cRegEx := "[aeiou]"
WAIT "Key in a letter: " TO char
? char + " is" + iif( hb_regexLike( cRegEx, char, .F. ), "", " not" ) + " a vowel."

The synopsis of hb_RegExLike is this: hb_RegExLike(<hRegEx>|<cRegEx>, <cText> [, <lCaseSensitive>] [, <lMultiLine>]).

The next example is again the program to average of an array which grows until we insert numbers as input. This time we use a regular expression to validate the numbers we enter.

PROCEDURE MAIN

   LOCAL n, sum := 0, average    && let's initialize the variables
   LOCAL reNumber := "-?[0-9]+(\.?[0-9]+)?" && and a regular expression that matches numbers (optionally with a decimal point and a sign)
   aNumbers := {}                && and an empty array

   ? "enter a list of integer numbers, and a non-number when you are finished"
   WHILE .T.                     && we begin an endless loop
      ACCEPT "next element: " TO item
      IF hb_RegExLike ( reNumber, item ) && we replace IsDec() with hb_RegExLike()
         AAdd ( aNumbers, val(item) ) && if we did, then we add the number as a new element to the array
         sum := sum + val(item)      && (and we update the sum of the elements in our array)...
      ELSE
         EXIT                    && ...if we did not, we quit this loop
      ENDIF
   ENDDO

   average := sum / Len( aNumbers ) && we compute the average of the values in the array

   ?
   ? "to average the array you must correct each element by adding to it the corresponding value below"
   ?
   FOR n := 1 to LEN ( aNumbers )
      ?? AVERAGE - aNumbers[n]
   NEXT
RETURN

Error Handling With “Try-Catch-Finally” Block[edit | edit source]

This construct is analogous to those found in Java, JavaScript and C# (and similar to the C++'s one). They are actually translated into the BEGIN SEQUENCE structure and therefore perhaps they are useful only for those who are accustomed to those languages.

PROCEDURE MAIN

   LOCAL n, sum := 0, average    && let's initialize the variables
   aNumbers := {}                && and an array without elements

   ? "enter a list of integer numbers, and a non-number when you are finished"
   WHILE .T.                     && we begin an endless loop
   ACCEPT "next element: " TO item
      TRY
         AAdd ( aNumbers, &item ) && if we did, then we add the number as a new element to the array
         sum := sum + &item      && (and we update the sum of the elements in our array)...
      CATCH
         EXIT                    && ...if we did not, we quit this loop
      END
   ENDDO

   average := sum / Len( aNumbers ) && we compute the average of the values in the array

   ?
   ? "to average the array you must correct each element by adding to it the corresponding value below"
   ?
   FOR n := 1 to LEN ( aNumbers )
      ?? AVERAGE - aNumbers[n]
   NEXT
RETURN

Multithreading[edit | edit source]

http://harbourlanguage.blogspot.com/2010/04/harbour-multi-thread.html

https://github.com/Petewg/harbour-core/wiki/MultiThreading

see samples in \hb30\tests\mt

INET[edit | edit source]

https://github.com/Petewg/harbour-core/wiki/Harbour-INET-API

PDF[edit | edit source]

hbhpdf: Libharu http://libharu.org/ bindings. Documentation: https://github.com/libharu/libharu/wiki/API%3A-Document

http://www.pagescript32.com/

https://github.com/MRonaldo/MR-Tools/downloads

How to create a DLL[edit | edit source]

http://www.xharbour.com/xhdn/referenceguide/index.asp?page=article&article=create_dll

Debugging[edit | edit source]

http://www.kresin.ru/en/debugger.html

You may have heard or read the legend that the term bug appeared when an actual bug was found in the Harvard's Mark II computer. According to one of my books from the high school, it was a little butterfly found in the ENIAC!

Text books less prone to folklore call this a story without foundation, for example

https://www.computerworld.com/article/2515435/moth-in-the-machine--debugging-the-origins-of--bug-.html states that Thomas Edison used the word already in 1878. The same goes with the Wikipedia entry →Software bug which even includes a picture of the moth actually found in the Harvard Mark II and describes the whole history.

A page from the →Harvard Mark II electromechanical computer's log, featuring a dead moth that was removed from the device.