Преобразование числа в строку на Go: эффективный алгоритм

В программировании часто встречаются задачи, которые кажутся элементарными, пока не попробуешь реализовать их без встроенных средств языка. Одна из таких задач — преобразование числа в строку без использования стандартных библиотечных функций.

Давайте разберем элегантное решение, которое можно уверенно показать на техническом собеседовании.

Понимание задачи

Нам нужно написать функцию, которая принимает целое число (integer) и возвращает его строковое представление. Например:

  • 123"123"
  • -456"-456"
  • 0"0"

Ограничения: нельзя использовать встроенные функции преобразования StringBuilder и пакет fmt (кроме тестов).

Ключевые аспекты решения

Основная сложность этой задачи заключается в том, что мы работаем с отдельными цифрами числа, а не с числом как единым целым. Цифры в числе имеют разный вес: единицы, десятки, сотни и т.д. Нам нужно:

  1. Извлекать цифры числа по одной, начиная с конца.
  2. Преобразовать каждую цифру в соответствующий символ.
  3. Корректно обрабатывать отрицательные числа.
  4. Особо обрабатывать ноль.
  5. Собирать все символы в правильном порядке.

Пошаговое решение

Шаг 1: Обработка особых случаев

Первым делом обработаем два особых случая — ноль и отрицательные числа:

func IntToString(num int) string {
    // особый случай: ноль
    if num == 0 {
        return "0"
    }

    // определяем, является ли число отрицательным
    isNegative := false
    if num < 0 {
        isNegative = true
        num = -num // работаем с положительным числом
    }
}

Шаг 2: Подготовка к извлечению цифр

Нам понадобится временный массив для хранения цифр в обратном порядке (поскольку мы извлекаем цифры с конца):

// создаем массив для хранения символов 
// (максимальная длина в int в 64-битной системе - 20 символов)
const maxDigits = 20
temp := make([]byte, maxDigits)
index := 0

Шаг 3: Извлечение цифр

Извлекаем цифры числа по одной, начиная с младшего разряда:

// извлекаем цифры по одной, начиная с конца
for num > 0 {
    digit := num % 10 // получаем последнюю цифру
    temp[index] = byte('0' + digit) // преобразуем цифру в символ
    index++
    num /= 10 // убираем последнюю цифру из числа
}

Здесь используется важный трюк: в Go символы представляются числами (кодами Unicode), и цифры идут последовательно. Поэтому '0' + digit дает нам символ, соответствующий цифре.

Шаг 4: Формирование результата

Теперь у нас есть цифры в обратном порядке, нужно создать итоговую строку:

// вычисляем длину результата
length := index
if isNegative {
    length++ // добавляем место для знака минус
}

// создаем байтовый слайс для результата
result := make([]byte, length)

// добавляем знак минус, если необходимо
resultIndex := 0
if isNegative {
    result[0] = '-'
    result = 1
}

// записываем цифры в правильном порядке
// (извлекли с конца, поэтому переворачиваем)
for i := index - 1; i >= 0; i-- {
    result[resultIndex] = temp[i]
    resultIndex++
}

Полный код функции

func IntToString(num int) string {
    // обработка нуля
    if num == 0 {
        return "0"
    }

    // обработка отрицательных чисел
    isNegative := false
    if num < 0 {
        isNegative = true
        num = -num
    }

    // временное хранилище для цифр (в обратном порядке)
    const maxDigits = 20
    temp := make([]byte, maxDigits)
    index := 0

    // извлечение цифр
    for num > 0 {
        digit := num % 10
        temp[index] = byte('0' + digit)
        index++
        num /= 10
    }

    // формирование результата
    length := index
    if isNegative {
        length++
    }

    result := make([]byte, length)

    resultIndex := 0
    if isNegative {
        result[0] = '-'
        resultIndex = 1
    }

    // переворачиваем цифры (извлекли с конца)
    for i := index - 1; i >= 0; i-- {
        result[resultIndex] = temp[i]
        resultIndex++
    }

    return string(result)
}

Тестирование решения

Для тестирования можно использовать простые тесты в отдельном файле:

package main

import "testing"

func TestIntToString(t *testing.T) {
    tests := []struct {
        name     string
        input    int
        expected string
    }{
        {"positive number", 123, "123"}, 
        {"negative number", -456, "-456"},
        {"zero", 0, "0"},
        {"large positive", 987654321, "987654321"},
        {"large negative", -987654321, "-987654321"},
        {"minimum positive", 1, "1"}, 
        {"minimum negative", -1, "-1"},
    }

    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            result := IntToString(tt.input)
            if result != tt.expected {
                t.Errorf("IntToString(%d) = %s, expected %s", 
                    tt.input, result, tt.expected)
            }
        })
    }
}

Почему это решение хорошее?

  1. Полное понимание процесса: решение демонстрирует, что вы понимаете, как работают числа на низком уровне.
  2. Обработка граничных случаев: учтены ноль, отрицательные числа, минимальное и максимальное значения.
  3. Эффективность: алгоритм работает за O(n), где n — количество цифр в числе.
  4. Память: используется фиксированный объем дополнительной памяти.
  5. Читаемость: код хорошо структурирован и легко читается.

Ключевой вывод

Задача преобразования числа в строку — отличный пример того, как простое на первый взгляд задание позволяет оценить фундаментальные знания программиста. Представленное решение показывает понимание основ работы с числами, строками и алгоритмами.

А какие оптимизации или улучшения для этой задачи можете предложить вы? Поделитесь своими мыслями в комментариях.

Понравилась статья? Поделиться с друзьями:
Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: