Rot13 Reader в Go

Для этого упражнения Я сделал это:

func between(start, end, value byte) bool{
    if value > end {
        return false
    } else if value < start {
        return false
    }
    return true
}
func (r rot13Reader) Read(p []byte) (n int, err error) {
    s, err := r.r.Read(p)
    if err != nil {
        return s, err
    }
    for i,v := range p {
        if between(97,122, v) {
            new := v + 13
            if new > 122 {
                new -= 26
            }
            p[i] = new
        } else if between(65, 90, v) {
            new := v + 13
            if new > 90 {
                new -= 26
            }
            p[i] = new
        }
    }

    return s, err

Он работает правильно, но это похоже на слишком много кода для чего-то подобного. Могу ли я улучшить этот код?

11 голосов | спросил yasar 1 stEurope/Moscowp30Europe/Moscow09bEurope/MoscowSat, 01 Sep 2012 04:09:59 +0400 2012, 04:09:59

4 ответа


14

Я не знаю. Иди, но вот несколько общих вещей, которые я заметил:

func between(start, end, value byte) bool {
    if value > end {
        return false
    } else if value < start {
        return false
    }
    return true
}

Как общий шаблон, не пишите if condition return true else return false или любую их перестановку. Вместо этого верните условие непосредственно. В вашем случае вам нужно немного переписать условие:

func between(start, end, value byte) bool {
    return ! (value > end) && ! (value < start)
}

То же, что и (и которое также может выводиться интуитивно из ожидаемой семантики between):

func between(start, end, value byte) bool {
    return value >= start && value <= end
}

В общем случае используют только логические константы true и false для инициализации переменных (или параметров) . Это их единственное использование.


В вашем основном коде вы по существу делаете одно и то же дважды, один раз для верхнего регистра и один раз для строчных букв. Избегайте избыточности и делайте эти случаи одним:

is_upper := between(65, 90, v)
is_lower := between(97, 122, v)

if is_upper || is_lower {
    new := v + 13
    if (is_upper && new > 90) || (is_lower && new > 122) {
        new -= 26
    }
    p[i] = new
}

Но этот код по-прежнему загадочен: каковы эти странные числа? Замените их по именованным константам или используйте символьные константы (поскольку ясно, что означает 'a').

Кроме того, вы можете избавиться от between, поскольку у Go есть функции для проверки того, есть ли byte - это строчная или строчная буква (требуется пакет unicode):

is_upper := unicode.IsUpper(rune(v))
is_lower := unicode.IsLower(rune(v))

Наконец, логика rot-13, преобразующая символ, может быть упрощена с помощью операции модуля:

c := (c + 13) % 26

Очевидно, это предполагает, что c - значение от 0 до 25; поэтому реальная логика должна быть чем-то вроде этого: (Серьезно, Go? Нет условного оператора?!)

if is_upper || is_lower {
    a := byte('a')
    if is_upper { a = byte('A') }
    p[i] = (v - a + 13) % 26 + a
}

В последней строке здесь сначала помещается значение v в диапазон 0-25 путем вычитания a, затем преобразование rot-13, а затем переводит его обратно в букву, добавляя значение a назад.

Обратите внимание, насколько короче логика этого кода: тело всего цикла теперь имеет всего семь строк.

ответил Konrad Rudolph 1 stEurope/Moscowp30Europe/Moscow09bEurope/MoscowSat, 01 Sep 2012 16:20:29 +0400 2012, 16:20:29
6

Это немного улучшает ответ Кита, делая более понятным источник магических чисел.

func (r *rot13Reader) Read(p []byte) (n int, err error) {
    n, err = r.r.Read(p)
    for i := 0; i < n; i++ {
        c := p[i]
        switch {
        case 'a' <= c && c <= 'z':
            p[i] = (c-'a'+13)%26 + 'a'
        case 'A' <= c && c <= 'Z':
            p[i] = (c-'A'+13)%26 + 'A'
        default:
        }
    }
    return
}

Я хочу отметить, в частности, что нам не нужно проверять «err» внутри этого кода, особенно, прежде чем обрабатывать все байты, которые мы получили! http://golang.org/pkg/io/#Reader явно говорит: «Вызывающие должны всегда обрабатывать n> 0 байтов, возвращенных перед рассмотрением ошибки err.Это корректно обрабатывает ошибки ввода-вывода, которые происходят после чтения некоторых байтов, а также оба разрешенных поведения EOF ». В исходном коде ясара проверка состояния err сначала проскальзывает правильную обработку n байтов, которые были созданы до возникновения ошибки.

ответил jonrock 17 +04002014-10-17T04:22:44+04:00312014bEurope/MoscowFri, 17 Oct 2014 04:22:44 +0400 2014, 04:22:44
3

Я использовал аналогичную логику для оригинала, но упростил работу модуля:

func (self rot13Reader) Read(p []byte) (n int, err error) {
    n, err = self.r.Read(p)
    for i,v := range p {
        switch {
        case v > 64 && v < 91:
            p[i] = (v - 65 + 13) % 26 + 65
        case v > 96 && v < 123:
            p[i] = (v - 97 + 13) % 26 + 97
        }
    }
    return
}
ответил Keeth 5 PM00000090000005531 2014, 21:48:55
2

Я использовал таблицу поиска, сохраняя код считывателя коротким и кодируя шифр в O (n).

var rot13lot = map[byte]byte{
    'A': 'N',
    /* ... */
    'z': 'm',
}

func (rot13r rot13Reader) Read(p []byte) (n int, err error) {
    n, err = rot13r.r.Read(p)
    for i := 0; i < n; i++ {
        rot, isRot := rot13lot[p[i]]
        if isRot {
            p[i] = rot
        }
    }
    return
}
ответил stefanobaghino 16 Maypm14 2014, 20:30:09

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132