Оператор if внутри цикла while с тем же условием

Есть ли лучший способ написать следующий код, исключив повторяющееся условие в операторе if на C?

while (n < 0) {
   printf("Enter a positive integer: ");
   scanf("%d", &n);

   if (n < 0) {
      printf("Error: please enter a positive integer\n");
   }
}

Спасибо.

Что вы имеете в виду под словом «лучше»? Безопаснее? Быстрее? Более читабельный? Кроме того, какой тип n?

Lundin 31.10.2018 12:34

Во-первых, было бы лучше проверить возвращаемое значение scanf.

Steve Summit 31.10.2018 12:36

Возможно ли для n >= 0 до шлейфа while? Если нет, вы можете превратить его в «бесконечный» цикл и добавить break внутри блока if.

Ian Abbott 31.10.2018 12:36

n - это int. Под «лучше» я подразумеваю устранение повторяющегося условия в операторе if.

user10480767 31.10.2018 12:36

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

Ian Abbott 31.10.2018 12:41

@EricPostpischil Ну хотя бы для шлейфа while! :)

Ian Abbott 31.10.2018 12:43
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
6
1 003
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

Ответ принят как подходящий

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

while (1)
{
   printf("Enter a positive integer: ");
   scanf("%d", &n);

   if (n >= 0)
       break;

   printf("Error: please enter a positive integer\n");
}

И, как указано в комментариях, оптимизированный компилятор должен иметь возможность полностью изменить цикл.

Следующие ниже примеры представлены в том духе, что люди должны знать, что доступно на языке. 1 То, как я обычно пишу код, показано в Ответ Frankie_C. Как отмечали некоторые, оптимизация обычно делает этот простой случай не заслуживающим беспокойства, но вопрос не ограничивается простой оценкой, такой как n < 0; тест может быть вызовом функции для какой-то дорогостоящей оценки более сложных критериев.

Людям это не понравится, но:

    goto middle;
    do
    {
        printf("Error, please enter a positive integer\n");
middle:
        printf("Enter a positive integer: ");
        scanf("%d", &n);
    } while (n < 0);

Если вы категорически против goto, вы можете использовать урезанную версию Устройство Даффа:

    switch (0)
    do
    {
        printf("Error, please enter a positive integer\n");
    case 0:
        printf("Enter a positive integer: ");
        scanf("%d", &n);
    } while (n < 0);

Но не стоит.

Сноска

1 Обычно программистам приходится работать с кодом, написанным другими, поэтому они должны быть готовы распознать и понять все, что можно выразить на языке, даже если это всего лишь первый шаг к переписыванию его в более совершенный код. И иногда возникают ситуации, когда «некрасивый» код становится желательным по коммерческим или другим практическим причинам.

Повышен. Я довольно часто использую второй способ (хотя форматирую его несколько иначе; я пишу switch (0) do { в одной строке): я не уверен, почему вы утверждаете, что вам не следует его использовать.

Bathsheba 31.10.2018 14:39

Это то, что IMO лучше всего выполнить с небольшим рефакторингом:

#include <stdio.h>
#include <stdbool.h>

static bool get_postive_integer(int *pOut) {
    int n;
    printf("Enter a positive integer: ");
    scanf("%d", &n);

    if (n < 0)
        return false;

    *pOut = n;
    return true;
}

int main(void)
{
   int n;
   while (!get_postive_integer(&n)) {
       printf("Error: please enter a positive integer\n");
   }
}

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

Вы можете использовать:

while (printf("Enter a positive integer: ") > 0 &&
       scanf("%d", &n) == 1 &&
       n < 0)
{
    printf("Error: please enter a positive integer\n");
}

Это останавливается, если printf() выходит из строя, если scanf() выходит из строя или если значение в n неотрицательно. Рекомендуется всегда проверять, что scanf() работает успешно. Просто удобно, что printf() возвращает количество записанных символов (или отрицательное число в случае сбоя), поэтому его также можно использовать в условии. Вы также можете добавить fflush(stdout) == 0 && в стек операций.

Или вы можете решить, что код в условии должен быть в функции:

static int read_positive_integer(void)
{
    int value;
    if (printf("Enter a positive integer: ") > 0 &&
        fflush(stdout) == 0 &&
        scanf("%d", &value) == 1 &&
        value >= 0)
        return value;
   return -1;
}

а затем вызывающий код:

while ((n = read_positive_integer()) < 0)
    printf("Error: please enter a positive integer\n");

Есть много вариаций на эту тему; вы можете превратить цикл while в функцию; вы можете превратить подсказки в параметры функции. Вы можете решить быть более осторожными при сообщении о том, что идет не так (различные действия в случае сбоя printf() по сравнению с тем, что происходит, если scanf() возвращает 0 (нечисловые данные на входе) или EOF (во входных данных больше нет данных).

Другой вариант - разделить на функцию:

int func(){
   int n;
   printf("Enter a positive integer: ");
   scanf("%d", &n);
   return scanf("%d", &n) == 1 ? n : -1;
}

и цикл становится

while ((n = func()) < 0){
    printf("Error: please enter a positive integer\n");
}

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


Что я делаю в этой ситуации (см. Ответ Эрика), так это пишу

switch (0) do {
    printf("Error, please enter a positive integer\n");
case 0:
    printf("Enter a positive integer: ");
    scanf("%d", &n);
} while (n/*ToDo - make sure n is initialised if scanf fails*/ < 0);

Знаешь ... Эрик честно говорит об использовании goto. Это здесь просто под видом чего-то структурированного.

StoryTeller - Unslander Monica 31.10.2018 15:33

@StoryTeller: Моя проблема с goto связана как с тем фактом, что он пропускает метку в единицу перевода, так и с эффектом времени выполнения. То, как я это делаю, обходит первое. А switch (0) do - явный сигнал какой-то фанковой идиомы.

Bathsheba 31.10.2018 15:34

Не в единицу перевода, а в функцию. Ярлыки имеют область действия, поэтому они не будут протекать в глобальном масштабе.

StoryTeller - Unslander Monica 31.10.2018 15:35

@StoryTeller: В некотором смысле я счастлив, что не знал этого. Всегда ли так было? Я использовал раннюю версию C очень в университете.

Bathsheba 31.10.2018 15:35

Кажется, столько же лет, сколько C89. Я не знаю, было ли так в K&R.

StoryTeller - Unslander Monica 31.10.2018 15:37

Другие вопросы по теме