帮助课堂上的数学操作数 (c#)

发布于 2024-11-01 10:34:05 字数 1084 浏览 0 评论 0原文

public class Racional<T>
{
    private T nominator;
    private T denominator;
    public T Nominator
    {
        get { return nominator; }
        set { nominator = value; }
    }
    public T Denominator
    {
        get { return denominator; }
        set { denominator = value; }
    }
    public Racional(T nominator, T denominator)
    {
        this.nominator = nominator;
        this.denominator = denominator;
    }
    public static Racional<int> operator *(Racional<int> a, Racional<int> b)
    {
        return ((int)(a.nominator + b.nominator, a.denominator + b.denominator));
    }
    public override string ToString()
    {
        return "(" + this.nominator + " " + this.denominator + ")";
    }
}

我对这部分感兴趣:

public static Racional<int> operator *(Racional<int> a, Racional<int> b)
{
    return ((int)(a.nominator + b.nominator,  a.denominator + b.denominator));
}

出了什么问题:

二元运算符的参数之一必须是包含类型

我如何正常编码这部分以进行数学运算?

public class Racional<T>
{
    private T nominator;
    private T denominator;
    public T Nominator
    {
        get { return nominator; }
        set { nominator = value; }
    }
    public T Denominator
    {
        get { return denominator; }
        set { denominator = value; }
    }
    public Racional(T nominator, T denominator)
    {
        this.nominator = nominator;
        this.denominator = denominator;
    }
    public static Racional<int> operator *(Racional<int> a, Racional<int> b)
    {
        return ((int)(a.nominator + b.nominator, a.denominator + b.denominator));
    }
    public override string ToString()
    {
        return "(" + this.nominator + " " + this.denominator + ")";
    }
}

I'm interested in this part :

public static Racional<int> operator *(Racional<int> a, Racional<int> b)
{
    return ((int)(a.nominator + b.nominator,  a.denominator + b.denominator));
}

What's wrong:

One of the parameters of a binary operator must be the containing type

How I can normaly code this part for mathematic operations?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

水晶透心 2024-11-08 10:34:05

编译器错误解释了代码无法编译的原因。包含类型是泛型类型定义,从此类类型构造的泛型类型不被视为同一类型。

我有几个问题:

  1. 为什么 Rational 类型必须是通用的?有理数定义为可以表示为两个整数的商/分数(其中分母不是0)的数字。为什么不使类型成为非泛型并在整个过程中简单地使用 int 呢?或者您是否打算将该类型用于其他整数类型,例如 longBigInteger?在这种情况下,如果您想要某种代码共享机制,请考虑使用类似阿利斯塔德的建议。
  2. 为什么两个有理数的乘积等于它们的分子之和除以分母之和?这对我来说没有意义。

无论如何,您似乎希望能够“一般地”添加“可添加”类型的两个实例。不幸的是,目前没有任何方法可以在 C# 中表达“有合适的加法运算符”约束。

方法 #1: C# 4 中的一种解决方法是使用 dynamic 类型为您提供所需的“虚拟运算符”语义。

public static Racional<T> operator *(Racional<T> a, Racional<T> b)
{
    var nominatorSum = (dynamic)a.Nominator + b.Nominator;
    var denominatorSum = (dynamic)a.Denominator + b.Denominator;

    return new Racional<T>(nominatorSum, denominatorSum);
}

如果该类型没有合适的加法运算符,该运算符将抛出异常。


方法#2:另一种(更有效)的方法是使用表达式树。

首先,创建并缓存一个委托,该委托可以通过编译适当的表达式来执行加法:(

private readonly static Func<T, T, T> Adder;

static Racional()
{
    var firstOperand = Expression.Parameter(typeof(T), "x");
    var secondOperand = Expression.Parameter(typeof(T), "y");
    var body = Expression.Add(firstOperand, secondOperand);
     Adder = Expression.Lambda<Func<T, T, T>>
                (body, firstOperand, secondOperand).Compile();    
} 

如果类型没有合适的加法运算符,静态构造函数将抛出异常。)

然后在运算符中使用它:

public static Racional<T> operator *(Racional<T> a, Racional<T> b)
{
    var nominatorSum = Adder(a.Nominator, b.Nominator);
    var denominatorSum = Adder(a.Denominator, b.Denominator);
    return new Racional<T>(nominatorSum, denominatorSum);
}

The reason your code doesn't compile is explained by the compiler error. The containing type is a generic type definition, and a generic type constructed from such a type is not considered to be the same type.

I have a few questions:

  1. Why must the Rational type be generic? A rational number is defined as a number that can be expressed as the quotient / fraction of two integers (where the denominator is not 0). Why not make the type non-generic and simply use int throughout? Or do you intend that the type be used for other integral types such as long and BigInteger? In that case, consider using something like Aliostad's suggestion if you want some code-sharing mechanism.
  2. Why do you want the product of two rational numbers to be the equal to the sum of their numerators over the sum of their denominators? That doesn't make sense to me.

In any case, you appear to want to be able to 'generically' add two instances of an 'addable' type. Unfortunately, there currently isn't any way to express a 'has a suitable addition operator' constraint in C#.

Method #1: One workaround for this in C# 4 is to use the dynamic type to give you the desired "virtual operator" semantics.

public static Racional<T> operator *(Racional<T> a, Racional<T> b)
{
    var nominatorSum = (dynamic)a.Nominator + b.Nominator;
    var denominatorSum = (dynamic)a.Denominator + b.Denominator;

    return new Racional<T>(nominatorSum, denominatorSum);
}

The operator will throw if the type doesn't have a suitable addition operator.


Method #2: Another (more efficient) way is to use expression-trees.

First, create and cache a delegate that can perform the addition by compiling the appropriate expression:

private readonly static Func<T, T, T> Adder;

static Racional()
{
    var firstOperand = Expression.Parameter(typeof(T), "x");
    var secondOperand = Expression.Parameter(typeof(T), "y");
    var body = Expression.Add(firstOperand, secondOperand);
     Adder = Expression.Lambda<Func<T, T, T>>
                (body, firstOperand, secondOperand).Compile();    
} 

(The static constructor will throw if the type doesn't have a suitable addition operator.)

Then employ it in the operator:

public static Racional<T> operator *(Racional<T> a, Racional<T> b)
{
    var nominatorSum = Adder(a.Nominator, b.Nominator);
    var denominatorSum = Adder(a.Denominator, b.Denominator);
    return new Racional<T>(nominatorSum, denominatorSum);
}
花期渐远 2024-11-08 10:34:05

这里的问题是您正在类 Racional 中为 Racional 定义一个运算符。这是不可能的。 类型不相同,只能为 Racional 定义运算符。

泛型无法表达运算符的泛化,因为它们仅为特定类型定义解决方案是创建一个类并继承自 Racional

public class IntRacional : Racional<int>
{
    public static Racional<int> operator +(IntRacional a, IntRacional b)
    {
        return new Racional<int>()
        {
            Nominator = a.Nominator + b.Nominator,
            Denominator = a.Denominator + b.Denominator
        };
    }
}

The issue here is you are defining an operator for Racional<int> in the class Racional<T>. This is not possible. The types are not the same, you can only define operator for Racional<T>.

Generics cannot express generalization of operators since they are defined only for a certain types. Solution is to create a class and inherit from Racional<int>:

public class IntRacional : Racional<int>
{
    public static Racional<int> operator +(IntRacional a, IntRacional b)
    {
        return new Racional<int>()
        {
            Nominator = a.Nominator + b.Nominator,
            Denominator = a.Denominator + b.Denominator
        };
    }
}
满天都是小星星 2024-11-08 10:34:05

要解决您的问题,您需要提供从 T 到定义了 operator+ 的某种类型的转换函数,反之亦然。假设 Int64 在大多数情况下足够大,可以这样做:

public class Racional<T> 
{
    private T nominator;
    private T denominator;
    static Converter<T,Int64> T_to_Int64;
    static Converter<Int64,T> Int64_to_T;

    public static void InitConverters(Converter<T,Int64> t2int, Converter<Int64,T> int2t )
    {
        T_to_Int64 = t2int;
        Int64_to_T = int2t;
    }

    public T Nominator
    {
        get { return nominator; }
        set { nominator = value; }
    }
    public T Denominator
    {
        get { return denominator; }
        set { denominator = value; }
    }
    public Racional(T nominator, T denominator)
    {
        this.nominator = nominator;
        this.denominator = denominator;
    }
    public static Racional<T> operator *(Racional<T> a, Racional<T> b) 
    {
        return new Racional<T>(
            Int64_to_T(T_to_Int64(a.nominator) + T_to_Int64(b.nominator)),
            Int64_to_T(T_to_Int64(a.denominator) + T_to_Int64(b.denominator)));
    }

    // By the way, should this not be * instead of + ???
    //
    // public static Racional<T> operator *(Racional<T> a, Racional<T> b) 
    // {
    //    return new Racional<T>(
    //        Int64_to_T(T_to_Int64(a.nominator) * T_to_Int64(b.nominator)),
    //        Int64_to_T(T_to_Int64(a.denominator) * T_to_Int64(b.denominator)));
    // }



    public override string ToString()
    {
        return "(" + this.nominator + " " + this.denominator + ")";
    }
}

当然,这有一个缺点,您必须在程序启动时的某个地方提供这些转换器的初始化,应该如下所示:

Racional<int>.InitConverters(x => (Int64)x, y => (int)y);

在真实的程序中,您可能知道要使用哪些可能的 T 替代品。因此,可以在静态构造函数中提供这 3 或 4 个调用,如下所示:

    public static Racional()
    {
        Racional<int>.InitConverters(x => (Int64)x, y => (int)y);
        Racional<short>.InitConverters(x => (Int64)x, y => (short)y);
        Racional<Int64>.InitConverters(x => (Int64)x, y => (Int64)y);
    }

在大多数情况下应该足够了。请注意,此转换器初始化对所有 3 种类型重复 3 次,从而多次重新初始化转换函数。实际上这不会造成任何麻烦。

To solve your issue, you need to provide conversion functions from T to some type where operator+ is defined and vice versa. Assuming Int64 is big enough in most cases, this can be done this way:

public class Racional<T> 
{
    private T nominator;
    private T denominator;
    static Converter<T,Int64> T_to_Int64;
    static Converter<Int64,T> Int64_to_T;

    public static void InitConverters(Converter<T,Int64> t2int, Converter<Int64,T> int2t )
    {
        T_to_Int64 = t2int;
        Int64_to_T = int2t;
    }

    public T Nominator
    {
        get { return nominator; }
        set { nominator = value; }
    }
    public T Denominator
    {
        get { return denominator; }
        set { denominator = value; }
    }
    public Racional(T nominator, T denominator)
    {
        this.nominator = nominator;
        this.denominator = denominator;
    }
    public static Racional<T> operator *(Racional<T> a, Racional<T> b) 
    {
        return new Racional<T>(
            Int64_to_T(T_to_Int64(a.nominator) + T_to_Int64(b.nominator)),
            Int64_to_T(T_to_Int64(a.denominator) + T_to_Int64(b.denominator)));
    }

    // By the way, should this not be * instead of + ???
    //
    // public static Racional<T> operator *(Racional<T> a, Racional<T> b) 
    // {
    //    return new Racional<T>(
    //        Int64_to_T(T_to_Int64(a.nominator) * T_to_Int64(b.nominator)),
    //        Int64_to_T(T_to_Int64(a.denominator) * T_to_Int64(b.denominator)));
    // }



    public override string ToString()
    {
        return "(" + this.nominator + " " + this.denominator + ")";
    }
}

Of course, this has the drawback that you must provide the initialization of those converters somewhere at the program start, should look like this:

Racional<int>.InitConverters(x => (Int64)x, y => (int)y);

In a real program, you may know which possible replacements for T you are going to use. So one can provide those 3 or 4 calls in a static constructor like this:

    public static Racional()
    {
        Racional<int>.InitConverters(x => (Int64)x, y => (int)y);
        Racional<short>.InitConverters(x => (Int64)x, y => (short)y);
        Racional<Int64>.InitConverters(x => (Int64)x, y => (Int64)y);
    }

should be sufficient in most cases. Note that this converter initialization is repeated for all 3 types 3 times again, re-initializing the conversion functions multiple times again. In practice this should not make any trouble.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文