Java, основанная на втором решении Эско:
class C{
public static void main(String[]d){
String s="";
d="first1second1third1fourth1fifth1sixth1seventh1eighth1ninth1tenth1eleventh1twelfth".split("1");
String[]g="a partridge in a pear tree1two turtle doves,\nand 1three french hens,\n1four calling birds,\n1five gold rings,\n1six geese a-laying\n1seven swans a-swimming,\n1eight maids a-milking,\n1nine ladies dancing,\n1ten lords a-leaping,\n1eleven pipers piping,\n1twelve drummers drumming,\n".split("1");
for(int i=0;i<12;)
System.out.println("On the "+d[i]+" day of Christmas my true love gave to me:\n"+
(s=g[i++]+s)+".\n");
}
}
Если я посчитал правильно, то это 579 символов (не считая отступов и переносов). И этот полностью работает и не печатает каждый стих в одной строке. Выходные данные:
В первый день Рождества моя настоящая любовь подарила мне:
куропатку в грушевом дереве.Во второй день Рождества моя настоящая любовь подарила мне:
двух горлиц и
куропатку в грушевом дереве.В третий день Рождества моя настоящая любовь подарила мне:
три французских курицы,
две горлицы и
куропатку в грушевом дереве....
РЕДАКТИРОВАТЬ: Немного улучшенная версия, основанная на идее Балабастера о замене общей строки одним символом:
class C{
public static void main(String[]d){
String s="";
d="first1second1third1fourth1fifth1sixth1seventh1eighth1ninth1tenth1eleventh1twelfth".split("1");
String[]g="a partridge in a pear tree1two turtle doves,\nand 1three french hens,\n1four calling birds,\n1five gold rings,\n1six geese a-lay#seven swans a-swimm#eight maids a-milk#nine ladies danc#ten lords a-leap#eleven pipers pip#twelve drummers drumm#".replace("#","ing,\n1").split("1");
for(int i=0;i<12;)
System.out.println("On the "+d[i]+" day of Christmas my true love gave to me:\n"+
(s=g[i++]+s)+".\n");
}
}
Теперь до 562 символов (хотя я добавил запятую) что я пропустил раньше).
Переполнение беззнакового целого числа (в форме циклического переноса) обычно используется в хеш-функциях, и это происходит с точки года.
Вкратце:
Совершенно законно / нормально / безопасно использовать целочисленное переполнение без знака по своему усмотрению, если вы уделяете внимание и придерживаетесь определения (для любых целей - оптимизация, супер умные алгоритмы и т. д.)
То, что вы знаете подробности стандарта, не означает, что это знает человек, поддерживающий ваш код. Этому человеку, возможно, придется тратить время на то, чтобы беспокоиться об этом при отладке позже, или ему придется поискать стандарт, чтобы проверить это поведение позже.
Конечно, мы ожидаем знания разумных функций языка от работающего программиста - и разные компании / группы имеют разные ожидания относительно того, где находится этот разумный уровень владения языком. Но для большинства групп это кажется слишком большим ожиданием того, что следующий человек будет знать всю свою голову, и ему не придется думать об этом.
Если этого было недостаточно, вы с большей вероятностью столкнуться с ошибками компилятора, когда вы работаете за пределами стандарта. Или, что еще хуже, человек, переносящий этот код на новую платформу, может столкнуться с ними.
Короче говоря,
Один из способов, когда я мог подумать о переполнении беззнакового целого числа, вызывающем проблему, - это когда вычитание из небольшого значения без знака приводит к его переносу к большому положительному значению.
Практические советы по целочисленному переполнению :
http://www.gnu.org/software/hello/manual/autoconf/Integer-Overflow-Basics.html#Integer-Overflow-Basics.html#Integer-Overflow-Basics
Поскольку числа со знаком на ЦП могут быть представлены по-разному, 99,999% всех текущих ЦП используют нотацию с дополнением до двух. Поскольку это большинство машин, трудно найти другое поведение, хотя компилятор может это проверить (большой шанс). Спецификации C, однако, должны составлять 100% компиляторов, поэтому не определили его поведение.
Так что это внесло бы еще большую путаницу, что является хорошей причиной, чтобы этого избежать. Однако, если у вас есть действительно веская причина (скажем, повышение производительности в 3 раза для критической части кода), хорошо задокументируйте ее и используйте.
I wouldn't rely on it just for readability reasons. You're going to be debugging your code for hours before you figure out where you're resetting that variable to 0.
Еще одно место, где можно с пользой использовать беззнаковое переполнение, - это когда вам нужно выполнить итерацию назад от заданного беззнакового типа:
void DownFrom( unsigned n )
{
unsigned m;
for( m = n; m != (unsigned)-1; --m )
{
DoSomething( m );
}
}
Другие альтернативы не так удобны. Попытка выполнить m> = 0
не сработает, если вы не измените m на знаковое, но тогда вы можете усечь значение n или, что еще хуже, преобразовать его в отрицательное число при инициализации.
В противном случае вам нужно сделать! = 0 или> 0, а затем вручную выполнить регистр 0 после цикла.
Я все время использую его, чтобы сказать, пора ли что-то делать.
UInt32 now = GetCurrentTime()
if( now - then > 100 )
{
// do something
}
Пока вы проверяете значение до «сейчас, кругов, потом», все в порядке. значения «сейчас» и «тогда».
РЕДАКТИРОВАТЬ: Я предполагаю, что это действительно потеря значимости.
Можно полагаться на переполнение, если вы знаете, КОГДА оно произойдет ...
Я, например, имел проблемы с реализацией C MD5 при переходе на более свежий компилятор ... Код действительно ожидал переполнения, но он также ожидал 32-битных int.
С 64-битными результатами были неправильные!
К счастью, для этого нужны автоматические тесты: я обнаружил проблему на ранней стадии, но это могло быть настоящей ужасной историей, если бы осталось незамеченным.
Вы можете возразить, «но такое случается редко»: да, но это делает это еще более опасным! Когда возникает ошибка, все с подозрением относятся к коду, написанному за последние несколько дней. Никто не вызывает подозрений в отношении кода, который «просто работал годами» и обычно никто до сих пор не знает, как он работает ...
Если вы используете его с умом (хорошо комментируете и читаете), вы можете извлечь из этого пользу, имеющий меньший и более быстрый код.
siukurnin хорошо замечает, что вам нужно знать, когда произойдет переполнение. Самый простой способ избежать описанной им проблемы переносимости - использовать целочисленные типы фиксированной ширины из stdint.h. uint32_t - это 32-разрядное целое число без знака на всех платформах и операционных системах, которое не будет вести себя иначе при компиляции для другой системы.