Лицензия Creative Commons
Содержимое блога доступно по лицензии Creative Commons Атрибуция — С сохранением условий
(Attribution-ShareAlike) 3.0 Unported
, если не указано иное.

понедельник, 17 января 2011 г.

Замечания по задаче удаления из строки лишних пробелов

Для решения задачи удаления из строки лишних пробелов я использовал конечный автомат в бесконечном цикле.

Я получил от Антона Александровича (преподавателя из НИИТа) несколько замечаний по решению этой задачи:


1. Бесконечный цикл while(1) можно заменить на цикл while(charray [i]).

Известно, что строка (в отличии от массива символов) всегда заканчивается нулевым символом, он же '\0'. Так же известно, что 0 (ноль) в языке Си (да и во многих других ЯП) означает "ложь" (false). Исходя из этих двух фактов становится ясно, что я могу использовать проверку кода i-того символа из строки в условии завершения цикла while().


while('\0' != charray[i]) {
  ...
  i++;
}

Проверку условия можно упростить следующим образом:

а) while('\0' != charray[i]) - цикл выполняется, пока i-й элемент строки не равен символу '\0'
б) while(0 != charray[i]) - цикл выполняется, пока i-й элемент строки не равен нулю
в) while(charray[i]) - цикл выполняется, пока проверка i-го элемента не возвращает значение "false".

Очевидно, что вышеперечисленные варианты равнозначны. Однако, вариант "в" работает не хуже варианта "а", а если нет разницы - то зачем писать больше? Поэтому, в конечном счёте я использовал вариант "в".

Использование вместо бесконечного цикла while(1) цикл с проверкой i-того элемента строки на соответствие нулевому символу позволило избавиться от двух строк в теле цикла:


if(i == len)
  break;

Понятно, что теперь эти строки не нужны, т.к. цикл завершится, когда проверка charray[i] вернёт "false".


2. Из функций можно выкинуть переменную len2.

Возьму для примера функцию удаления лишних пробелов.


int remove_n(char *arr, int len, int pos) {
  int i;
  int len2 = len-1;

  for(i = pos; i <= len2; i++)
    arr[i] = arr[i+1];
  
  return 0;
}

Я использовал дополнительную переменную len2 для сохранения результата вычисления len-1, чтобы не вычислять это значение при каждой итерации в цикле for(). Как я понимаю, в данном случае использование len2 излишне, и цикл можно переписать следующим образом:


for(i = pos; i <= len-1; i++)
  arr[i] = arr[i-1];

Использование промежуточной переменной для хранения результатов вычислений, которые используются в проверке условия завершения цикла или в теле цикла, оправдано в случае, если эти вычисления достаточно сложны. Или же результат вычисления используется несколько раз в теле цикла. Ещё одна цель использования промежуточных переменных - улучшение читаемости программ человеком (если формулы вычислений достаточно сложны).


3. Проверка на наличие лишнего пробела, после работы конечного автомата, не является "костылём"

После размышлений, я понял, почему конечный автомат не может удалить все пробелы в конце строки. Рассмотрим два случая:


    _______________________
1. | * | * | * | * |   | 0 |
    -----------------------
                     ^
    _______________________
2. | * | * | * |   |   | 0 |
    -----------------------
                 ^

Конечный автомат находится в состоянии STATE_1, т.е. ждёт появления в строке пробела. В первом случае, теоретически я могу модернизировать программу следующим образом:


while(charray[i])

  switch(state) {

  case STATE_1:
    if(' ' == charray[i]) {
      state = STATE_2;
      if((0 == i) || !charray[i+1])) // <-- здесь
        continue;
    }
    break;

  case STATE_2:
    ...
    break;

  }

  i++;
}
...

То есть, если символ, находящийся в строке на позиции i+1 является нулём, я перехожу к следующей итерации цикла while(charray[i]) без инкремента i. Таким образом, я могу удалить последний пробел тем же способом, что я удаляю все пробелы в начале строки.

Но если я попробую использовать приведённый выше код для второго варианта (когда пробелов в конце строки больше 1), то опять же получу один лишний пробел в конце строки после работы программы. Почему? А потому, что условие !charray[i+1] никогда не выполнится уже в том случае, если количество пробелов в конце строки будет хотя бы равно двум: элемент строки, находящийся на позиции i+1, так же будет являться пробелом.

Поэтому я согласен с Антоном Александровичем, что проверка на наличие лишнего пробела после работы конечного автомата не является "костылём", а всего лишь дополняет решение задачи.

Тем не менее, я понял, что можно обойтись без дополнительной проверки длины строки, чтобы удалить последний пробел в строке. Ведь переменная i в цикле while() увеличивается до тех пор, пока i-тый элемент в строке не будет равен нулю.

Таким образом, я могу использовать эту переменную и для удаления последнего пробела из строки.


...
while(charray[i]) {
  ...
  i++;
}

if(' ' == charray[i-1])
  charray[i-1] = 0;
...

Комментариев нет:

Отправить комментарий