Grande divertimento con questi quiz, ragazzi!... Alcune soluzioni sono degne di TheDailyWTF! (sì, WTF viene proprio da What The F**k :-))
Inizio come sempre con la soluzione a cui ho pensato io e lascio poi che le vostre parlino da sole:
private enum Roman
{
UnFintoZeroRomano,
I, II, III, IV, V, VI, VII, VIII, IX, X,
XI, XII, XIII, XIV, XV, XVI, XVII, XVIII, XIX, XX
}
public static string RomanEval(string r)
{
string[] split = r.Split('+', '-');
int t1 = (int)Enum.Parse(typeof(Roman), split[0]);
int t2 = (int)Enum.Parse(typeof(Roman), split[1]);
Roman ret = (Roman)(t1 - t2 * ((int)r[split[0].Length] - 44));
return ret.ToString().Substring(0, ret.ToString().Length % ((Roman)0).ToString().Length);
}
Si può notare l'uso di una enum, l'espressione di ritorno calcolata in base al codice ASCII dell'operatore, e il trattamento dello 0 con l'aiuto di un piccolo trucchetto: nella enum il nome dell'elemento corrispondente al valore 0, UnFintoZeroRomano, deve avere una lunghezza superiore a qualunque altro elemento per ottenere nella return una sottostringa di lunghezza 0 per esso (a questo serve il modulo %).
Il primo che mi ha mandato una soluzione corretta è stato (anche stavolta) Flavio Polesello:
// soluzione di Flavio Polesello (1)
public static string RomanEval(string r)
{
string[] romanNumbers =
{
"",
"I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X",
"XI", "XII", "XIII", "XIV", "XV", "XVI", "XVII", "XVIII", "XIX", "XX"
};
int[] opValues = {1, -1};
string rWithSum = r.Replace("-", "+");
string[] opNumbers = rWithSum.Split("+".ToCharArray());
int op = opValues[Math.Abs(r.CompareTo(rWithSum))];
int a = Array.IndexOf(romanNumbers, opNumbers[0]);
int b = Array.IndexOf(romanNumbers, opNumbers[1]);
int intResult = a + b * op;
return romanNumbers[intResult];
}
Il giorno dopo, mi ha mandato una seconda soluzione, direi la più originale di tutte:
// soluzione di Flavio Polesello (3)
public class RomanQuiz
{
private static string[] romanNumbers =
{
"",
"I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X",
"XI", "XII", "XIII", "XIV", "XV", "XVI", "XVII", "XVIII", "XIX", "XX"
};
private delegate int ExecuteOpDelegate(int a, int b);
private static int ExecuteAdd(int a, int b)
{
return a + b;
}
private static int ExecuteSub(int a, int b)
{
return a - b;
}
private static string ConvertInt32ToRoman(int number)
{
return romanNumbers[number];
}
private static int ConvertRomanToInt32(string number)
{
return Array.IndexOf(romanNumbers, number);
}
public static string RomanEval(string r)
{
System.Collections.Hashtable registeredOps = new System.Collections.Hashtable();
// registra le operazioni eseguibili
registeredOps.Add("+", new ExecuteOpDelegate(ExecuteAdd));
registeredOps.Add("-", new ExecuteOpDelegate(ExecuteSub));
// determina i numeri romani dell'espressione
string[] opNumbers = r.Split("+-".ToCharArray());
// determina l'operatore
string op = r.Substring(opNumbers[0].Length, 1);
// determina l'operazione che deve essere eseguita
ExecuteOpDelegate executeOp = (ExecuteOpDelegate)registeredOps[op];
// converte i numeri da romani a interi
int a = ConvertRomanToInt32(opNumbers[0]);
int b = ConvertRomanToInt32(opNumbers[1]);
// esegue l'operazione
int c = executeOp(a, b);
// converte e restituisce il numero romano calcolato
return ConvertInt32ToRoman(c);
}
}
Il secondo che mi ha mandato una soluzione corretta è stato Lorenzo Melato:
// soluzione di Lorenzo Melato (2)
public static string RomanEval(string r)
{
System.Text.RegularExpressions.Regex reRomanExpr = new System.Text.RegularExpressions.Regex
(
"\\A\\s*(?i?[xv]?|v?i{0,3})\\s*(?[+-]+)\\s*(?i?[xv]?|v?i{0,3})\\s*\\Z",
System.Text.RegularExpressions.RegexOptions.IgnoreCase
);
string[] roman =
{
string.Empty,
"I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X",
"XI", "XII", "XIII", "XIV", "XV", "XVI", "XVII", "XVIII", "XIX", "XX"
};
System.Text.RegularExpressions.Match m = reRomanExpr.Match(r);
string expr = Array.IndexOf(roman, m.Groups["rn"].Value).ToString() + m.Groups["op"].Value + Array.IndexOf(roman, m.Groups["ln"].Value).ToString();
Type t = Type.GetTypeFromProgID("MSScriptControl.ScriptControl");
object obj = Activator.CreateInstance(t);
t.InvokeMember("Language", System.Reflection.BindingFlags.SetProperty, Type.DefaultBinder, obj, new string[]{"VBScript"});
int res = Convert.ToInt32(t.InvokeMember("Eval", System.Reflection.BindingFlags.InvokeMethod, null, obj, new string[]{expr}));
return roman[res];
}
Lascio a voi i commenti :-)
L'idea di utilizzare le regular expression l'ha avuta anche M.rkino (in più, ha implementato anche il pattern Interpreter). Non è stato attento però al vincolo "è vietato l'uso di istruzioni di selezione"... Peccato, aveva fatto non una soluzione, ma una solution di ben 250 righe! :-)
Il terzo, Marco Poponi - con la sua dose di umorismo :-)
// soluzione di Marco Poponi (4)
public static string RomanEval(string r)
{
r = r.Replace("IV", "IIII");
r = r.Replace("V", "IIIII");
r = r.Replace("IX", "IIIIIIIII");
r = r.Replace("X", "IIIIIIIIII");
int x=0;
try
{
x = r.Split('+')[0].Length + r.Split('+')[1].Length;
}
catch(Exception E)
{
x = r.Split('-')[0].Length - r.Split('-')[1].Length;
}
string o = "";
o = o.PadLeft(x, 'I');
o = o.Replace("IIIIIIIIII", "X");
o = o.Replace("IIIIIIIIII", "X");
o = o.Replace("IIIIIIIII", "IX");
o = o.Replace("IIIII", "V");
o = o.Replace("IIII", "IV");
return o;
}
Il quarto e ultimo, Daniele Proietti:
// soluzione di DanieleProietti (5)
public static string RomanEval(string r)
{
r = r.ToUpper();
System.Collections.ArrayList myAL = new System.Collections.ArrayList();
myAL.Add("");
myAL.Add("I");
myAL.Add("II");
myAL.Add("III");
myAL.Add("IV");
myAL.Add("V");
myAL.Add("VI");
myAL.Add("VII");
myAL.Add("VIII");
myAL.Add("IX");
myAL.Add("X");
myAL.Add("XI");
myAL.Add("XII");
myAL.Add("XIII");
myAL.Add("XIV");
myAL.Add("XV");
myAL.Add("XVI");
myAL.Add("XVII");
myAL.Add("XVIII");
myAL.Add("XIX");
myAL.Add("XX");
int operatorePos = r.IndexOfAny(new char[] {'+', '-'});
string operatore = r.Substring(operatorePos, 1);
int val1 = myAL.IndexOf(r.Substring(0, operatorePos));
int val2 = myAL.IndexOf(r.Substring(operatorePos + 1));
return myAL[val1 + (val2 * int.Parse(operatore + "1"))].ToString();
}
Per i prossimi sto pensando di cambiare il formato, rinunciare ai vincoli e premiare (soggettivamente per forza...) la bellezza della progettazione.
Grazie a tutti, il vostro Inquizzitore di fiducia :-)
adrian_floreachiocciolayahoopuntocom