Визначення того, чи є число кратним десяти, або в межах певного набору діапазонів


103

У мене є кілька циклів, які мені потрібні у своїй програмі. Я можу виписати псевдо-код, але я не зовсім впевнений, як їх записати логічно.

Мені потрібно -

if (num is a multiple of 10) { do this }

if (num is within 11-20, 31-40, 51-60, 71-80, 91-100) { do this }
else { do this } //this part is for 1-10, 21-30, 41-50, 61-70, 81-90

Це для настільної гри змій та сходів, якщо це має більше сенсу для мого питання.

Я уявляю перше, якщо твердження, що мені потрібно буде використовувати модуль, було if (num == 100%10)б правильним?

Другий я поняття не маю. Я можу це написати так, if (num > 10 && num is < 21 || etc)але має бути щось розумніше.


16
Як правило, довжина хорошого коду пропорційна довжині англійської мови, що описує те, що він робить. Тож, коли у вашій "специфікації" написано 11-20, 31-40, 51-60, 71-80, 91-100, ви можете очікувати, що ваш код також буде згадувати ці цифри. Якщо ці числа походять звідкись або були породжені з якоїсь причини, подивіться, чи можете ви кодувати причину, а не числа.
luqui

39
@ user3419168: компілятор дбає не про те, наскільки читається ваш код; він складе його за частку секунди. Але для людей, які читають ваш код, ваш вибір може спричинити його розуміння за секунди, хвилини, години чи ніколи. Це накладає витрати; люди платять за читання та розуміння коду, тому полегшуйте їх. Завжди пишіть виробничий код для максимальної читабельності та пам’ятайте, що чіткість не обов'язково робить код ефективнішим.
Ерік Ліпперт

2
@AmadeusDrZaius - Я дуже рідко робив те саме, але лише для критичних розділів продуктивності. Найкруткіший цикл, який називається в 100 М разів, відповідає критеріям if у грі змій та драбинок. Де ви проводите межу між ними - це особистий вибір.
Флоріс

2
Я ненавиджу це говорити, але, зробивши достатньо корпоративної роботи, починаючи з написання реального коду, я б порекомендував грубе форсування. Бо тоді нові хлопці це зрозуміють і не порушать. сумно, але правда - в деяких випадках розумний бути німим.
Річард Ле Месюр'є

22
Це гідне питання, і я взагалі нічого не хочу знімати з афіші, але це не заслуговує 500+ балів. Ось як ми закінчуємо деякі дурниці, які ми робимо з людьми, у яких тисячі пунктів виявляються владою. (Не соромтеся перемістити цей коментар, якщо він належить в іншому місці.)
GaTechThomas

Відповіді:


86

Перший - перевірити, чи є число кратним для використання:

if (num % 10 == 0) // It's divisible by 10

Для другого:

if(((num - 1) / 10) % 2 == 1 && num <= 100)

Але це досить щільно, і вам може бути краще просто вказати варіанти.


Тепер, коли ви краще зрозуміли, чим займаєтесь, я напишу другий як:

   int getRow(int num) {
      return (num - 1) / 10;
   }

   if (getRow(num) % 2 == 0) {
   }

Це та сама логіка, але за допомогою функції ми отримуємо чіткіше уявлення про те, що це означає.


79
if((num - 1) / 10) % 2 == 1 && num < 100)- Я б плакала, якби це бачила.
Даніель Каміль Козар

32
@DanielKamilKozar, як слід.
Вінстон Еверт

2
@ user3419168, він сам по собі залишає питання, що в світі це означає. Це не дає ніяких підказів щодо того, що у світі намагається зробити. Ось чому в редагуванні я показав версію, яка розбиває логіку на функцію, яка дозволяє зрозуміти, що насправді роблять обчислення.
Вінстон Еверт

3
Може бути доцільним також стверджувати num >= 11як (1), що нижня межа забороняється, а (2) %на від’ємне число також повертає від’ємне число. (Я мушу визнати, що використання & 1тут "безпечніше", але також передбачає додаткові знання.)
usr2564301

