Introduction
You work for a company that sells fonts through their website.
They’d like to show a different sentence each time someone views a font on their website.
To give a comprehensive sense of the font, the random sentences should use all the letters in the English alphabet.
They’re running a competition to get suggestions for sentences that they can use.
You’re in charge of checking the submissions to see if they are valid.
Pangram comes from Greek, παν γράμμα, pan gramma, which means "every letter".
The best known English pangram is:
> The quick brown fox jumps over the lazy dog.
Instructions
Instructions
Your task is to figure out if a sentence is a pangram.
A pangram is a sentence using every letter of the alphabet at least once.
It is case insensitive, so it doesn’t matter if a letter is lower-case (e.g. k) or upper-case (e.g. K).
For this exercise, a sentence is a pangram if it contains each of the 26 letters in the English alphabet.
Dig Deeper
strings.ContainsRune()
strings.ContainsRune()
// Package pangram is a small library for checking if a phrase is a pangram
package pangram
import "strings"
// IsPangram checks if a phrase is a pangram
func IsPangram(s string) bool {
lookup := strings.ToLower(s)
for chr := 'a'; chr <= 'z'; chr++ {
if !strings.ContainsRune(lookup, chr) {
return false
}
}
return true
}
This begins by lowercasing the input by using the strings ToLower() function.
Since, for example, A is considered the same letter as a, lowercasing A makes it easier to check if a is in the input.
The runes of a to z are iterated in the for loop.
The strings.ContainsRune() function is used to see if the letter is contained in the input string.
If not, then the function immediately returns false.
If the loop completes without returning false, then all of the a-z letters are contained in the input string,
and the function returns true.
Bit field
Bit field
// Package pangram is a small library for checking if a phrase is a pangram
package pangram
const alphamask int = 0b11111111111111111111111111
// IsPangram checks if a phrase is a pangram
func IsPangram(phrase string) bool {
phrasemask := 0
length := len(phrase)
for i := range length {
letter := phrase[i]
if letter > 96 && letter < 123 {
phrasemask |= 1 << (letter - 97)
} else if letter > 64 && letter < 91 {
phrasemask |= 1 << (letter - 65)
}
}
return phrasemask == alphamask
}
This solution uses the ASCII value of the letter to set the corresponding bit position.
The constant alphamask is the value for the rightmost 26 bits of an int being set.
It is set using a binary literal.
The blank bit field phrasemask is defined to keep track of the used letters.
The ways to iterate characters are by Unicode runes, or by each letter being a string, or by each letter being a byte.
The runes are from range on a string, the strings from Split(), and the bytes from indexing into the string.
Another way to iterate runes is to convert the string to a rune slice and range on it.
The difference between ranging on a rune slice vs ranging on a string is that the index returned from a string is the position of the next rune in bytes,
not which rune it is.
For example, if the first unicode character is two bytes, then the second unicode character index will be 2 when ranging on a string and 1 when ranging on a rune slice.
As of the time of this writing we can iterate bytes, since all of the characters are ASCII.
The string is looped through its characters, looking for a character being a through z or A through Z.
-
The ASCII value for a is 97, and for z is 122.
-
The ASCII value for A is 65, and for Z is 90.
-
If the lowercase letter is subtracted by 97, then a will result in 0, because 97 minus 97 equals 0.
z would result in 25, because 122 minus 97 equals 25.
So a would have 1 shifted left 0 places (so not shifted at all) and z would have 1 shifted left 25 places.
-
If the uppercase letter is subtracted by A, then A will result in 0, because 65 minus 65 equals 0.
Z would result in 25, because 90 minus 65 equals 25.
So A would have 1 shifted left 0 places (so not shifted at all) and Z would have 1 shifted left 25 places.
In that way, both a lowercase z and an uppercase Z can share the same position in the bit field.
So, for a 32-bit unsigned integer, if the values for a and Z were both set, the bits would look like
zyxwvutsrqponmlkjihgfedcba
00000010000000000000000000000001
We can use the bitwise OR operator to set the correct bit for the letter’s position in the alphabet.
After the loop completes, the function returns if the phrasemask value is the same value as when all 26 bits are set.
Source: Exercism go/pangram