9
  • 破码

    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);
        }
    }
    

上面的代码期望表达:

结论推断前提,因为前提蕴含结论。

前提暗示结论,因为结论推断前提。

这将是循环推理,肯定会导致堆栈溢出。然后我重新设计如下:

  • 工作代码

    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));
        }
    }
    

但这意味着:

  1. 它在正确的逻辑上失败了,它不能没有Paradox<T>我最初命名Predicate<T>但与System.Predicate<T>.

  2. 与代码生成器不同,它T必须实现的缺陷。IConvertable

需要明确的是,我试图使代码不仅可以工作,而且还可以表示类似的逻辑公式,我可以进一步重用它来推理逻辑,而不受Timplements的约束IConvertable。有没有办法使逻辑正确并摆脱有缺陷的设计?

4

2 回答 2

9

从您的问题中不清楚您要做什么。您是否尝试在 C# 中表达一些逻辑谓词?您是否正在尝试编写能够推理逻辑的代码?您是否试图表示逻辑公式?

悖论。在谈论计算中的悖论时,最好阅读 lambda 演算和 Russel 悖论(这是一篇不错的文章)。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(然后做某事......)等。

于 2013-03-27T12:31:20.547 回答
2

丢弃 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

解决这个问题的最简单方法是使用多方法。您可以为此使用“动态”关键字:

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);
    }

not(not(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 和 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();
    }
}
于 2013-04-08T15:02:02.727 回答