Пограничные случаи

Функция mysubstr, которую вы реализовали в прошлом уроке, содержит множество ошибок. «Но ведь она прошла проверки!». Да, но в этих проверках не было так называемых пограничных случаев. Функция нормально работала с нормальными аргументами, но как она поведет себя, если передать ей такие варианты длины?

  • 0
  • Отрицательное число
  • Число, превышающее реальный размер строки

Функция mysubstr не рассчитана на такие варианты. Можно подумать, что это не проблема: функция работает в нормальных условиях, и просто не нужно передавать ей «плохие» аргументы. В идеальном мире — да, но в реальном мире ваш код будет запускаться в разных ситуациях, с разными комбинациями условий и данных. Тот, кто будет его запускать, скорее всего не будет читать его внимательно, и даже документацию по нему, если вы ее напишете, он, возможно, не будет читать внимательно. Поэтому он не будет точно знать, с чем ваш код работать сможет, а с чем нет; да и просто он может ошибиться. То есть нельзя быть уверенным, что аргументы всегда будут корректными.

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

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

Поэтому нужно учитывать различные особые случаи, в рамках здравого смысла. Учитывать - означает например постараться избежать порчи данных, которые по логике операции изменяться не должны были; а также, вместо того чтобы “молча” выполнить что-то бессмысленное или вообще ничего - как-то сообщить о проблеме, и тем самым помочь вызывающему программисту, подсказав ему, что он передал недопустимые аргументы.

Например, если функция удаляет из какой-то строки все символы с индексами от N и далее, где N - аргумент функции, то мы могли написать функцию так, что если вдруг ей подали значение N<0, то функция уже первый (с индексом 0) символ посчитает лишним и удалит все. Но не факт, что это разумно: передача отрицательного числа в эту функцию почти наверняка говорит об ошибке программиста, и вместо того чтобы портить строку и продолжать работать как будто так и надо, лучше сгенерировать ошибку, которая укажет вызвавшему нас программисту, что он что-то сделал не так. Мы в этих уроках не говорим о том, как сигнализировать об ошибках, но как минимум, даже не зная этого, мы могли бы добавить в функцию первым действием запрос из строки символа с индексом N-1, и тогда при попытке проделать это при N<0 выполнение функции прервется с ошибкой. А некоторые другие случаи неверного указания аргументов мы можем посчитать безобидными. К примеру, мы можем решить, что указание индекса большего, чем длина строки, нужно понимать так что удалять ничего не нужно - скажем, мы хотим дать вызывающему программисту возможность обрезать несколько строк до одной и той же нужной ему максимальной длины, при этом не заставляя его каждую строку проверять: не короче ли она уже, чем ему нужно. Но даже если мы решили, что такой пограничный случай - корректен, все равно мы это сделали, сначала его обдумав - то есть всегда нужно задаваться вопросом, а какие неожиданные для нас значения аргументов нам могут передать.

Заметим, что можно было написать функцию и таким образом, что она в цикле идет от индекса N в сторону возрастания до тех пор, пока счетчик цикла не станет равен длине строки-1. И тогда, если вдруг на вход подали N большее чем длина строки, то счетчик цикла никогда не станет равным длине строки-1, и цикл станет вечным. Нет, на самом деле не станет, потому что при попытке увелить счетчик свыше максимально допустимого значения целого числа в Java произойдет ошибка; но это не наша заслуга, нас спас компилятор, добавляющий такие проверки автоматически. Но все равно нет ничего хорошего, если перед этим тело цикла успеет выполниться 2 миллиарда раз. А если бы в нем выводилось на экран текущее значение счетчика?


Давайте представим себе расширенную функцию mysubstr. Она принимает три аргумента: строку, индекс и длину извлекаемой подстроки. Функция возвращает подстроку указанной длины начиная с указанного индекса. Примеры вызова:

var str = "If I look back I am lost";
mysubstr(str, 0, 1); // => 'I'
mysubstr(str, 3, 6); // => 'I look'

Прикинем, что может пойти не так. Какие пограничные случаи стоит учитывать:

  • Отрицательная длина извлекаемой подстроки
  • Отрицательный заданный индекс
  • Заданный индекс выходит за границу всей строки
  • Длина подстроки в сумме с заданным индексом выходит за границу всей строки

В реализации функции каждый пограничный случай будет отдельным куском кода, скорее всего реализованным с помощью if.

Чтобы написать функцию mysubstr и защититься от этих случаев, стоит написать отдельную функцию, которая будет проверять аргументы на корректность. Займемся этим в задании.

Задание

Реализуйте функцию-предикат isArgumentsForSubstrCorrect, которая принимает три аргумента:

  1. строку
  2. индекс, с которого начинать извлечение
  3. длину извлекаемой подстроки

Функция возвращает false, если хотя бы одно из условий истинно:

  • Отрицательная длина извлекаемой подстроки
  • Отрицательный заданный индекс
  • Заданный индекс выходит за границу всей строки
  • Длина подстроки в сумме с заданным индексом выходит за границу всей строки

В ином случае функция возвращает true.

Не забывайте, что индексы начинаются с 0, поэтому индекс последнего элемента — это «длина строки минус 1».

Пример вызова:

var str = "Sansa Stark";
isArgumentsForSubstrCorrect(str, -1, 3); // => false
isArgumentsForSubstrCorrect(str, 4, 100); // => false
isArgumentsForSubstrCorrect(str, 10, 10); // => false
isArgumentsForSubstrCorrect(str, 11, 1); // => false
isArgumentsForSubstrCorrect(str, 3, 3); // => true
isArgumentsForSubstrCorrect(str, 0, 5); // => true

Нашли ошибку? Есть что добавить? Пулреквесты приветствуютсяhttps://github.com/hexlet-basics

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

Пожалуйста, авторизуйтесь, это необходимо для отслеживания прогресса выполнения уроков. Если у вас ещё нет учётной записи, то сейчас самое время создать аккаунт.