非正式谬误导致堆栈溢出
Posted
技术标签:
【中文标题】非正式谬误导致堆栈溢出【英文标题】:Informal fallacy causes stack overflow 【发布时间】:2013-03-17 11:37:49 【问题描述】:破码
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
。有没有办法使逻辑正确并摆脱有缺陷的设计?
【问题讨论】:
Paradox<T>... 此时我可能会切换到 Prolog。 这段代码到底是干什么用的?我的意思是,C# 代码做了 什么,它不表达 什么。 哈! 16 年前,我做了大约 4 个月的 Prolog。我可能拥有的任何技能现在都已成为历史。 @StefanSteinegger:如果 C# 代码不表达任何东西,它就什么也做不了。 【参考方案1】:您的问题不是很清楚您要做什么。您是否尝试在 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
(然后做一些事情......)等等。
【讨论】:
【参考方案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();
【讨论】:
我刚刚阅读,还没有测试。看起来不错,但很像一种解决方法,对吗? @KenKin 好吧,是的,不是的...是的,因为根本不支持泛型专业化,因此您需要一些解决方法...这是一种帮助编译器的辅助类型。没有,因为我不直接评估值,而是使用辅助类来推迟操作。以上是关于非正式谬误导致堆栈溢出的主要内容,如果未能解决你的问题,请参考以下文章
.NET 异常处理程序导致 Visual C++ 6.0 异常的堆栈溢出