Some days ago I read a post on CodingHorror in which the author talks about the importance of including a simple test in a job interview for programmers. He is talking about small and easy task that has to solved on paper by the applier in a few minutes to prove that he/she is capable of programming.
The author calls these "FizzBuzz" questions. Example:
Write a program that prints the numbers from 1 to 100. But for multiples of three print "Fizz" instead of the number and for the multiples of five print "Buzz". For numbers which are multiples of both three and five print "FizzBuzz".
A colleague of mine, Valeriu, decided to solve this problem. He uses .Net Reflection, because it is elegant; with .Net Reflection Emit he generates in runtime the dll that contains the solution for the problem above and executes it. The algorithm itself is written in Intermediate Language (IL) to keep things simple.
"This kind of approach to the problem is extremely simple, efficient and elegant"
he states. He honored me by naming this simple approach "Vencel Algorithm". Here is the code:
class Program
{
static void
{
VencelAlgorithm();
Console.ReadLine();
}
public static void VencelAlgorithm()
{
Type type = CodeGenerator.EmitClass();
type.GetMethod("Print").Invoke(null, null);
}
}
class CodeGenerator
{
public static Type EmitClass()
{
AssemblyName asmName = new AssemblyName();
asmName.Name = "VencelAlghoritm";
AssemblyBuilder asmBuilder =
Thread.GetDomain().DefineDynamicAssembly(asmName, AssemblyBuilderAccess.RunAndSave);
ModuleBuilder modBuilder = asmBuilder.DefineDynamicModule("Vencel.dll");
TypeBuilder typeBuilder = modBuilder.DefineType(
"Algorithm",
TypeAttributes.Public | TypeAttributes.Class);
MethodBuilder methodBuilder = typeBuilder.DefineMethod("Print",
MethodAttributes.Static | MethodAttributes.Public,
typeof(void),
new Type[] {typeof(Int32)});
EmitFunction(methodBuilder);
Type type = typeBuilder.CreateType();
asmBuilder.Save("Vencel.dll");
return type;
}
private static void EmitFunction(MethodBuilder methodBuilder)
{
Type[] intType = { typeof(Int32) };
MethodInfo writeLineInt = typeof(Console).GetMethod("WriteLine", intType);
Type[] stringType = { typeof(string) };
MethodInfo writeLineString = typeof(Console).GetMethod("WriteLine", stringType);
ILGenerator ilGenerator = methodBuilder.GetILGenerator();
//am nevoie de cateva etichete pentru a implementa un for si 4 if-uri
Label IL_0046 = ilGenerator.DefineLabel();
Label IL_001a = ilGenerator.DefineLabel();
Label IL_0042 = ilGenerator.DefineLabel();
Label IL_002b = ilGenerator.DefineLabel();
Label IL_003c = ilGenerator.DefineLabel();
Label IL_0004 = ilGenerator.DefineLabel();
//declar variabila contor pentru for (denumita variabila locala de index 0)
ilGenerator.DeclareLocal(typeof(int));
//se pune valoarea 1 in stiva
ilGenerator.Emit(OpCodes.Ldc_I4_1);
//se scoate valoarea de pe stiva (operatia pop) si se pune in variabila locala de index 0
ilGenerator.Emit(OpCodes.Stloc_0);
//salt la eticheta IL_0046
ilGenerator.Emit(OpCodes.Br_S, IL_0046);
//marchez urmatoarea instructiune cu o eticheta (pt revenire la o noua iteratie in for)
ilGenerator.MarkLabel(IL_0004);
//se incarca variabila locala de index in stack
ilGenerator.Emit(OpCodes.Ldloc_0);
//se incarca valoarea 3 in stack
ilGenerator.Emit(OpCodes.Ldc_I4_3);
//se calculeaza restul impartirii celor 2 valori puse pe stack
//rem - remainder
//rezultatul se pune pe stiva
ilGenerator.Emit(OpCodes.Rem);
//sare la eticheta IL_001a daca operatia anterioara este evaluata la true
//(un fel de je din 8086)
ilGenerator.Emit(OpCodes.Brtrue_S, IL_001a);
ilGenerator.Emit(OpCodes.Ldloc_0);
ilGenerator.Emit(OpCodes.Ldc_I4_5);
ilGenerator.Emit(OpCodes.Rem);
ilGenerator.Emit(OpCodes.Brtrue_S, IL_001a);
//se pune referinta stringului "FizzBuzz" pe stiva
ilGenerator.Emit(OpCodes.Ldstr, "FizzBuzz");
//se apeleaza functia Console.WriteLine cu parametrul string
//(pentru afisarea la consola)
ilGenerator.Emit(OpCodes.Call, writeLineString);
//salt neconditionat (echivalentul instructiunii jmp din 8086)
ilGenerator.Emit(OpCodes.Br_S, IL_0042);
ilGenerator.MarkLabel(IL_001a);
ilGenerator.Emit(OpCodes.Ldloc_0);
ilGenerator.Emit(OpCodes.Ldc_I4_3);
ilGenerator.Emit(OpCodes.Rem);
ilGenerator.Emit(OpCodes.Brtrue_S, IL_002b);
ilGenerator.Emit(OpCodes.Ldstr, "Fizz");
//se apeleaza functia Console.WriteLine cu parametrul string
//(pentru afisarea la consola a string-ului "Fizz")
ilGenerator.Emit(OpCodes.Call, writeLineString);
ilGenerator.Emit(OpCodes.Br_S, IL_0042);
ilGenerator.MarkLabel(IL_002b);
ilGenerator.Emit(OpCodes.Ldloc_0);
ilGenerator.Emit(OpCodes.Ldc_I4_5);
ilGenerator.Emit(OpCodes.Rem);
ilGenerator.Emit(OpCodes.Brtrue_S, IL_003c);
ilGenerator.Emit(OpCodes.Ldstr, "Buzz");
//se apeleaza functia Console.WriteLine cu parametrul string
//(pentru afisarea la consola a string-ului "Buzz")
ilGenerator.Emit(OpCodes.Call, writeLineString);
ilGenerator.Emit(OpCodes.Br_S, IL_0042);
ilGenerator.MarkLabel(IL_003c);
ilGenerator.Emit(OpCodes.Ldloc_0);
//se apeleaza functia Console.WriteLine cu parametrul int
//(pentru afisarea la consola a numarului curent)
ilGenerator.Emit(OpCodes.Call, writeLineInt);
ilGenerator.MarkLabel(IL_0042);
ilGenerator.Emit(OpCodes.Ldloc_0);
ilGenerator.Emit(OpCodes.Ldc_I4_1);
ilGenerator.Emit(OpCodes.Add);
ilGenerator.Emit(OpCodes.Stloc_0);
ilGenerator.MarkLabel(IL_0046);
ilGenerator.Emit(OpCodes.Ldloc_0);
ilGenerator.Emit(OpCodes.Ldc_I4_S, 100);
ilGenerator.Emit(OpCodes.Ble_S, IL_0004);
//return din funtie
ilGenerator.Emit(OpCodes.Ret);
}
No comments:
Post a Comment