Можно ли определить переменную в выражении в C ++? - PullRequest
1 голос
/ 03 октября 2009

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

Проблема в том, что все должно быть в одном выражении, поэтому я не могу использовать ;

Есть ли варианты определения переменной в выражении? Что-то вроде

d < 31 && (bool leapyear = y % 4 == 0) || (leapyear ? d % 2 : 3) ....

где я мог бы определить и инициализировать одну или несколько переменных и использовать их в этом одном выражении без использования ;?

Edit: явно сказано, что это должно быть однострочное выражение. Нет функций ..

То, как я сейчас это делаю, - это написание макросов и их расширение, так что в итоге я получаю такие вещи

#define isJulian(d, m, y) (y < 1751 || (y == 1752 && (m < 9) || (m == 9 && d <= 2)))
#define isJulianLoopYear(y) (y % 4 == 0)
#define isGregorian(d, m, y) (y > 1573 || (y == 1752 && (m > 9) || (m == 9 && d > 13)))
#define isGregorianLoopYear(y) ((y % 4 == 0) || (y % 400 = 0))
// etc etc ....

похоже, что это единственный подходящий способ решения проблемы

изменить: вот оригинальный вопрос

Предположим, у нас есть переменные d m и y, содержащие день, месяц и год. Задача состоит в том, чтобы написать одно выражение, которое решает, является ли дата действительной или нет. Значение должно быть истинным (ненулевое значение), если дата действительна, и ложным (ноль), если дата недействительна.

Это пример выражения (правильное выражение будет выглядеть примерно так):

d + 4 == y ^ 85 ? ~m : d * (y-2)

Это примеры неправильных ответов (не выражений):

if ( log ( d ) == 1752 ) m = 1;

или

for ( i = 0; i < 32; i ++ ) m = m / 2;

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

  • До 2.9.1752 года был юлианский календарь, после этой даты - григорианский календарь
  • В юлианском календаре каждый год делится на 4 високосных года.
  • В григорианском календаре это високосный год, который делится на 4, но не делится на 100. Годы, которые делятся на 400, являются еще одним исключением и являются високосными годами.
  • 1800, 1801, 1802, 1803, 1805, 1806, ...., 1899, 1900, 1901, ..., 2100, ..., 2200 не являются циклами.
  • 1896, 1904, 1908, ..., 1996, 2000, 2004, ..., 2396, ..., 2396, 2400 - циклические годы
  • В сентябре 1752 года - еще одно исключение, когда за 2.9.1752 последовало 14.9.1752, поэтому даты 3.9.1752, 4.9.1752, ..., 13.9.1752 недействительны.

Ответы [ 9 ]

6 голосов
/ 03 октября 2009
((m >0)&&(m<13)&&(d>0)&&(d<32)&&(y!=0)&&(((d==31)&&
((m==1)||(m==3)||(m==5)||(m==7)||(m==8)||(m==10)||(m==12)))
||((d<31)&&((m!=2)||(d<29)))||((d==29)&&(m==2)&&((y<=1752)?((y%4)==0):
((((y%4)==0)&&((y%100)!=0))
||((y%400)==0)))))&&(((y==1752)&&(m==9))?((d<3)||(d>13)):true))
5 голосов
/ 05 октября 2009

<evil> Зачем вам определять новый, если вы можете повторно использовать существующий? errno - идеальная временная переменная. </evil>

4 голосов
/ 03 октября 2009

Я думаю, что цель домашней работы - попросить вас сделать это без использования переменных, и то, что вы пытаетесь сделать, может нарушить ее цель.

2 голосов
/ 03 октября 2009

Я не верю, что вы можете, но даже если бы вы могли, он имел бы только область видимости внутри скобок, в которых они определены (в вашем примере), и не мог бы использоваться вне их.

2 голосов
/ 03 октября 2009

В стандарте C ++ это невозможно. G ++ имеет расширение, известное как операторные выражения , которое может это сделать.

1 голос
/ 03 октября 2009

Ваше решение, которое я не предоставлю вам полностью, вероятно, будет выглядеть следующим образом:

isJulian ? isJulianLeapyear : isGregorianLeapyear

Чтобы сделать это более конкретным, это может быть так:

isJulian ? (year % 4) == 0 : ((year % 4) == 0 || (year % 400) == 0) 

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

1 голос
/ 03 октября 2009

