This code is giving A1..A9, B0..B9, C0..C9, ...
.
But I only want A1..A8, B1..B8, C1..C8, D1..D8, E1..E8, F1..F8, G1..G8, H1..H8
(1-64).
package main
import (
"fmt"
)
func ToString(n int8) string {
return string((n/10)+65) + string(((n%10)+49)-1)
}
func main() {
var i int8
for i = 1; i < 11; i++ {
fmt.Println(ToString(i))
}
}
Firstly, you're dividing by the wrong number. If you want the numbers only up to 8, you need to divide by 8.
Secondly, you don't need to subtract one in the second string, but you do need to decrease the n
.
func ToString(n int8) string {
n--
return string((n/8)+65) + string((n%8)+49)
}
func main() {
var i int8
for i = 1; i <= 64; i++ {
fmt.Println(ToString(i))
}
}
Let's see different approaches and performance improvements.
All the solutions and the benchmarking code can be found on the Go Playground. The code on the Playground is a test file, not an executable. You have to save it into a file named XX_test.go
and run it with go test -bench .
.
Ainar-G's answer is cool (+1):
func ToStringConcat(n byte) string {
n--
return string((n/8)+65) + string((n%8)+49)
}
But note that the previous solution concatenates 2 string
values which is "costly" especially if we want to call this ToString()
many times.
One might think that we could spare the string
concatenation if we would try to convert a single integer number containing the 2 codes (the first shifted left by 8 bits), but that wouldn't work as converting integer numbers to string
results in a string
value with a single rune
(a string
containing the UTF-8 representation of the integer).
But we may use a []byte
with 2 values (the letter code and the digit code), and then we only need to convert this single slice value to string
:
func ToStringSlice(n byte) string {
n--
return string([]byte{(n / 8) + 65, (n % 8) + 49})
}
string
values are slicable in Go which results in a new string
(new string
header). So we may use a string
constant of all the values, and do a simple slicing to get the part we need:
const values = " A1A2A3A4A5A6A7A8B1B2B3B4B5B6B7B8C1C2C3C4C5C6C7C8D1D2D3D4D5D6D7D8E1E2E3E4E5E6E7E8F1F2F3F4F5F6F7F8G1G2G3G4G5G6G7G8H1H2H3H4H5H6H7H8"
func ToStringConst(n byte) string {
n *= 2
return values[n : n+2]
}
Even though slicing a string
results in a substring which shares the underlying array, it sill needs to create a new string
header.
Since we don't have a lot of possible values, best (fastest) solution is to prepare all the possible values, and just do a simple lookup. Since input is a number, we don't even need a map
, we can use a simple []string
slice:
var dict = []string{"",
"A1", "A2", "A3", "A4", "A5", "A6", "A7", "A8",
"B1", "B2", "B3", "B4", "B5", "B6", "B7", "B8",
"C1", "C2", "C3", "C4", "C5", "C6", "C7", "C8",
"D1", "D2", "D3", "D4", "D5", "D6", "D7", "D8",
"E1", "E2", "E3", "E4", "E5", "E6", "E7", "E8",
"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8",
"G1", "G2", "G3", "G4", "G5", "G6", "G7", "G8",
"H1", "H2", "H3", "H4", "H5", "H6", "H7", "H8",
}
func ToStringDict(n byte) string {
return dict[n]
}
Let's benchmark speed of the above solutions:
func BenchmarkConcat(b *testing.B) {
for i := 0; i < b.N; i++ { ToStringConcat(1) }
}
func BenchmarkSlice(b *testing.B) {
for i := 0; i < b.N; i++ { ToStringSlice(1) }
}
func BenchmarkConst(b *testing.B) {
for i := 0; i < b.N; i++ { ToStringConst(1) }
}
func BenchmarkDict(b *testing.B) {
for i := 0; i < b.N; i++ { ToStringDict(1) }
}
Result:
BenchmarkConcat-4 20000000 106 ns/op
BenchmarkSlice-4 100000000 17.0 ns/op
BenchmarkConst-4 2000000000 1.34 ns/op
BenchmarkDict-4 2000000000 1.04 ns/op
Just by jumping from concatenation to slice conversion, it immediately becomes 6 times faster.
Utilizing string slicing, we again make it 12 times faster.
And pre-building all possible values and just doing a simple lookup, we further gain 22%.
Comparing the final to the initial: the dictionary lookup is a hundred times faster than the original concatenation.
fmt
For completeness, here's a solution using the fmt
package:
func ToStringFmt(n byte) string {
n--
return fmt.Sprintf("%c%c", (n/8)+65, (n%8)+49)
}
But this one is slower by almost 2.5 times even than our slowest String concatenation solution, as this has to wrap arguments into interface{}
values, create and put them into a slice (for the vararg), has to parse and analyze a format string
, use reflection to process arguments, build the string
representation in a buffer which finally is used to produce the returned string
value. A lot of things happen which are for the "general" case and are not required for our "special" case.