2
+1 для редагування, він потрапляє до списку діапазонів і подає його в читаному вигляді. IMO, одним із кроків було б ввімкнути getRow(num) % 2 == 0функцію, а також зрозуміти, що таке наміри. bool inEvenRow(int num){ return getRow(num) % 2 ==0;}
МістерМіндор

40

if (число кратне 10) {do this}

if (num % 10 == 0) {
  // Do something
}

if (число знаходиться в межах 11-20, 31-40, 51-60, 71-80, 91-100) {зробіть це}

Трюк тут - шукати якусь спільність серед діапазонів. Звичайно, ви завжди можете скористатися методом "грубої сили":

if ((num > 10 && num <= 20) ||
    (num > 30 && num <= 40) ||
    (num > 50 && num <= 60) ||
    (num > 70 && num <= 80) ||
    (num > 90 && num <= 100)) {
  // Do something
}

Але ви можете помітити , що, якщо відняти 1з num, ви будете мати діапазони:

10-19, 30-39, 50-59, 70-79, 90-99

Іншими словами, всі двоцифрові числа, перша цифра яких непарна. Далі вам потрібно придумати формулу, яка виражає це. Ви можете отримати першу цифру шляхом ділення на 10, а ви можете перевірити, що це непарно, перевіривши на залишок 1, коли ділиться на 2. Помістивши, що всі разом:

if ((num > 0) && (num <= 100) && (((num - 1) / 10) % 2 == 1)) {
  // Do something
}

Враховуючи компроміс між більш довгим, але підтримуваним кодом і коротшим "розумним" кодом, я б вибирав довший і зрозуміліший кожен раз. Принаймні, якщо ви намагаєтесь бути розумними, будь ласка, додайте коментар, який пояснює, що саме ви намагаєтеся зробити.

Це допомагає припустити, що наступний розробник працює над кодом озброєним і знає, де ви живете. :-)


7
Я б все-таки шукав розумний код, але перетворив його на бездоганний код, витягуючи функції. Це було б так само читано, якби цей останній біт сказав && isTensDigitOdd(num), можливо, з коментарем до визначення функції, що пояснює, що він робить. Якщо така закономірність існує, коментар, що пояснює міркування шаблону, є освічуючим для imotatability imo.
chris

3
Кріс, це відмінна стратегія, коли «кмітливість» має очевидну перевагу: набагато коротший код (що означає менший шанс помилкового помилки, особливо якщо він зміниться) або значне підвищення ефективності. Майже завжди існує компроміс між стислістю, ясністю та ефективністю, і знайти хороший компроміс - це велика майстерність. (Див. Stackoverflow.com/a/2151844/29157 для снайкера.)
Адам Лісс,

1
Це набагато кращий підхід. Так набагато простіше зрозуміти, ніж «розумний код», і різниця в продуктивності, ймовірно, незначна.
користувач1477388

@AdamLiss, Так, моя думка малоцінна, оскільки я не мав достатнього досвіду, щоб побачити наслідки цих рішень. Я впевнений, що незабаром досить, і обов'язково отримаю другу думку, якщо це буде потрібно.
chris

1
Не продавайте себе коротко. Ваші інстинкти дуже розумні, і вам здається, що прагнете продовжувати вчитися. Кожна думка цінна, якщо за нею є вагомі причини ... а іноді навіть якщо їх немає. Я б ставлю гроші, що ти підеш далеко.
Адам Лісс

30

Якщо ви використовуєте GCC або будь-який компілятор, який підтримує діапазони регістрів, ви можете це зробити, але ваш код не буде портативним .

switch(num)
{
case 11 ... 20:
case 31 ... 40:
case 51 ... 60:
case 71 ... 80:
case 91 ... 100:
    // Do something
    break;
default:
    // Do something else
    break;
}

1
Скажіть, будь ласка, чому цей код не портативний?
M Sharath Hegde

8
@MSharathHegde тому, що для цього потрібне розширення GCC, яке не є частиною стандарту, і якийсь компілятор не підтримуватиме його
Bryan Chen,

5
Це правильна відповідь, адже відразу видно, у чому полягає наміри. Усі ці "розумні" відповіді з модулем - це кошмар, що підтримує, навіть із коментарями.
усміхнений

@smirkingman Дійсно саме це я і сказав у своєму коментарі до головного питання. Просто потрібен певний досвід нових кодерів на корпоративної роботі, щоб зрозуміти, що очевидний спосіб часто набагато кращий, ніж спосіб розумного ніндзя.
Річард Ле Месюр'є

15

Це для майбутніх відвідувачів більше, ніж для початківців. Для більш загального рішення, подібного до алгоритму, ви можете взяти список початкових і кінцевих значень і перевірити, чи є передане значення в межах одного з них:

template<typename It, typename Elem>
bool in_any_interval(It first, It last, const Elem &val) {
    return std::any_of(first, last, [&val](const auto &p) {
        return p.first <= val && val <= p.second;
    });
}

Для простоти я використав поліморфну ​​лямбду (C ++ 14) замість явного pairаргументу. Це також, ймовірно, повинно дотримуватися використання <та ==відповідати стандартним алгоритмам, але воно працює так, поки Elemце <=визначено. У будь-якому випадку, його можна використовувати так:

std::pair<int, int> intervals[]{
    {11, 20}, {31, 40}, {51, 60}, {71, 80}, {91, 100}
};

const int num = 15;
std::cout << in_any_interval(std::begin(intervals), std::end(intervals), num);

Там є живий приклад тут .


Охайний розчин. Я, мабуть, використав би один масив, оскільки ви можете відформатувати його з 2 числами на рядок, щоб представити пари.
Кевін Лам

@ HunterGuy2, Дуже добре. Я фактично збираюся змінити його для роботи на парах, бо я чомусь тільки думав про поштові ітератори.
chris

Дійсно приємний підхід stl! Любіть це!
higuaro

5

Перший - це легко. Вам просто потрібно застосувати оператор модуля до вашого числового значення:

if ( ( num % 10 ) == 0)

Оскільки C ++ оцінює кожне число, яке не дорівнює 0, як істинне, ви також можете написати:

if ( ! ( num % 10 ) )  // Does not have a residue when divided by 10

Для другого, я думаю, це зрозуміліше:

Шаблон повторюється кожні 20, тому ви можете обчислити модуль 20. Усі потрібні елементи будуть в ряд, крім тих, які поділяються на 20.

Щоб отримати їх теж, просто використовуйте число-1 або краще число + 19, щоб не мати справу з негативними числами.

if ( ( ( num + 19 ) % 20 ) > 9 )

Це припущення, що модель повторюється назавжди, тому для 111-120 вона застосовуватиметься знову тощо. Інакше вам потрібно обмежити числа до 100:

if ( ( ( ( num + 19 ) % 20 ) > 9 ) && ( num <= 100 ) )

5

Маючи пару хороших коментарів у коді, це може бути написано досить стисло та читано.

// Check if it's a multiple of 10
if (num % 10 == 0) { ... }

// Check for whether tens digit is zero or even (1-10, 21-30, ...)
if ((num / 10) % 2 == 0) { ... }
else { ... }

2
Перший коментар зайвий. Будь-який програміст з невеликим досвідом знатиме, що num % 10 == 0це те саме, що numє кратним 10.
Джастін

7
так, але і початківці читають цей сайт. Я зазвичай не використовую цей коментар у своєму власному коді, але він робить зрозумілішим відповідь для початківців, хто отримає користь від цього питання для початківців.
La-comadreja

2
Будь ласка, ніколи цього не робіть. Це фактично знижує читабельність, уповільнюючи читача і змушуючи їх прочитати все двічі. Будь-який програміст, який не розуміє це if (num % 10 == 0)означає те саме, що // Check if it's a multiple of 10не повинен підтримувати ваш код. Це добре відомий анти-шаблон.
Давуд ібн Карім

1
@DavidWallace див. Коментар вище. Ми не можемо гарантувати, що читачі цієї публікації знають цю антидіаграму.
La-comadreja

1
Ні, я маю на увазі, що коментуючи кожен рядок, щоб сказати, що це робиться, це анти-шаблон. Я не маю на увазі, що використання %є антидіаграмою; очевидно, що це не так. Дійсно, якщо припустити, що багато читачів цього допису будуть початківцями, навчання цього стилю написання коментарів вносить негативний внесок у їх розвиток як програмістів.
Давуд ібн Карім

4

Ви в принципі самі пояснили відповідь, але ось код на всякий випадок.

if((x % 10) == 0) {
  // Do this
}
if((x > 10 && x < 21) || (x > 30 && x < 41) || (x > 50 && x < 61) || (x > 70 && x < 81) || (x > 90 && x < 101)) {
  // Do this
}

2
Виправте x < 41 x > 50і поставте дужки.
101010,

1
@ 40two Технічно operator&&має вищий пріоритет ніж operator||, тому це добре, але я впевнений, що GCC про це так чи інакше попереджає.
chris

18
Розглянемо подання нерівності 10 < x < 21, 10 < x && x < 21а не x > 10 && x < 21. Простіше читати нерівність, коли вона йде в тому ж порядку, як і ти, записавши її математично.
Ерік Ліпперт

5
Цей код є досить нечитабельним і мало говорить про фактичну логіку. Мені не подобається ця відповідь.
Даріуш

3
Я спростовую це, тому що ви відповіли, що саме зробила ОП.
Бруно Феррейра

3

Ви можете це переосмислити.

if (x % 10)
{
   .. code for 1..9 ..
} else
{
   .. code for 0, 10, 20 etc.
}

Перший рядок if (x % 10)працює тому, що (a) значення, кратне 10 обчислюється як '0', інші числа призводять до їх залишку, (b) значення 0 в an ifвважається false, будь-яке інше значенняtrue .

Редагувати:

Щоб перемикати назад і назад у двадцяті роки, використовуйте той самий трюк. Цього разу основне число 10:

if (((x-1)/10) & 1)
{
  .. code for 10, 30, ..
} else
{
   .. code for 20, 40, etc.
}

x/10повертає будь-яке число від 0 до 9 як 0, від 10 до 19 як 1і так далі. Тестування на парне чи непарне - те & 1- говорить про те, чи парне чи непарне. Оскільки ваші діапазони насправді "11-20", віднімайте 1 перед тестуванням.


1

Заява про читаність

Хоча у вас вже є кілька хороших відповідей, я хотів би порекомендувати техніку програмування, яка зробить ваш код більш читабельним для майбутнього читача - таким ви зможете через півроку, колега попросив переглянути перевірку коду, ваш наступник, .. .

Це для того, щоб перетворити будь-які «розумні» заяви у функцію, яка точно (із своїм ім’ям) показує, що вона робить. Незважаючи на незначний вплив на продуктивність (від "функція виклику над головою"), це справді незначно в такій ігровій ситуації.

Попутно ви зможете очистити свої дані - наприклад, перевірити на "незаконні" значення. Таким чином, у вас може виникнути такий код - подивіться, наскільки він читає? "Помічні функції" можна десь заховати (не потрібно бути в основному модулі: з їх імені зрозуміло, що вони роблять):

#include <stdio.h>

enum {NO, YES, WINNER};
enum {OUT_OF_RANGE=-1, ODD, EVEN};

int notInRange(int square) {
  return(square < 1 || square > 100)?YES:NO;
}

int isEndOfRow(int square) {
  if (notInRange(square)) return OUT_OF_RANGE;
  if (square == 100) return WINNER; // I am making this up...
  return (square % 10 == 0)? YES:NO;
}

int rowType(unsigned int square) {
  // return 1 if square is in odd row (going to the right)
  // and 0 if square is in even row (going to the left)
  if (notInRange(square)) return OUT_OF_RANGE; // trap this error
  int rowNum = (square - 1) / 10;
  return (rowNum % 2 == 0) ? ODD:EVEN; // return 0 (ODD) for 1-10, 21-30 etc.
                                       // and 1 (EVEN) for 11-20, 31-40, ...
}

int main(void) {
  int a = 12;
  int rt;
  rt = rowType(a); // this replaces your obscure if statement

  // and here is how you handle the possible return values:
  switch(rt) {
  case ODD:
    printf("It is an odd row\n");
    break;
  case EVEN:
    printf("It is an even row\n");
    break;
  case OUT_OF_RANGE:
    printf("It is out of range\n");
    break;
  default:
    printf("Unexpected return value from rowType!\n");
  }

  if(isEndOfRow(10)==YES) printf("10 is at the end of a row\n");
  if(isEndOfRow(100)==WINNER) printf("We have a winner!\n");
}

3
Хіба не намагається проштовхнути його занадто далеко YESі NO?
rmobis

@Raphael_ - це може бути так: я показував "наприклад". Багато людей використовують істинно / хибно, очевидно. Але я ніколи не можу згадати (тому що різні мови використовують різні угоди): це є TRUE, Trueабо true? І що, якщо вони є, заголовкові файли мені потрібно було б включити до звичайного C? Тож я згорнув своє. Цікаво, що це те, що отримало протиріччя ...
Флоріс

1

Для першого:

if (x % 10 == 0)

застосовуватиметься до:

10, 20, 30, .. 100 .. 1000 ...

Для другого:

if (((x-1) / 10) % 2 == 1)

подасть заявку на:

11-20, 31-40, 51-60, ..

Ми в основному спочатку робимо, x-1щоб отримати:

10-19, 30-39, 50-59, ..

Потім ми ділимо їх на, 10щоб отримати:

1, 3, 5, ..

Тож ми перевіряємо, чи цей результат непарний.


1

Ви можете спробувати наступне:

        // multiple of 10
        if ((num % 10) == 0)
        {
           // Do something
        }
        else if (((num / 10) % 2) != 0)
        {
            //11-20, 31-40, 51-60, 71-80, 91-100
        }
         else
        {
            //other case
        }

У питанні щодо ОП перевірка на кратну 10 не пов'язана з перевіркою дальності, а в діапазоні перевірки 20 має бути в тому ж діапазоні 11, з вашим кодом ((20/10)% 2) -> ( 2% 2) -> 0
Серпітон

0

Я знаю, що на це запитання так багато відповідей, але я все одно кину свою тут ...

Взяте з повного коду Стіва Макконнелла , 2-е видання: "Таблиці доступу по сходах:

Ще одним видом доступу до столу є метод сходинок. Цей спосіб доступу не такий прямий, як структура індексу, але він не витрачає стільки простору даних. Загальне уявлення про сходові структури, проілюстроване на рисунку 18-5, полягає в тому, що записи в таблиці є дійсними для діапазонів даних, а не для окремих точок даних.

Введіть тут опис зображення

Малюнок 18-5 Підхід по сходах класифікує кожен запис, визначаючи рівень, на якому він потрапляє на "сходи". "Крок", який він потрапив, визначає його категорію.

Наприклад, якщо ви пишете програму оцінювання, діапазон введення “B” може становити від 75 відсотків до 90 відсотків. Ось діапазон оцінок, які ви могли б запрограмувати колись:

Введіть тут опис зображення

Щоб використовувати метод східчастого кроку, ви кладете верхній кінець кожного діапазону в таблицю, а потім пишете цикл, щоб перевірити бал проти верхнього кінця кожного діапазону. Коли ви знайдете точку, в якій оцінка вперше перевищує верхню частину діапазону, ви знаєте, що таке оцінка. Завдяки техніці сходи, потрібно бути обережним, щоб правильно обробляти кінцеві точки діапазонів. Ось код у Visual Basic, який присвоює оцінки групі студентів на основі цього прикладу:

Введіть тут опис зображення

Хоча це простий приклад, ви можете легко узагальнити його для обробки декількох учнів, декількох схем оцінювання (наприклад, різних оцінок для різних рівнів балів у різних завданнях) та змін у схемі оцінювання ".

Код повний , 2-е видання, сторінки 426 - 428 (Глава 18).


чому ти вважаєш, що це неправильне питання? тільки те, що я не
наводив

0

Як зазначають інші, збільшення умов більш чітким не прискорить компіляцію чи виконання, а також не обов'язково допоможе читати.

Це може допомогти зробити вашу програму гнучкішою, якщо ви згодом вирішите, що хочете версію гри для малюка на дошці розміром 6 х 6 або вдосконалену версію (в яку можна грати всю ніч) на дошці розміром 40 х 50. .

Тому я б це кодував так:

// What is the size of the game board?
#define ROWS            10
#define COLUMNS         10

// The numbers of the squares go from 1 (bottom-left) to (ROWS * COLUMNS)
// (top-left if ROWS is even, or top-right if ROWS is odd)
#define firstSquare     1
#define lastSquare      (ROWS * COLUMNS)
// We haven't started until we roll the die and move onto the first square,
// so there is an imaginary 'square zero'
#define notStarted(num) (num == 0)
// and we only win when we land exactly on the last square
#define finished(num)   (num == lastSquare)
#define overShot(num)   (num > lastSquare)

// We will number our rows from 1 to ROWS, and our columns from 1 to COLUMNS
// (apologies to C fanatics who believe the world should be zero-based, which would
//  have simplified these expressions)
#define getRow(num)   (((num - 1) / COLUMNS) + 1)
#define getCol(num)   (((num - 1) % COLUMNS) + 1)

// What direction are we moving in?
// On rows 1, 3, 5, etc. we go from left to right
#define isLeftToRightRow(num)    ((getRow(num) % 2) == 1)
// On rows 2, 4, 6, etc. we go from right to left
#define isRightToLeftRow(num)    ((getRow(num) % 2) == 0)

// Are we on the last square in the row?
#define isLastInRow(num)    (getCol(num) == COLUMNS)

// And finally we can get onto the code

if (notStarted(mySquare))
{
  // Some code for when we haven't got our piece on the board yet
}
else
{
  if (isLastInRow(mySquare))
  {
    // Some code for when we're on the last square in a row
  }


  if (isRightToLeftRow(mySquare))
  {
    // Some code for when we're travelling from right to left
  }
  else
  {
    // Some code for when we're travelling from left to right
  }
}

Так, це багатослівно, але це дає зрозуміти, що саме відбувається на ігровій дошці.

Якщо я розробляв цю гру для відображення на телефоні чи планшеті, я би робив змінні ROWS та COLUMNS замість констант, щоб їх можна було динамічно встановлювати (на початку гри), щоб вони відповідали розміру екрана та орієнтації.

Я б також дозволив змінити орієнтацію екрану в будь-який час, в середині гри - все, що вам потрібно зробити, це переключити значення ROWS і COLUMNS, залишаючи все інше (поточний номер квадрата, на якому кожен гравець включений, і початкові / кінцеві квадрати всіх змій та сходів) без змін. Тоді вам "просто" слід красиво намалювати дошку та написати код для анімації (я припускаю, що це було метою ваших ifзаяв) ...


Я також проголосував відповідь Флоріса - інший стиль досягнення подібного результату, якого я не бачив, перш ніж написав свою відповідь
Лоранс Реншо,

2
вам слід використовувати функцію inline замість#define
Bryan Chen

Доброю практикою, коли ви користуєтесь функціональними #defineвказівками, є розміщення дужок навколо аргументів, де вони з'являються в розширенні. Тож замість #define finished(num) (num == lastSquare)вас слід писати #define finished(num) ((num) == lastSquare). Причина полягає в тому, що якщо ви використовуєте таку інструкцію з виразом, що містить оператора з досить низьким пріоритетом, ви не отримаєте очікуваної відповіді. У цьому випадку, якщо ви не використовуєте зайві дужки, вони finished(a & b)розширюються, (a & b == lastSquare)а це майже точно не те, що ви хочете.
Давуд ібн Карім
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.