Первый: не надо. Это может быть мило, но даже если есть расширение, позволяющее это сделать, Code Golf - это опасная игра, которая почти всегда приводит к большому сожалению, чем она решает.

Хорошо, вернемся к «реальному» вопросу, определенному домашним заданием. Можете ли вы сделать дополнительные функции? Если это так, вместо того, чтобы фиксировать, является ли это високосным годом в переменной, создайте функцию isLeapYear (int year), которая возвращает правильное значение.

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

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

......

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

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

Как только вы пройдете через это, я, вероятно, проверим наличие 1752 особых случаев.

Если это не актуально, «обычные» месяцы можно обрабатывать довольно просто.

Это оставляет нам случаи високосного года, которые можно разбить на два выражения - является ли что-то високосным годом (который будет дополнительно разбит на основе григорианского / юлианского) и является ли дата действительной в этот момент .

Итак, на самом высоком уровне ваше выражение выглядит примерно так:

areWithinRange (d, m, y) && Pass1752SpecialCases (d, M, Y) && Pass30DayMonths (d, M, Y) && Pass31DayMonths (D, M, Y) && проходит февральские проверки (D, M, Y)

Если мы предполагаем, что мы возвращаем false из наших подвыражений, только если мы активно обнаруживаем разрыв правила (31 день в июне для правила 30DayMonth возвращает false, но 30 дней в феврале не имеет значения, и поэтому проходит верно), тогда мы можем в значительной степени сказать, что логика на этом уровне верна.

На этом этапе я бы написал отдельные функции для отдельных частей (как чистые выражения, одно выражение return ...). После того, как вы их получили, вы можете заменить вызов метода в выражении верхнего уровня на расширенную версию. Просто убедитесь, что вы заключили в скобки (это слово?) Все достаточно.

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

bool isValidDate(int d, int m, int y)
{
    return
        // your expression here
}

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

Вы можете найти другие способы упростить вашу логику - за исключением 1752 особых случаев, дни между 1 и 28, например, всегда действительны.

0 голосов
/ 03 октября 2009

Учитывая, что это домашнее задание, я думаю, что лучшим советом будет подход к поиску собственного решения.

Если бы я занимался этим заданием, я бы начал с нарушения правил.

  1. Я бы написал функцию c ++, которая, учитывая переменные d, m и y, возвращает логический результат о достоверности даты. Я бы использовал столько нерекурсивных вспомогательных функций, сколько нужно, и не стеснялся бы использовать if, else if и else, но без зацикливания вслух.

  2. Я бы тогда встроил все вспомогательные функции

  3. Я бы сократил все, если, иначе, если, и еще заявление до? : запись

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

Удачи.

0 голосов
/ 03 октября 2009

Вам явно нужно как-то передать дату. Кроме того, все, что вы действительно собираетесь делать, - это цепочки && и || (при условии, что мы получаем дату в виде tm структуры):

#include <ctime>
bool validate(tm date)
{
     return (
             // sanity check that all values are positive
             date.tm_mday >= 1 && date.tm_mon >= 0 && date.tm_year >= 0
             // check for valid days
             && ((date.tm_mon == 0 && date.tm_mday <= 31)
              || (date.tm_mon == 1 && date.tm_mday <= (date.tm_year % 4 ? 28 : 29))
              || (date.tm_mon == 2 && date.tm_mday <= 31)
             // yadda yadda for the other months
              || (date.tm_mon == 11 && date.tm_mday <= 31))
             );
}

Скобки вокруг date.tm_year % 4 ? 28 : 29 на самом деле не нужны, но я включаю их для удобства чтения.


UPDATE

Глядя на комментарий, вам также понадобятся аналогичные правила для проверки дат, которых нет в григорианском календаре.

ОБНОВЛЕНИЕ II

Поскольку вы имеете дело с датами в прошлом, вам нужно будет внедрить более правильный тест високосного года. Тем не менее, я обычно имею дело с датами в будущем, и этот неправильный тест високосного года даст правильные результаты в 2012, 2016, 2020, 2024, 2028, 2032, 2036, 2040, 2044, 2048, 2052, 2056, 2060, 2064, 2068, 2072, 2076, 2080, 2084, 2088, 2092 и 2096. Я сделаю прогноз, что до сбоя этого теста в 2100 компьютерах на основе кремния будут забыты реликвии. Я серьезно сомневаюсь, что тогда мы будем использовать C ++ на квантовых компьютерах. Кроме того, я не буду программистом, назначенным для исправления ошибки.

...