代码之家  ›  专栏  ›  技术社区  ›  Feryt

C:通用数学函数(最小、最大等)

  •  13
  • Feryt  · 技术社区  · 15 年前

    我在考虑为一些基本的数学运算编写通用函数,比如min、max等。 但我不知道如何比较两种通用类型:

    public T Max<T>(T v1, T v2) where T: struct
    {
       return (v1 > v2 ? v1 : v2);
    }
    

    那怎么样?

    谢谢您。

    6 回复  |  直到 6 年前
        1
  •  22
  •   LukeH    15 年前

    如果只想创建比较函数,则可以使用 default comparer 针对类型 T . 例如:

    public static T Max<T>(T x, T y)
    {
        return (Comparer<T>.Default.Compare(x, y) > 0) ? x : y;
    }
    

    如果 T 器具 IComparable<T> 然后将使用该比较器;如果 T 不执行 i可比较<t> 但执行 IComparable 然后将使用该比较器;如果 T 也不执行 i可比较<t> 可计算的 然后将引发运行时异常。

    如果你想/需要做的不仅仅是比较项目,那么你可以看看 generic operators implementation in MiscUtil 以及 related article .

        2
  •  22
  •   Joey Gumbo    15 年前

    你可能想 constrain 要实现的泛型类型 IComparable 以下内容:

    public T Max<T>(T v1, T v2) where T: struct, IComparable<T>
    

    然后使用 CompareTo 方法:

    {
        return (v1.CompareTo(v2) > 0 ? v1 : v2);
    }
    
        3
  •  4
  •   Fernando Pelliccioni    11 年前

    我不同意。 @lukeh的实施 不是通用的 .

    我将解释为什么它不是通用的:

    Comparer<T>.Default 涉及在运行时检查t以确定它是否实现 IComparable<T> , IComparable 或者两者都没有。 尽管这种行为在 http://msdn.microsoft.com/en-us/library/azhsac5f.aspx 我们可以扣除,因为 比较器<t>。默认值 当t既不实现也不实现时引发异常。如果检查是在编译时完成的,那么就不需要异常(运行时),只要有编译时错误就足够了。

    然后,作为 比较器<t>。默认值 使用反射,这意味着运行时成本很高,然后….. 它不是一般的 …为什么?

    因为 通用程序设计 手段: 一个单一的算法(通用)可以覆盖许多实现(对于许多类型),从而保持手写版本的效率。

    举个例子。整数的手写版本为:

    public static int Max( int x, int y)
    {
        return (x.CompareTo(y) > 0) ? x : y;
    }
    

    它非常简单,只涉及一个比较(或者更多,取决于Int32.CompareTo()的实现方式)。 如果我们使用@lukeh的实现,我们将为一些非常简单的东西添加反射。

    简而言之:

    1. 总是更喜欢编译时错误而不是运行时异常(这不是javascript、ruby,…-)
    2. 与手写版本(对于任何类型)相比,此实现效率较低。

    另一方面。 当x和y相等时,你认为max应该返回什么?

    我开始分析真正的通用实现……

    理想的实现方式是……

        public static T Max<T>(T x, T y, Func<T, T, int> cmp)
        {
            return (cmp(x, y) > 0) ? x : y;
        }
    
        //Pseudo-code ( note the 'or' next to 'where' )
        public static T Max<T>(T x, T y) where T: IComparable<T> or IComparable
        {
            return Max(x, y, (a, b) => { return a.CompareTo(b); });
        }
    

    这在C中是不可能的,下一次尝试可能是…

        //pseudo-code
        public static T Max<T>(T x, T y, Func<T, T, int> cmp)
        {
            return (cmp(x, y) > 0) ? x : y;
        }
    
        public static T Max<T>(T x, T y) where T: IComparable<T>
        {
            return Max(x, y, (a, b) => { return a.CompareTo(b); });
        }
    
        public static T Max<T>(T x, T y) where T: IComparable
        {
            return Max(x, y, (a, b) => { return a.CompareTo(b); });
        }
    

    但是,这两者都不可能,因为重载解析不考虑泛型约束…

    那么,我就不提了 可计算的 有意识地。我只是担心 i可比较<t>

        public static T Max<T>(T x, T y, Func<T, T, int> cmp)
        {
            return (cmp(x, y) > 0) ? x : y;
        }
    
        public static T Max<T>(T x, T y) where T: IComparable<T>
        {
            return Max(x, y, (a, b) => { return a.CompareTo(b); });
        }
    
        4
  •  3
  •   Indy9000    12 年前

    这有点晚了,但为什么不使用动态类型和委托作为IComparable的替代方法呢?这样,在大多数情况下都可以获得编译类型的安全性,并且只有在所提供的类型都不支持运算符<并且无法将默认比较器作为参数提供时才会出现运行时错误。

    public static T Max<T>(T first, T second, Func<T,T,bool> f = null)
    {
        Func<dynamic,dynamic,bool> is_left_smaller = (x, y) => x < y ? true : false;
    
        var compare = f ?? new Func<T, T, bool>((x, y) => is_left_smaller(x, y));
    
        return compare(first, second) ? second : first; 
    }
    
        5
  •  0
  •   Will    15 年前

    从记忆中,T也需要 IComparable (把这个加到 where ,然后使用 v1.CompareTo(v2) > 0 等。

        6
  •  -1
  •   Shadi Alnamrouti    6 年前

    为什么要重新发明数学的轮子

    为什么要重新思考多年来得到增强的现有函数的内部数学,例如: Min(), Round(), Ceiling(), Abs(), Floor(), Clamp(), Truncate() and Sign()

    我建议使用 动态类型 从而利用运行时函数 超载 无需重新考虑多年来调试的现有科学的内部实施:

    public static T Max<T>(T a, T b) where T : struct
    {
       return Math.Max((dynamic) a, (dynamic) b);
    }