Pull to refresh

Говнокод: врага надо знать в лицо

Delirium coding


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

Что делает код говнокодом — никто точно не знает. Точного определения нет. Часто то, что одни считают очевидным говнокодом, другим кажется лаконичным и эффективным решением.

Примеры


Примеры говнокода варьируются от избыточных до откровенного хардкода. Самые любмые примеры — такие, говнокодистость которых очевидна для всех:
Boolean b = new Boolean( is_admin );
if( b.toString().length() == 4 ) {
   // something...
}
// something


Но знаете, что я скажу? Осуждать говнокод легко, но написать его не так-то легко!
Не верите? Давайте попробуем!

UPD Ниже добавлен анализ поступивших решений.


Задание


Вот простое задание: Напечатать на экране следующее:
1
2-1
1-2-3
4-3-2-1
1-2-3-4-5
6-5-4-3-2-1
(Естественно, вместо 6 может быть любое число).

Попробуйте написать два решения на любом языке:
1. Максимально лаконичное/красивое/читабельное
2. Максимально говнокодистое (но не слишком много. Скажем, ограничение в один экран. И конечно, решение должно работать!)

Можете писать свои варианты в комментарии (для длинных кусков рекомендуется pastebin). Помимо развлекательной составляющей, это может оказаться и полезным упражнением, потому что возможно, оно приблизит вас к понимаю, как получается плохой код и как с ним бороться.

Дайте волю своей фантазии!

UPDATE: Анализ решений


Спасибо всем, кто прислал свои варианты. Так много комментариев я ещё не видел. Стало быть, тема актуальна.

Попробуем проанализировать поступившие решения.

Хорошие решения


«Хорошие» решения, как и полагается, все однотипные, то есть примерно такие:
$n = 6;
for($i = 1; $i <= $n; $i++) {
    $array = $i % 2 ? range(1, $i) : range($i, 1);
    echo join('-', $array), PHP_EOL;
}


Среди них выделяются два решения на Руби и Хаскеле, которые, на мой взгляд, хороши тем, что они написаны в одну строчку, но при этом сохраняют читабельность. Достигается это, очевидно, засчёт языка.

Руби от Arion:
(6 + 1).times{|i| p (1..i).to_a.send((i%2 == 0)? :to_a: :reverse).join('-')}


Хаскель от pechlambda:
main = mapM_ (putStrLn . foo) [1..6]
foo n = concat $ intersperse "-" $ (if odd n then id else reverse) $ take n $ map show [1..]

Что ещё раз доказывает, что Руби рулит!

Если говорить серьёзно, то у всех этих решений есть один недостаток: они жрут много ресурсов при больших N (создают много временных массивов и строк). Следующее решение от rPman отчасти решает эту проблему (интересно, что сам автор представил его как говнокод):
<?php
define('LENGTH',6);
define('nl',"\n");

$right='1';
$left='1';
echo '1'.nl; // <== это он
for($i=2;$i<=LENGTH;$i++)
{
    $left=$i.'-'.$left;
    $right=$right.'-'.$i;
    echo ($i%2?$right:$left).nl;
}
?>


Плохие решения



Меня изначально интересовали, конечно, именно «плохие» решения. Надо сказать, что хабровчане смогли-таки меня удивить и придумал больше говновариантов, чем я ожидал.

Добротный треш

Решение от runcore пытается в одной строчке совместить цикл 1..N и N..1, что надёжно обфускирует изначальное предназначение этого кода:
$is_reverse = false;
for($i=1; $i<=6; ++$i) {
	for($is_reverse?$j=$i:$j=1; $is_reverse?$j>=1:$j<=$i; $is_reverse?--$j:++$j) {
		echo (($is_reverse)?($j.($j>1?'-':'')):($j.($j<$i?'-':'')));
	}
	$is_reverse = (($is_reverse) ? false : true);
	if (in_array($is_reverse, array(true,false))) {
		echo '<br />';
	}
}


Комментарии рулят

AHDPEu в своём решении активно использует комментарии. Получилось классно. Что ещё раз доказывает, что комментирование кода — от лукавого.

Premature optimization

Говнокод на Perl у youlose получается засчёт попыток чего-то там оптимизировать. Что ещё раз доказывает, что преждевременная оптимизация — зло.

Обработка ошибок

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

Говноязыки

  • Доставило решение sledopit на языке bash:
    #!/bin/bash
    i=1;m=7;while [ $i -lt $m ];do seq 1 $(($i%$m))|tr '\n' ' '|sed 's/ /-/g;s/-$//';((i++));echo;done|while read line; do [ $((${line/*-/}%2)) -eq 1 ]&&echo $line||echo $line|rev;done
    

    Думаю, все согласятся с тем, что оно ни разу не читабельное. Похоже, это тот случай, когда сам язык обязывает программиста писать говнокод. То же подтверждает и целая череда решений на brainfuck.
  • Решение pechlambda на Хаскеле: codepad.org/NQUSqcjc доказывает, что каким бы хорошим язык ни был, на нём всегда можно добротно наговнокодить.
  • Спасибо огромное Next_Alex за то, что напомнил нам про старые добрые goto.
  • А вот Levsha100, совершенно зря наговаривает на своё решение на ассемблере. По-моему, ничего читабельнее я тут не встречал. :)
  • Решения на Лиспе, по-моему, оба нечитабельные. Впрочем, позже S2nek исправился.


Школы говнокода



Классика

Но самый классический говнокод, похоже, вышел у SnakeSolid: pastebin.com/rdCtf2j5
Вроде бы ничего особенно лишнего: комментарии, и парочка лишних проверок — да, это всё немножко лишнее, но без криминала. Начнёшь придираться — автор наверняка сможет всё это защитить. Но кода получилось на целую страницу вместо пары строчек. Именно так оно в жизни и бывает.

Подводя итоги


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

Всем спасибо!

Пишите чисто!

Tags:
Hubs:
Total votes 93: ↑73 and ↓20 +53
Views 47K
Comments Comments 290