Проверка эквивалентности карт в Go с одинаковым содержимым, но проверка не удалась

Вот функция подсчета слов в Go

package wc

import (
    "regexp"
    "strings"
)

type Histogram map[string]int

func WordCount(input string) Histogram {
    histogram := make(map[string]int)
    re := regexp.MustCompile("[^a-zA-Z0-9 ]*")
    input = re.ReplaceAllString(input, "")

    for _, word := range strings.Split(input, " ") {
        if word == "" {
            continue
        }
        histogram[strings.ToLower(word)]++
    }

    return histogram
}

Этот код проходит или не проходит тесты недетерминировано. Иногда это не удавалось из-за несоответствия ожидаемой карты и фактической карты. Однако содержание обоих абсолютно одинаково. Я думаю, что есть некоторая проблема со сравнением карт. Я не знаю, как я могу это исправить. Кто-нибудь, помогите мне, пожалуйста!

Вот код набора тестов

package wc

import (
    "fmt"
    "testing"
)

var testCases = []struct {
    description string
    input       string
    output      Histogram
}{
    {
        description: "a single word",
        input:       "word",
        output:      Histogram{"word": 1},
    },
    {
        description: "one of each",
        input:       "one of each",
        output:      Histogram{"one": 1, "of": 1, "each": 1},
    },
    {
        description: "multiple occurrences",
        input:       "one fish two fish red fish blue fish",
        output:      Histogram{"one": 1, "fish": 4, "two": 1, "red": 1, "blue": 1},
    },
    {
        description: "ignore punctuation",
        input:       "car : carpet as java : javascript!!&@$%^&",
        output:      Histogram{"car": 1, "carpet": 1, "as": 1, "java": 1, "javascript": 1},
    },
    {
        description: "including numbers",
        input:       "testing, 1, 2 testing",
        output:      Histogram{"testing": 2, "1": 1, "2": 1},
    },
    {
        description: "normalises case",
        input:       "go Go GO",
        output:      Histogram{"go": 3},
    },
}

func TestWordCount(t *testing.T) {
    for _, tt := range testCases {
        expected := fmt.Sprintf("%v", tt.output)
        actual := fmt.Sprintf("%v", WordCount(tt.input))

        if expected != actual {
            t.Fatalf("%s\n\tExpected: %v\n\tGot: %v", tt.description, expected, actual)
        } else {
            t.Logf("PASS: %s - WordCount(%s)", tt.description, tt.input)
        }
    }
}

Ниже приведены примеры аварийной ситуации:

1.
Expected: map[two:1 red:1 blue:1 one:1 fish:4]
Got: map[one:1 fish:4 two:1 red:1 blue:1]
2.
Expected: map[one:1 fish:4 two:1 red:1 blue:1]
Got: map[red:1 blue:1 one:1 fish:4 two:1]
3.
Expected: map[java:1 javascript:1 car:1 carpet:1 as:1]
Got: map[javascript:1 car:1 carpet:1 as:1 java:1]
...

Дополнительная информация находится здесь: http://exercism.io/submissions/cf94f4732fd97335be2e755f


person ChangHun Lee    schedule 13.02.2014    source источник


Ответы (2)


Вы не можете сравнивать expected и actual с !=, потому что он сравнивает строковое представление карт, поэтому он будет работать только случайным образом (если значения печатаются в одном и том же порядке).

Что вам нужно сделать, так это использовать метод reflect пакета DeepEqual() для сравнения карт:

import "reflect"
// ...

if !reflect.DeepEqual(tt.output, WordCount(tt.input)) {
// ...

Сначала он проверяет, являются ли обе карты nil, затем имеют ли они одинаковую длину, а затем имеют ли они одинаковый набор пар (ключ, значение).

person Florent Bayle    schedule 13.02.2014
comment
Я пробовал это, но это не работает (я получил те же результаты, что и выше). Я не знаю почему. - person ChangHun Lee; 13.02.2014
comment
Я знаю, как решить эту проблему вручную. Однако я хотел бы обсудить эту проблему. Почему метод !reflect.DeepEqual не работает с этой проблемой? - person ChangHun Lee; 13.02.2014
comment
@ChangHunLee Ой, плохо, вам нужно сравнивать карты !reflect.DeepEqual(tt.output, WordCount(tt.input)), а не expected и actual (которые являются строками). - person Florent Bayle; 13.02.2014
comment
@ChangHunLee Я отредактировал ответ с исправлением. - person Florent Bayle; 13.02.2014
comment
Да! Я понял свою вину! Благодарю вас! - person ChangHun Lee; 13.02.2014
comment
Вы не можете сравнивать две карты с помощью !=, потому что он сравнивает строковое представление карт. Вы никогда не можете сравнивать две карты с помощью !=. Ни из-за чего. И он не сравнивает карты с !=; он сравнивает строковые представления. Это две разные вещи. - person newacct; 14.02.2014
comment
@newacct Да, неправильная формулировка, я отредактировал ответ, чтобы исправить это. Спасибо. - person Florent Bayle; 14.02.2014

Вы не сравниваете две карты, вы сравниваете вывод String() двух карт. Однако, когда карта печатается или используется range(), содержимое выбирается случайным образом, поэтому вы не можете сравнивать его со строками.

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

person siritinga    schedule 13.02.2014
comment
Должен ли я делать это вручную? Я хотел бы найти какой-то способ или метод, чтобы решить эту проблему сразу. Я нашел reflect.DeepEqual метод. Но это не работает. Это ошибка Go? - person ChangHun Lee; 13.02.2014