Могу ли я сделать предварительно заполненную строку в golang с помощью make или new? - PullRequest
0 голосов
/ 31 августа 2018

Я пытаюсь оптимизировать свою библиотеку строк в Go. До сих пор я нашел единственный способ заполнить строку (на самом деле bytes.Buffer) известным символьным значением (например, 0 или "") с помощью цикла for.

фрагмент кода:

// PadLeft pads string on left side with p, c times
func PadLeft(s string, p string, c int) string {
    var t bytes.Buffer
    if c <= 0 {
        return s
    }
    if len(p) < 1 {
        return s
    }
    for i := 0; i < c; i++ {
        t.WriteString(p)
    }
    t.WriteString(s)
    return t.String()
}

Чем больше строковый блок, тем больше копий памяти в буфере t. Есть ли более элегантный способ сделать буфер известного размера с известным значением при инициализации?

1 Ответ

0 голосов
/ 31 августа 2018

Вы можете использовать только make() и new() для выделения буферов (байтовых срезов или массивов), которые обнуляются. Вы можете использовать составные литералы для получения срезов или массивов, которые изначально содержат ненулевые значения, но вы не можете описать начальные значения динамически (индексы должны быть константами).

Вдохновитесь схожей, но очень эффективной strings.Repeat() функцией. Повторяет данную строку с указанным количеством:

func Repeat(s string, count int) string {
    // Since we cannot return an error on overflow,
    // we should panic if the repeat will generate
    // an overflow.
    // See Issue golang.org/issue/16237
    if count < 0 {
        panic("strings: negative Repeat count")
    } else if count > 0 && len(s)*count/count != len(s) {
        panic("strings: Repeat count causes overflow")
    }

    b := make([]byte, len(s)*count)
    bp := copy(b, s)
    for bp < len(b) {
        copy(b[bp:], b[:bp])
        bp *= 2
    }
    return string(b)
}

strings.Repeat() выполняет однократное распределение для получения рабочего буфера (который будет срезом байтов []byte) и использует встроенную функцию copy() для копирования повторяемой строки. Следует отметить, что он использует рабочую копию и пытается копировать ее целиком, например, например. если строка уже была скопирована 4 раза, копирование этого буфера сделает его 8 раз и т. д. Это сведет к минимуму вызовы на copy(). Кроме того, решение использует то преимущество, что copy() может копировать байты из string без необходимости преобразования его в фрагмент байта.

Нам нужно нечто похожее, но мы хотим, чтобы результат был добавлен к строке.

Мы можем объяснить это, просто выделив буфер, который используется внутри Repeat() плюс длину строки, которую мы заполняем слева.

Результат (без проверки параметра count):

func PadLeft(s, p string, count int) string {
    ret := make([]byte, len(p)*count+len(s))

    b := ret[:len(p)*count]
    bp := copy(b, p)
    for bp < len(b) {
        copy(b[bp:], b[:bp])
        bp *= 2
    }
    copy(ret[len(b):], s)
    return string(ret)
}

Тестирование:

fmt.Println(PadLeft("aa", "x", 1))
fmt.Println(PadLeft("aa", "x", 2))
fmt.Println(PadLeft("abc", "xy", 3))

Вывод (попробуйте на Go Playground ):

xaa
xxaa
xyxyxyabc

См. Аналогичный / связанный вопрос: Есть ли аналог go memset в go?

...