Dropping IConvertible
Cominciamo con la 'parte facile': far cadere il IConvertible
. Il motivo per cui ne hai bisogno è perché vuoi che questo codice funzioni su tutti i tipi, il che significa che non puoi sempre influenzare il fatto che abbia un determinato membro (Implies
). Ciò che si vorrebbe fare è quello che chiamano in C++: modello di specializzazione, ma purtroppo non è disponibile in C# (ancora?):
static bool Implies<T>(T premise, T conclusion) where T : IConvertible
{
var x = premise.ToUInt64(null);
return x == (x & conclusion.ToUInt64(null));
}
static bool Implies<T>(T premise, T conclusion) where T : Foobar
{
// other fancy logic
}
// and so on
Il modo più semplice per risolvere questo è quello di utilizzare multimethods. È possibile utilizzare la parola 'dinamica' per questo:
public partial class Implications
{
internal static bool CheckImplies<T>(T lhs, T rhs)
{
return Implies((dynamic)lhs, (dynamic)rhs);
}
public static bool Implies(int lhs, int rhs)
{
return lhs == (lhs & rhs);
}
// your other implies thingies implement this same partial class
}
public static partial class LogicExtensions
{
public static bool Implies<T>(this T premise, T conclusion, Paradox<T> predicate = null)
{
if (null == predicate)
return conclusion.Infers(premise, Implies);
if (Infers != predicate)
return predicate(premise, conclusion);
return Implications.CheckImplies(premise, conclusion);
}
public static bool Infers<T>(this T premise, T conclusion, Paradox<T> predicate = null)
{
if (null == predicate)
return premise.Implies(conclusion, Infers);
if (Implies != predicate)
return predicate(premise, conclusion);
return Implications.CheckImplies(premise, conclusion);
}
}
E se si dispone di un metodo di 'terza', si può semplicemente chiamare
Sono stato alla ricerca di un paio di minuti al strana definizione ricorsiva e per me non ha senso ... se hai comunque un terzo metodo di aiuto, perché non chiamarlo direttamente? :-)
public static bool Implies<T>(this T premise, T conclusion)
{
return Implications.CheckImplies(premise, conclusion);
}
public static bool Infers<T>(this T premise, T conclusion)
{
return Implications.CheckImplies(conclusion, premise);
}
Il non (non (T)) problema
Mentre quanto sopra non ha molto senso per me, trovo perfettamente ragionevole usare il sistema di tipi e la lingua per aiutarti un po '. Beh, sicuramente si può fare questo e questo è come l'avrei fatto ... :-)
Diamo introdurre un 'non' classe con un generico:
public class Not<T>
{
public Not(T val)
{
this.not = val;
}
internal T not;
}
Se abbiamo un non> situazione qui, vogliamo dare - altrimenti, vogliamo usare direttamente.Beh, possiamo farlo abbastanza facile con alcune estensioni:
public static T Optimize<T>(this Not<Not<T>> var)
{
return Optimize(var.not.not);
}
public static T Optimize<T>(this T var)
{
return var;
}
Per provarlo, si può fare una cosa simile:
var val = new Not<Not<int>>(new Not<int>(2));
var result = val.Optimize();
Questo funziona, perché la risoluzione di sovraccarico prenderà la chiamata Optimize corretta, che garantisce l'ottimizzazione di Non >>>> nel valore T e così via.
Funziona anche, perché avvolgiamo il "Not" in una classe wrapper e quindi usiamo il sistema di tipi a nostro vantaggio.
Tornando al problema originale
Invece di valutare direttamente 'Implica' e 'Inferi', perché non utilizzare un oggetto temporaneo per fare il vostro lavoro il male. È possibile utilizzare l'overloading dell'operatore (conversione implicita per la precisione) per specificare il modo in cui Implies e Infers sono correlati. L'unico problema è che ha i suoi limiti con i metodi di estensione.
L'overloading dell'operatore C# seleziona quindi il metodo di corrispondenza migliore. Nel primo caso questa sarà la corrispondenza esatta, nel secondo caso il metodo verrà convertito implicitamente e in seguito verrà chiamato Evaluate. In altre parole, non impilerà l'overflow, semplicemente perché farà la sua valutazione pigra. Pronto per il codice? :-)
public class Implies<T>
{
public Implies(T premise, T conclusion)
{
this.premise = premise;
this.conclusion = conclusion;
}
public T premise;
public T conclusion;
public static implicit operator Infers<T>(Implies<T> src)
{
return new Infers<T>(src.conclusion, src.premise);
}
}
public class Infers<T>
{
public Infers(T premise, T conclusion)
{
this.premise = premise;
this.conclusion = conclusion;
}
public T premise;
public T conclusion;
public static implicit operator Implies<T>(Infers<T> src)
{
return new Implies<T>(src.conclusion, src.premise);
}
}
public static partial class LogicExtensions
{
public static Implies<T> Implies<T>(this T premise, T conclusion)
{
return new Implies<T>(premise, conclusion);
}
public static Infers<T> Infers<T>(this T premise, T conclusion)
{
return new Infers<T>(premise, conclusion);
}
}
public class Foo
{
// The things you wish to implement :-)
public static bool Evaluate(Implies<int> impl)
{
return impl.premise == (impl.conclusion & impl.premise);
}
static void Main(string[] args)
{
Implies<int> impl= 0.Implies(2); // will be called directly
Infers<int> impl2 = 0.Infers(2); // will be converted
Console.WriteLine("Res: {0} {1}", Evaluate(impl), Evaluate(impl2));
Console.ReadLine();
}
}
[* Paradox *] (http://www.veterangamers.co.uk/blog/wp-content/uploads/2011/04/paradox.jpg) ... –
è al questo punto probabilmente passerei a Prolog. – spender
@spender: È tanto tempo fa, che ho scritto una volta in Prolog. Forse una risposta porting da Prolog per risolvere questo? –