Remove [P] from VisualNeoWin!

Last week I tried -inspired by challenge 033 on https://theweeklychallenge.org/challenges/– to create a VisualNeoWin program that shows the distribution of characters in a string (using the 26 characters of the ISO basic Latin alphabet a..z). Suppose the inputstring is CbcbbA than the lowercase output should be like

a: 1
b: 3
c: 2

When scanning a string, it is necessary to have variables for ‘a’ through ‘z,’ and the corresponding integer values should be incremented upon encountering a character. An idea is to create an array [count] with 26 elements: [count1]…[count26], associated with the character ‘a’..’z’. Counting the number of occurrences of a character in the inputstring means incrementing the corresponding array element:

If “[char]” “=” “a”
 Math “[count1] + 1” “0” “[count1]”
EndIf
.
.
.
If “[char]” “=” “z”
 Math “[count26] + 1” “0” “[count26]”
EndIf

This means 26 If-EndIf statements! It can surely be done better. A little trick. Several programming languages have hashes or associative arrays. We can mimic these by [count[a]]…[count[z]]. The code above would be modified into

If “[char]” “=” “a”
 Math “[count[a]] + 1” “0” “[count[a]]”
EndIf
.
.
.
If “[char]” “=” “z”
 Math “[count[z]] + 1” “0” “[count[z]]”
EndIf

[count[a]] should be equal to [count1] which is the case if [a] = 1, [count[z]] should be equal to [count26] which is the case if [z] = 26 etc.

Nevertheless still 26 If-EndIf statements. Luckily these can be reduced now to one statement:

Math “[count[[char]]] + 1” “0” “[count[[char]]]”

This is an efficient solution. Character counting can be done as following:

SetVar “[alphabet]” “abcdefghijklmnopqrstuvwxyz”
SetVar “[input]” “This is a string that I want to know about!”
StrLen “[input]” “[len_input]”

.loop each character a…z through the string to count
Loop “1” “[len_input]” “[i]”
 SubStr “[input]” “[i]” “1” “[char]”
 StrLower “[char]” “[char]”
 SearchStr “[char]” “[alphabet]” “[found]” “”

 If “[found]” “>” “0”
  Math “[count[[char]]] + 1” “0” “[count[[char]]]”
 EndIf

EndLoop

Of course, first we have to initialize the 26 variables [a]…[z] and the 26 array elements [count[a]]…[count[z]] (ala [count1][count26]) . This is also easy:

SetVar “[alphabet_csv]” “a;b;c;d;e;f;g;h;i;j;k;l;m;n;o;p;q;r;s;t;u;v;w;x;y;z”
StrParse “[alphabet_csv]” “;” “[letter]” “[no_letters_alphabet]”

.initialize
Loop “1” “[no_letters_alphabet]” “[i]”
 SetVar “[[letter[i]]]” “[i]”
 SetVar “[count[i]]” “0”
 .equivalent to SetVar “[count[[letter[i]]]]” “0”
EndLoop

So, we could now finetune our code and make the variable [output] for presenting the letters and their occurrences in the inputstring:

.display character counts
SetVar “[output]” “”
Loop “1” “[no_letters_alphabet]” “[i]”
 SetVar “[output]” “[output][#13][letter[i]]: [count[[letter[i]]]]”
EndLoop

Unfortunately our program does not run!

The problem appears to be:

SetVar “[[letter[i]]]” “[i]” gives an error when [i] = 16

In other words:

SetVar “[p]” “16”

is the problem.  The explanation: [p] is a predefined global read-only variable which value cannot be modified. What to do? I’m used to escape characters in other languages, so I thought about a quasi escape solution.

Loop “1” “[no_letters_alphabet]” “[i]”
 SetVar “[\[letter[i]]]” “[i]”
 SetVar “[count[\[letter[i]]]]” “0”
EndLoop

Now [count[\a]] = [count1] = 0 … [count[\z]] = [count26] = 0

Two places in the above code need to be adapted:

If “[found]” “>” “0”
 Math “[count[\[char]]] + 1” “0” “[count[\[char]]]”
EndIf

and the output:[count[\[letter[i]]]]

Loop “1” “[no_letters_alphabet]” “[i]”
 SetVar “[output]” “[output][#13][letter[i]]: [count[\[letter[i]]]]”
EndLoop

Now the program runs fine and gives the intended output

a: 4
b: 1
c: 0
d: 0
e: 0
f: 0
g: 1
h: 2
i: 4
j: 0
k: 1
l: 0
m: 0
n: 3
o: 3
p: 0
q: 0
r: 1
s: 3
t: 7
u: 1
v: 0
w: 2
x: 0
y: 0
z: 0

Below the complete code.

[P] is an alias for [PageNumber], so it can be left out of VisualNeoWin as far as I can see. It is, by the way, the only predefined global variable with an one-character-name.

I hope it helps. Thanks for reading.

Reinier

SetVar “[alphabet]” “abcdefghijklmnopqrstuvwxyz”
SetVar “[alphabet_csv]” “a;b;c;d;e;f;g;h;i;j;k;l;m;n;o;p;q;r;s;t;u;v;w;x;y;z”
StrParse “[alphabet_csv]” “;” “[letter]” “[no_letters_alphabet]”
.initialize
Loop “1” “[no_letters_alphabet]” “[i]”
 SetVar “[\[letter[i]]]” “[i]”
 SetVar “[count[\[letter[i]]]]” “0”
.equivalent to SetVar “[count[[letter[i]]]]” “0”
EndLoop

SetVar “[input]” “This is a string that I want to know about!”
StrLen “[input]” “[len_input]”

.loop each character a..z through the string to count
Loop “1” “[len_input]” “[i]”
 SubStr “[input]” “[i]” “1” “[char]”
 StrLower “[char]” “[char]”
 SearchStr “[char]” “[alphabet]” “[found]” “”

 If “[found]” “>” “0”
  Math “[count[\[char]]] + 1” “0” “[count[\[char]]]”
 EndIf
EndLoop

.display character counts
SetVar “[output]” “”
Loop “1” “[no_letters_alphabet]” “[i]”
 SetVar “[output]” “[output][#13][letter[i]]: [count[\[letter[i]]]]”
EndLoop

.remove [#13] at the beginning of [output]
StrDel “[output]” “1” “1” “[output]”

.[output_def] makes presenting the results in e.g. listbox more smooth
SetVar “[output_def]” “[output]”