public static partial class LogicExtensions {
public static bool Implies<T>(this T premise, T conclusion) {
return conclusion.Infers(premise);
}
public static bool Infers<T>(this T premise, T conclusion) {
return premise.Implies(conclusion);
}
}
上面的代码希望表达:
结论推断了前提,因为前提暗示了结论。
前提暗示了结论,因为结论推断了前提。
这将是circular reasoning,肯定会导致堆栈溢出。然后我重新设计如下:
public delegate bool Paradox<T>(T premise, T conclusion, Paradox<T> predicate=null);
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 LogicExtensions.Implies(conclusion as IConvertible, premise as IConvertible);
}
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 LogicExtensions.Implies(conclusion as IConvertible, premise as IConvertible);
}
static bool Implies<T>(T premise, T conclusion) where T: IConvertible {
var x=premise.ToUInt64(null);
return x==(x&conclusion.ToUInt64(null));
}
}
但这意味着:
Paradox<T>
,我最初命名为Predicate<T>
,但与System.Predicate<T>
冲突。T
必须实现IConvertable
与代码前者不同,这是有缺陷的。为了清楚起见,我试图使代码不仅起作用,而且代表逻辑公式,我可以进一步重用它来推理逻辑而没有T
的约束实现IConvertable
。有没有办法使逻辑正确并摆脱有缺陷的设计?
你的问题不是很清楚你想要做什么。您是否尝试在C#中表达一些逻辑谓词?您是否正在尝试编写可以推理逻辑的代码?你想表现出逻辑公式吗?
悖论。当谈论计算中的悖论时,阅读lambda演算和Russel悖论(here is a nice article)可能会更好。 Lambda演算本质上是一种简单的函数式编程语言(想象一下具有lambda函数和应用程序的C#,但没有别的)。
它最初是作为数学基础的系统开发的(在计算机发明之前),但这并没有真正起作用,因为你能够编写没有意义的递归计算(详见文章),但你可以写计算结果如下(以C#表示法):
r(r) = not(r(r)) = not(not(r(r)))
...因为没有x = r(r)
这样的x = not(x)
,这个模型作为数学基础没有意义。但它作为编程语言的模型非常有用,您可以在其中编写递归计算 - 尽管它们可能永远不会终止。
代表逻辑。如果要在程序中表示逻辑公式,那么您可能希望将公式的表示与推理分开。这最好用函数式语言(比如F#)完成,但你也可以在C#中完成(只需要输入更多内容)。公式的F#表示如下:
type Formula =
| Variable of string
| Negation of Formula
| Implies of Formula * Formula
这个想法是公式是一个变量(命名)或另一个公式的否定或一个公式暗示另一个公式的含义。在C#中,您可以表示与类层次结构相同的东西(使用Formula
作为基类和三个派生类。)
然后可以将您的推理实现为操纵公式的方法。在F#中,使用模式匹配可以很容易地完成。在C#中,您可能需要使用类型测试来编写代码来检查参数是否为Variable
(然后执行某些操作......);如果争论是Negation
(然后做某事......)等
丢弃IConvertible
让我们从'简单部分'开始:放下IConvertible
。您需要它的原因是因为您希望此代码适用于所有类型,这意味着您不能总是影响它具有某个成员(Implies
)。你想要做的是他们在C ++中所称的:模板专业化,但遗憾的是在C#中不可用(但是?):
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
解决这个问题的最简单方法是使用多方法。您可以使用'dynamic'关键字:
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);
}
}
如果你有一个'第三'方法,你可以简单地调用它
我一直在寻找奇怪的递归定义几分钟而且它对我来说真的没有意义......如果你还有第三个辅助方法,为什么不直接调用呢? :-)
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);
}
不是(不是(T))问题
虽然上面对我来说没有多大意义,但我觉得使用类型系统和语言来帮助你有点合理是完全合理的。嗯,当然你可以做到这一点,这就是我会这样做... :-)
让我们介绍一个带有泛型的'Not'类:
public class Not<T>
{
public Not(T val)
{
this.not = val;
}
internal T not;
}
如果我们在这里有一个Not>情况,我们想给 - 否则,我们想直接使用。好吧,我们可以通过一些扩展很容易做到这一点:
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;
}
要测试它,你可以做类似的事情:
var val = new Not<Not<int>>(new Not<int>(2));
var result = val.Optimize();
这是有效的,因为重载决策将选择正确的Optimize调用,这确保您将Not >>>>优化为T值等等。
它也有效,因为我们将'Not'包装在一个包装类中,然后使用类型系统来获得优势。
回到原来的问题
而不是直接评估'Implies'和'推断',为什么不使用临时对象来做你的邪恶工作。您可以使用运算符重载(精确的隐式转换)来指定Implies和Infers的关联方式。唯一的问题是它有扩展方法的限制。
然后,C#运算符重载将选择最佳匹配方法。在第一种情况下,这将是完全匹配,在第二种情况下,将隐式转换方法,然后将调用Evaluate。换句话说,它不会叠加溢出,只是因为它会使其评估变得懒惰。准备好代码? :-)
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();
}
}