Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

C#: оптимальное сложение списка чисел разных типов

29K
21 ноября 2009 года
Ander Skirnir
109 / / 08.06.2009
Пишу курсовую, заданием которой является написание компилятора некоторого динамического языка на C# (это требование).

Хочу реализовать сложение списка чисел разных типов (для начала int, float и double), представленных object'ами, максимально эффективым образом (отдавая себе при этом отчёт, что это в любом случае будет не ахти).
Вот что у меня пока что получилось:
Код:
public static object Add(Cons arglist)
        {            
            byte castcode = 0; // 0 - int, 1 - float, 2 - double

            Cons iter = arglist;
            while (iter != null)
            {
                if (castcode < 2 && iter.head is double)
                    castcode = 2;
                else if (castcode < 1 && iter.head is float)
                    castcode = 1;

                iter = (Cons)iter.tail;
            }

            Type desiredType = typeof(int);

            if (castcode == 1) desiredType = typeof(float);
            else if (castcode == 2) desiredType = typeof(double);

            iter = arglist;

            if (castcode == 0)
            {
                int iresult = 0;
                while (iter != null)
                {
                    iresult += (int)Convert.ChangeType(iter.head, desiredType);
                    iter = (Cons)iter.tail;
                }
                return iresult;
            }

            if (castcode == 1)
            {
                float fresult = 0;
                while (iter != null)
                {
                    fresult += (float)Convert.ChangeType(iter.head, desiredType);
                    iter = (Cons)iter.tail;
                }
                return fresult;
            }

            if (castcode == 2)
            {
                double dresult = 0;
                while (iter != null)
                {
                    dresult += (double)Convert.ChangeType(iter.head, desiredType);
                    iter = (Cons)iter.tail;
                }
                return dresult;
            }

            return 0;
        }


Мне этот код не совсем нравится из-за потребности постоянного использоания Convert, а кроме того, у меня есть подозрение, что это можно сделать лучше.

Поясню как работает вышеприведённая функция и зачем именно так: первым проходом по всему списку выбирается максимально толстый тип, затем он запоминается в desiredType чтобы не нужно было его вдальнейшем вычислять на каждой итерации typeof'ом. Последующее ветвление так ужасно только для оптимизации: чтобы на каждой итерации не нужно было вычислять к какому типу приводить iter.head и чтобы избавиться от необходимости конвертать сам result.

Может быть у кого-нибудь есть какие-то соображения или критика по теме?
241
21 ноября 2009 года
Sanila_san
1.6K / / 07.06.2005
А если использовать (родное дотнетное) неявное приведение типов? В принципе, делать должно то же самое. Предложил бы ещё по умолчанию всё конвертировать в толстые типы, но идея эта мне самому не нравится.
5
21 ноября 2009 года
hardcase
4.5K / / 09.08.2005
Цитата: Sanila_san
А если использовать (родное дотнетное) неявное приведение типов? В принципе, делать должно то же самое.

Числа конвертирует не дотнет, но компиляторы, неявное приведение типа - суть вызова статического метода.
Как вариант, можно сделать собственный набор типов для представления чисел. Вот заготовка:

Код:
using System;
using System.Collections.Generic;

namespace ConsoleApplication22 {

    public abstract class Number {

        public abstract Type NumberType {
            get;
        }

        public abstract Number Add(Number number);

        public static Number operator +(Number a, Number b) {
            return a.Add(b);
        }

        public static implicit operator Number(int x) {
            return new Int32Number(x);
        }

        public static implicit operator Number(double x) {
            return new DoubleNumber(x);
        }

    }

    public abstract class Number<T> : Number {

        private static readonly Dictionary<Type, Converter<Number, Number<T>>> converters = new Dictionary<Type, Converter<Number, Number<T>>>();

        protected static void RegisterConverter<Key>(Converter<Number<Key>, Number<T>> converter) {
            converters[typeof(Key)] = delegate(Number n) {
                return converter((Number<Key>)n);
            };
        }

        protected static Number<T> Cast(Number number) {
            Type source_type = number.NumberType;
            if(source_type == typeof(T))
                return (Number<T>)number;
            Converter<Number, Number<T>> converter;
            if (converters.TryGetValue(source_type, out converter))
                return converter(number);

            throw new InvalidCastException(string.Format("Number type {0} is not convertible to {1}.", source_type, typeof(T)));
        }

        public Number(T value) {
            this.value = value;
        }

        private readonly T value;

        public T Value {
            get {
                return value;
            }
        }

        public sealed override Type NumberType {
            get {
                return typeof(T);
            }
        }

        public override Number Add(Number number) {
            return Add(Cast(number));
        }

        protected abstract Number<T> Add(Number<T> number);

        public override string ToString() {
            return NumberType.ToString() + " " + Value.ToString();
        }

    }

    public sealed class Int32Number : Number<int> {

        static Int32Number() {
            RegisterConverter<double>(delegate(Number<double> src) {
                return new Int32Number((int)src.Value);
            });
        }

        public Int32Number(int value)
            : base(value) {
        }

        protected override Number<int> Add(Number<int> number) {
            return new Int32Number(Value + number.Value);
        }

    }



    public sealed class DoubleNumber : Number<double> {

        static DoubleNumber() {
            RegisterConverter<int>(delegate(Number<int> src) {
                return new DoubleNumber(src.Value);
            });
        }

        public DoubleNumber(double value)
            : base(value) {
        }

        protected override Number<double> Add(Number<double> number) {
            return new DoubleNumber(Value + number.Value);
        }

    }


    class Program {

        static void Main(string[] args) {
            Number a = 13.7;
            Number b = 2;

            Console.WriteLine(a + a);
            Console.WriteLine(b + b);


            Console.WriteLine(a + b);
            Console.WriteLine(b + a);

            Console.ReadKey(true);
        }
    }
}
В C++ это выглядело бы покрасивее - там есть частные спецификации шаблонов.


При добавлении нового типа XxxNumber потребуется в существующие типы добавлять соответствующие конвертеры. Если посидеть-подумать, то, полагаю, можно сделать совсем элегантное решение, но на Nemerle, когда компилятор сам сгенерирует для нас типы-обертки и конвертеры.
11
21 ноября 2009 года
oxotnik333
2.9K / / 03.08.2007
Цитата: hardcase
В C++ это выглядело бы покрасивее


Странно такое слышать...
[QUOTE=hardcase]
[FONT=Courier New]Качу балоны на C++. Оптом. Недорого.[/FONT]
[/QUOTE]

29K
21 ноября 2009 года
Ander Skirnir
109 / / 08.06.2009
hardcase, спасибо, удобное и красивое решение, буду думать как его оптимально использовать - надеюсь, такая прослойка не сильно навредит производительности.

Sanila_san, функция, описанная в первом посте, суммирует миллион интов за 47-63 мс, а функция, приводящая всё к double - за 140-156.
241
22 ноября 2009 года
Sanila_san
1.6K / / 07.06.2005
[QUOTE=Ander Skirnir]Sanila_san, функция, описанная в первом посте, суммирует миллион интов за 47-63 мс, а функция, приводящая всё к double - за 140-156.[/QUOTE]Без вопросов.:) hardcase всё равно лучшее решение предложил.
Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог