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;
}
C#: оптимальное сложение списка чисел разных типов
Хочу реализовать сложение списка чисел разных типов (для начала int, float и double), представленных object'ами, максимально эффективым образом (отдавая себе при этом отчёт, что это в любом случае будет не ахти).
Вот что у меня пока что получилось:
Код:
Мне этот код не совсем нравится из-за потребности постоянного использоания Convert, а кроме того, у меня есть подозрение, что это можно сделать лучше.
Поясню как работает вышеприведённая функция и зачем именно так: первым проходом по всему списку выбирается максимально толстый тип, затем он запоминается в desiredType чтобы не нужно было его вдальнейшем вычислять на каждой итерации typeof'ом. Последующее ветвление так ужасно только для оптимизации: чтобы на каждой итерации не нужно было вычислять к какому типу приводить iter.head и чтобы избавиться от необходимости конвертать сам result.
Может быть у кого-нибудь есть какие-то соображения или критика по теме?
А если использовать (родное дотнетное) неявное приведение типов? В принципе, делать должно то же самое. Предложил бы ещё по умолчанию всё конвертировать в толстые типы, но идея эта мне самому не нравится.
Цитата: 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);
}
}
}
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);
}
}
}
При добавлении нового типа XxxNumber потребуется в существующие типы добавлять соответствующие конвертеры. Если посидеть-подумать, то, полагаю, можно сделать совсем элегантное решение, но на Nemerle, когда компилятор сам сгенерирует для нас типы-обертки и конвертеры.
Цитата: hardcase
В C++ это выглядело бы покрасивее
Странно такое слышать...
[QUOTE=hardcase]
[FONT=Courier New]Качу балоны на C++. Оптом. Недорого.[/FONT]
[/QUOTE]
Sanila_san, функция, описанная в первом посте, суммирует миллион интов за 47-63 мс, а функция, приводящая всё к double - за 140-156.
[QUOTE=Ander Skirnir]Sanila_san, функция, описанная в первом посте, суммирует миллион интов за 47-63 мс, а функция, приводящая всё к double - за 140-156.[/QUOTE]Без вопросов.:) hardcase всё равно лучшее решение предложил.