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]”
Comments
Vadim
Thank you very much, Reinier! This is really valuable material. I agree with you that it is better to remove the [P] variable.
CN_Iceman
Yes, I learned that [P] was a variable many years ago because of a problem that had me weeks of trial and error.
I really liked the explanation of the development of your program.
Good job and thanks for sharing it with the community.