wtorek, 16 marca 2010

Program w Javie bez main() i średników

Ostatnio padło wyzwanie - napisać program bez użycia funkcji main w Javie. Taki komentarz pojawił się przy okazji programu w C bez średników - http://wojtek-m.blogspot.com/2010/03/program-w-cc-bez-uzycia-srednikow.html ;)

Hm, stwierdziłem że podbije poziom trudności i w Javie też ani jeden średnik nie zagości! Co będzie trudniejsze niż w C, bo Java jest bardziej "uporządkowana".

Oryginalny program (do znanej już gry w "bum" - osoby nie w temacie niech zerkną do powyżej podanego posta):

public class BumGame {
  public static void main(String[] args) {
    for (int i = 1; i <= 100; i++) {
      if (i % 3 == 0 || i % 5 == 0) {
        System.out.println("BUM");
        continue;
      }
      int j;
      for (j = 1; j <= i; j *= 10) {
        int digit = (i / j) % 10;
        if (digit == 3 || digit == 5) {
          System.out.println("BUM");
          break;
        }
      }
      if (j > i) {
        System.out.println(i);
      }
    }
  }
}


Program można uruchomić normalnie bez funkcji main pisząc cały kod w statycznym bloku inicjalizującym - trzeba tylko na koniec napisać System.exit(0), aby program mógł się uruchomić. I tak otrzymujemy:

public class NoMain {
  static {
    for (int i = 1; i <= 100; i++) {
      if (i % 3 == 0 || i % 5 == 0) {
        System.out.println("BUM");
        continue;
      }
      int j;
      for (j = 1; j <= i; j *= 10) {
        int digit = (i / j) % 10;
        if (digit == 3 || digit == 5) {
          System.out.println("BUM");
          break;
        }
      }
      if (j > i) {
        System.out.println(i);
      }
    }
    System.exit(0);
  }
}


Można to normalnie uruchomić ("java NoMain" z linii komend) mimo iż brak metody main :)

OK... Ale jak się pozbyć średników? Z pomocą przyjdzie nam refleksja :) I metoda printf, nowość w Javie 5. Niestety mimo pary prób, nadal nie udało mi się usunąć średnika niezbędnego przy deklaracji zmiennej lokalnej :( Więc o to program z 1 średnikiem:

public class NoMainNoSemicolon {
  static {
    int flag, j, i; // pechowy średnik
    if ((i = 1) == 0) {
    }
    while (i <= 100) {
      if ((flag = 0) == 0) {
      }
      if (i % 3 == 0 || i % 5 == 0) {
        if (System.out.printf("BUM\n") == null) {
        }
        if ((flag = 1) == 0) {
        }
      }
      if (flag == 0) {
        if ((j = 1) == 0) {
        }
        while (j <= i && flag == 0) {
          if ((i / j) % 10 == 3 || (i / j) % 10 == 5) {
            if (System.out.printf("BUM\n") == null) {
            }
            if ((flag = 1) == 0) {
            }
          }
          if (flag == 0) {
            if ((j *= 10) == 0) {
            }
          }
        }
        if (j > i) {
          if (System.out.printf(i + "\n") == null) {
          }
        }

      }
      if (++i == 0) {
      }
    }
    try {
      if (Runtime.getRuntime().getClass().getDeclaredMethod("exit", int.class).invoke(Runtime.getRuntime(), 0) == null) {
      }
    } catch (Exception e) {
    }
  }
}


EDIT:
Dla osób rzadko czytających komentarze, oto wersja programu, która nie wykorzystuje ani jednego średnika - tak jak zaproponował to Herbi :)

Pomysł genialny - nie można wykorzystać zmiennych lokalnych, ale argumenty metody można definiować bez użycia średnika. Cały nasz kod przenosimy więc do metody - jednakże nie może to być ani metoda zwracająca void (bo takiej nie uruchomimy w if-ie, bo void nie można z niczym porównać) ani zwracająca cokolwiek, bo po return musi być średnik.

Rozwiązanie to zastosowanie konstruktora - jest to metoda, która zwraca referencję (po zastosowaniu operatora new), a nie potrzebuje return :)

Kod, bez ani jednego średnika:

public class NoMainReallyNoSemicolon {
  public NoMainReallyNoSemicolon(int i, int j, int k) {
    if ((i = 1) == 0) {
    }
    while (i <= 100) {
      if ((k = 0) == 0) {
      }
      if (i % 3 == 0 || i % 5 == 0) {
        if (System.out.printf("BUM\n") == null) {
        }
        if ((k = 1) == 0) {
        }
      }
      if (k == 0) {
        if ((j = 1) == 0) {
        }
        while (j <= i && k == 0) {
          if ((i / j) % 10 == 3 || (i / j) % 10 == 5) {
            if (System.out.printf("BUM\n") == null) {
            }
            if ((k = 1) == 0) {
            }
          }
          if (k == 0) {
            if ((j *= 10) == 0) {
            }
          }
        }
        if (j > i) {
          if (System.out.printf(i + "\n") == null) {
          }
        }

      }
      if (++i == 0) {
      }
    }
  }

  static {
    if (new NoMainReallyNoSemicolon(0, 0, 0) != null) {
      try {
        if (Runtime.getRuntime().getClass().getDeclaredMethod("exit", int.class).invoke(Runtime.getRuntime(), 0) == null) {
        }
      } catch (Exception e) {
      }
    }
  }
}

4 komentarze:

  1. Muszę przyznać, że z tą refleksją to nieźle rozkminiłeś, ale nadal jest jeden średnik, a da się napisać program bez średnika i bez maina - wystarczy wykorzystać konstruktor (bo funkcja, żeby móc ją porównać, musi coś zwracać, a return musi się kończyć średnikiem), poniżej przykład :)

    public class NoMainNoSemicolon {
    NoMainNoSemicolon(int flag, int j, int i) {

    if ((i = 1) == 0) {
    }
    while (i <= 100) {
    if ((flag = 0) == 0) {
    }
    if (i % 3 == 0 || i % 5 == 0) {
    if (System.out.printf("BUM\n") == null) {
    }
    if ((flag = 1) == 0) {
    }
    }
    if (flag == 0) {
    if ((j = 1) == 0) {
    }
    while (j <= i && flag == 0) {
    if ((i / j) % 10 == 3 || (i / j) % 10 == 5) {
    if (System.out.printf("BUM\n") == null) {
    }
    if ((flag = 1) == 0) {
    }
    }
    if (flag == 0) {
    if ((j *= 10) == 0) {
    }
    }
    }
    if (j > i) {
    if (System.out.printf(i + "\n") == null) {
    }
    }

    }
    if (++i == 0) {
    }
    }
    }

    static {
    if (new NoMainNoSemicolon(1, 2, 3) != null) {
    try {
    if (Runtime.getRuntime().getClass().getDeclaredMethod("exit",
    int.class).invoke(Runtime.getRuntime(), 0) == null) {
    }
    } catch (Exception e) {
    }
    }

    }
    }

    OdpowiedzUsuń
  2. To teraz pozostało tylko napisać program w javie, który nie będzie miał nawiasów klamrowych - jak myślisz, da się? :]

    OdpowiedzUsuń
  3. super rozwiązanie z tym konstruktorem!

    OdpowiedzUsuń