luq techblog

o tworzeniu słów kilka…

Jak najmniej kodu! 18 sierpnia 2009

Filed under: PHP,Programowanie — Łukasz @ 15:36

Dziś z innej beczki. Ostatnio natrafiłem w sieci na forum nullcode, które organizuję konkurs/zabawę programistyczną. Chodzi o to aby napisać kod, który rozwiązuje podane zadanie, używając jak najmniej znaków (jest też wersja z jak najszybszym kodem). Rozwiązując aktualne (drugie) zadanie, okazało się, że zabawa jest całkiem fajna ;)

Zadanianiem było narysować coś takiego jak poniżej na podstawie podanych parametrów: szerokość, wysokość (jako przykład szer=3, wys=2) (z tym, że liczby mogą być w dowolnym położeniu tyle żeby cała komórka miała dokładnie 5 znaków)

xxxxxxxxxxxxxxxxxxx
x  1  x  3  x  5  x
xxxxxxxxxxxxxxxxxxx
x  2  x  4  x  6  x
xxxxxxxxxxxxxxxxxxx

Oczywiście językiem w którym pisałem był PHP. Algorytm do rozwiązania zadania nie jest jakiś bardzo skomplikowany, i moja pierwsza działająca wersja, prezentowała się tak (znaki spacji i tabulacji oczywiście na końcu będą wyrzucone z kodu):

    <?
    for( $y = 0; $y < $argv[2]*2+1; $y++ ){
      for( $x = 0; $x < $argv[1]; $x++ ){
          if( $y % 2 == 0 ){
              echo 'xxxxxx';
          }
          else{
              $n = (floor($y/2)+1)+($x*$argv[2]);
              echo "x$n";
              
              for($k=5;$k>strlen($n);$k--){
                  echo ' ';
              }
              if( $x==$argv[1]-1 ){
                  echo 'x';
              }
          }
      }
      echo "\n";
    }
    ?>


Pierwsze co można zrobić to wywalić klamry dla bloków kodu gdzie wykonywana jest tylko jedna instrukcja (o 6 znaków mniej). Mało. Pierwszym elementem na którym się skupiłem była pętla:

    for($k=5;$k>strlen($n);$k--) echo ' ';

Podejrzewałem, że jest jakaś funkcja aby takie coś zrealizować, a więc manual, i tak, miałem rację str_repeat(), a więc

    echo str_repeat(' ',5-strlen($n));

W sumie dużo to nie dało, ale zawsze coś. Następną rzeczą która przykuła moją uwagę był warunek if -> else, gdyby zastosować ternariusza było by kilka znaków mniej. Tyle, że trzeba by skrócić else do jednej instrukcji, echo by się wyrzuciło „przed nawias” i byłoby o wiele mniej znaków. Z takiej czegoś:

    if( $y % 2 == 0 ) echo 'xxxxxx';
    else{
        $n = (floor($y/2)+1)+($x*$argv[2]);
        echo "x$n";
        echo str_repeat(' ',5-strlen($n));
              
        if( $x==$argv[1]-1 ) echo 'x';
    }

