wtorek, 13 lipca 2010

Program bez średników w C#

Do kompletu przykładów programów bez średników brakuje nam już tylko C#. C++ jest, Java jest, teraz czas na język Microsoftu ;)

Jeżeli ktoś nie zna jeszcze gry w "bum" niech zajrzy do tego posta:

http://wojtek-m.blogspot.com/2010/03/program-w-cc-bez-uzycia-srednikow.html.

Oryginalny program w C# grający w tą grę od 1 do 100 wygląda tak:

using System;

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


Czas usunąć średniki ;) Maina niestety nie udało mi się usunąć... jeszcze ;)

Opracowując ten program opierałem się na tym dla Javy (http://wojtek-m.blogspot.com/2010/03/program-w-javie-bez-main.html) - i jak zwykle bez pomysłu z konstruktorem nie dałbym rady ;) Więc może najpierw program, a potem szczegóły :) Udało mi się go zrobić w 2 wersjach, różniących się obsługą wypisywania na standardowe wyjście :P

Wersja z asynchronicznym wyjściem:

class NoSemicolons
{
  public static void Main(string[] args)
  {
    if (new NoSemicolons(0, 0, 0, new object()) == null)
    {
    }
  }

  public NoSemicolons(int i, int j, int k, object x)
  {
    if ((i = 1) == 0)
    {
    }
    while (i <= 100)
    {
      if ((k = 0) == 0)
      {
      }
      if (i % 3 == 0 || i % 5 == 0)
      {
        if (System.Console.OpenStandardOutput().BeginWrite(new byte[] { (byte)'B', (byte)'U', (byte)'M', (byte)'\n' }, 0, 4, null, x) == 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.Console.OpenStandardOutput().BeginWrite(new byte[] { (byte)'B', (byte)'U', (byte)'M', (byte)'\n' }, 0, 4, null, x) == null)
            {
            }
            if ((k = 1) == 0)
            {
            }
          }
          if (k == 0)
          {
            if ((j *= 10) == 0)
            {
            }
          }
        }
        if (j > i)
        {
          if (i < 10)
          {
            if (System.Console.OpenStandardOutput().BeginWrite(new byte[] { (byte)(i + '0'), (byte)'\n' }, 0, 2, null, x) == null)
            {
            }
          }
          else
          {
            if (System.Console.OpenStandardOutput().BeginWrite(new byte[] { (byte)(i / 10 + '0'), (byte)(i % 10 + '0'), (byte)'\n' }, 0, 3, null, x) == null)
            {
            }
          }
        }
      }
      if (++i == 0)
      {
      }
    }
    if (System.Console.OpenStandardOutput().BeginWrite(new byte[] { }, 0, 0, null, x) == null)
    {
    }
  }
}


Wykorzystujemy tutaj asynchroniczne wypisywanie na ekran metodą BeginWrite - tylko ta metoda wypisująca cokolwiek na ekran zwraca coś innego od void, dzięki czemu można jej użyć w ifie ;) Ponieważ jednak jest ona asynchroniczna, to potrzebujemy obiektu synchronizującego - tutaj jest to x. Asynchroniczność może też spowodować zakończenie programu przed wypisaniem wszystkiego na ekran - dlatego po pętli dodane zostało wypisywanie "niczego".

Dodatkowym problemem metody BeginWrite jest to, że przyjmuje ona surową tablicę bajtów - jeżeli chcemy wypisać coś bardziej skomplikowanego niż stały napis to powodzenia ;) Ja dla wypisywania liczby musiałem zrobić dodatkowego ifa, a skończyło się na jednym tylko dzięki temu, że wiedziałem iż przebiegów pętli jest 100, a setka jest "BUM" ;)

Można też spróbować wypisywać na ekran innym sposobem - wywołując metodę Console.Out.WriteLine z użyciem refleksji:

class NoSemicolons
{
  public static void Main(string[] args)
  {
    if (new NoSemicolons(0, 0, 0) == null)
    {
    }
  }

  public NoSemicolons(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 (typeof(System.IO.TextWriter).InvokeMember("WriteLine",
          System.Reflection.BindingFlags.Public |
          System.Reflection.BindingFlags.Instance |
          System.Reflection.BindingFlags.InvokeMethod,
          System.Type.DefaultBinder, System.Console.Out,
          new object[] { "BUM" }) == 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 (typeof(System.IO.TextWriter).InvokeMember("WriteLine",
              System.Reflection.BindingFlags.Public |
              System.Reflection.BindingFlags.Instance |
              System.Reflection.BindingFlags.InvokeMethod,
              System.Type.DefaultBinder, System.Console.Out,
              new object[] { "BUM" }) == null)
            {
            }
            if ((k = 1) == 0)
            {
            }
          }
          if (k == 0)
          {
            if ((j *= 10) == 0)
            {
            }
          }
        }
        if (j > i)
        {
          if (typeof(System.IO.TextWriter).InvokeMember("WriteLine",
              System.Reflection.BindingFlags.Public |
              System.Reflection.BindingFlags.Instance |
              System.Reflection.BindingFlags.InvokeMethod,
              System.Type.DefaultBinder, System.Console.Out,
              new object[] { i }) == null)
          {
          }
        }
      }
      if (++i == 0)
      {
      }
    }
  }
}


Wykorzystywanie refleksji jest o wiele bardziej elastyczne - wywołujemy normalną metodę WriteLine, a nie jakieś dziwy. No i możemy w ten sposób wywołać prawie każdą metodę ;)

1 komentarz: