Ката программирования на C# — "Палиндром"

Сегодня рассмотрим первую ката «Палиндром». Напомню, что ката в программировании, это упражнение, которое должно занимать не более 10 минут, которое выполняется снова и снова, для совершенствования скорости и качества написания кода. Определение, является ли слово палиндром в качестве первого ката выбрано по 2 причинам, во-первых, задача поиска палиндрома достаточно проста и её написание займет точно не более 10 минут, во-вторых она не столь тривиальна, как кажется на первый взгляд, но в сети очень легко найти примеры достаточно сложных палиндромов для тестов.

Начнём.

Первым тестом проверим строку на null, есть она равна null пусть метод, определяющий палиндром, возвращает исключение «ArgumentNullException» с именем параметра «value».

Первый тест будет выглядеть следующим образом:
public void StringIsNull()
{
    string str = null;
    try
    {
        var result = str.IsPalindrome();
    }
    catch (ArgumentNullException e)
    {
        Assert.AreEqual("value", e.ParamName);
    }
   
}

Теперь добьёмся того, чтобы наш тест проходил. Создадим расширение для типа String. С методом IsPalindrome. Пусть он всегда возвращает true. А если входящий объект равен null, пусть возвращается исключение ArgumentNullException

if(str == null) throw new ArgumentNullException("value");
Запустим тест, убедимся что он проходит. Теперь пишем следующий тест. В нем мы проверим, является ли палиндромом пустая строка. Думаю, что является.
public void StringIsEmpty()
{
    var str = "";
    var result = str.IsPalindrome();
    Assert.IsTrue(result);
}

Запустим наш тест, и убедимся, что он проходит. Следующий тест будет определять палиндром строки из одного символа.

public void StringContainsOneLetter()
{
    var str = "a";
    var result = str.IsPalindrome();
    Assert.IsTrue(result);
}

Данный тест также проходит, ведь в существующей реализации всё что не является null, является палиндромом. Пришло проверить слово, не являющееся палиндромом.

public void NotPalindrome()
{
    var str = "abcdefg";
    var result = str.IsPalindrome();
    Assert.IsFalse(result);
}
 

Данный тест не прошёл, пришло время дополнять метод. Реализуем простую логику проверки строки. Будем проходить от начала строки до её середины, сравнивая первый символ с последним, второй с предпоследним и т.д. Если на каком-то этапе символы не совпадают, возвращаем false.

if(str == null) throw new ArgumentNullException("value");
for (var i = 0; i < str.Length/2; i++)
{
    if (str[i] != str[str.Length - i - 1])
    {
        return false;
    }
}
return true;

Запускаем тесты, все они теперь проходят. Следующим тестом проверим строку, содержащую символы различного регистра, ведь слова могут быть записаны по-разному, но для определения палиндрома это не должно иметь значения.

public void IsPalindromeWithRegister()
{
    var str = "Shahs";
    var result = str.IsPalindrome();
    Assert.IsTrue(result);
}

Дополним сравнение символов приведением к нижнему регистру.

for (var i = 0; i < str.Length/2; i++)
    {
        if (char.ToLower(str[i]) != char.ToLower(str[str.Length - i - 1]))
        {
            return false;
        }
            }

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

public void IsPalindromeWithPunctuation()
{
    var str = "Was it a car, or a cat I saw?";
    var result = str.IsPalindrome();
    Assert.IsTrue(result);
}

Дополним метод так, чтобы в строке учитывались только буквы и цифры.

public static bool IsPalindrome(this string str)
{
    if(str == null) throw new ArgumentNullException("value");
    str = string.Join("", str.Where(char.IsLetterOrDigit).Select(char.ToLower));
    for (var i = 0; i < str.Length/2; i++)
    {
        if (char.ToLower(str[i]) != char.ToLower(str[str.Length - i - 1]))
        {
            return false;
        }
    }
    return true;
}

Теперь все тесты проходят.

Можно еще добавить несколько тестов, пытаясь «сломать» наше расширение. Убедившись, что все они проходят, можно завершать нашу первую ката.

Увидеть исходный код данного упражнения можно в GitHub оепозитории по ссылке 

Добавить комментарий