można by liczenie $n wyrzucić z else i wpisać przed pierwszy warunek, natomiast drugiego if`a po całym else – wtedy wyglądałoby to tak:

    $n = (floor($y/2)+1)+($x*$argv[2]);
    if( $y % 2 == 0 ) echo 'xxxxxx';
    else{
        echo "x$n";
        echo str_repeat(' ',5-strlen($n));
    }
    if( $x==$argv[1]-1 ) echo 'x';

Teraz spokojnie można połączyć te 2 x echo w bloku else, oraz można z tej formy przejść na taką z wykorzystaniem ternariusza.

    $n = (floor($y/2)+1)+($x*$argv[2]);
    echo $y%2==0 ? 'xxxxxx' : "x$n".str_repeat(' ',5-strlen($n));
    if($x==$argv[1]-1)echo'x';

Sporo znaków zaoszczędzona ;) Ostatnią rzeczą na tym etapie było przyjrzenie się zaokrągleniu i wyszło na to że zamiast użycia floor() można dodać 0.5 zamiast 1 i będzie działać jak należy, a więc:

    $n = $y/2+0.5+$x*$argv[2];

I to jest właściwie pierwsza wersja która poleciała jako rozwiązanie (179 znaków):

    <?
    for( $y = 0; $y < $argv[2]*2+1; $y++ ){
        for( $x = 0; $x < $argv[1]; $x++ ){
            $n = $y/2+0.5+$x*$argv[2];
            echo $y%2==0 ? 'xxxxxx' : "x$n".str_repeat(' ',5-strlen($n));
            if( $x==$argv[1]-1 ) echo'x';
        }
        echo"\n";
    }
    ?>

Nie byłem jednak do końca zachwycony, byłem przekonany że str_repeat(‚ ‚,5-strlen($n)) można zastąpić jakimś printem czy podobną funkcją. W sumie chyba nigdy printa nie używałem a jak używałem to może raz czy dwa (jest wolniejszy od echo) więc musiałem szukać no i tak można to napisać w taki sposób:

    printf( '%-6s','x' );

No dobra mamy printf i echo musimy to jakoś połączyć. W sumie jako pierwszy argument printfa należy podać format a w drugim string jednak, taki kod:

     printf($y%2==0?'xxxxxx':"%-6s","x".($y/2+0.5+$x*$argv[2]));

chodź w sumie błędny logicznie (a być może to ja coś mylę) przechodzi i działa prawidłowo. Aha i w ten sposób wywaliliśmy liczenie $n :) Zamieniłem też ifa na wersje:

    echo$x==$argv[1]-1?'x':'';

To była kolejna wersja – mająca 158 znaków. Ale później myśląc doszedłem do wniosku, że jeszcze coś można zrobić. Końcówka kodu:

        echo$x==$argv[1]-1?'x':'';
    } // for
    echo"\n";}

Warunek $x==$argv[1]-1 wykonuje się przy ostatnim obrocie pętli, a po nim echo „\n” a więc można to połączyć!.

    echo$x==$argv[1]-1?"x\n":'';

Dzięki temu można wyrzucić klamrę zewnętrznej pętli. Kod prezentuje się tak:

    <?
    for( $y = 0; $y < $argv[2]*2+1; $y++ )
        for( $x = 0; $x < $argv[1]; $x++ ){
            printf($y%2==0?'xxxxxx':"%-6s","x".($y/2+0.5+$x*$argv[2]));
            echo$x==$argv[1]-1?"x\n":'';
        }
    ?>

Kod 149 znaków, chodź naliczono mi za niego 151 (może ja coś pomyliłem, nieistotne). No i w sumie tyle. Jeśli udało by się połączyć printf`a z echo to byłoby już naprawdę świetnie, niestety ja nie widzę takiej możliwości, na koniec dodam tylko, że osoba o nicku zbt pokonała mnie w PHP – 145 znaków. Good work :)

Reklamy
 

2 Responses to “Jak najmniej kodu!”

  1. elmasterlow Says:

    Bardzo fajny blog. Od samego początku jak tylko wszedłem mi się spodobał ;)

  2. luq Says:

    Dzięki za miłe słowo ;>


Skomentuj

Wprowadź swoje dane lub kliknij jedną z tych ikon, aby się zalogować:

Logo WordPress.com

Komentujesz korzystając z konta WordPress.com. Wyloguj / Zmień )

Zdjęcie z Twittera

Komentujesz korzystając z konta Twitter. Wyloguj / Zmień )

Facebook photo

Komentujesz korzystając z konta Facebook. Wyloguj / Zmień )

Google+ photo

Komentujesz korzystając z konta Google+. Wyloguj / Zmień )

Connecting to %s