1

В настоящее время я создаю полусложный калькулятор, который в основном представляет собой преобразование из электронной таблицы Excel, которую я предоставил.Вычисление массива значений в C# (преобразование формулы из Excel)

Я прикрепил большую часть его, но есть часть в электронной таблице Excel, где несколько расчетов происходят между 6 строками и 7 столбцами, но проблема в том, что вычисления происходят не в каком-то конкретном порядке.

Так, например, Row0[Column1] рассчитывается с использованием (Row2[Column4] * Row2[Column5]) и Row1[Column4] рассчитывается с использованием (Row4[Column2]/Row5[Column1]) и так далее .. Вы получаете идею.

Я думал об использовании 2D-массива, но боюсь, что значения будут вычисляться в определенном порядке, не имея при этом значения. Насколько мне известно, Row1 будет рассчитываться первым, затем Стр2, Row3 и т.д.

Таким образом, не создавая переменную для каждой ячейки в моей таблицы Excel (и приказывать его соответствующим образом) , есть ли способ, который я могу вычислить с помощью C#?

Я бы очень признателен за любую помощь, советы, указания, что бы вы ни думали, возможно, я хотел бы это услышать!

EDIT После реализации отложенного класса предоставленного @dtb, у меня есть следующий код. Это прямая копия того, что было в электронной таблице Excel, которую я предоставил, включая указатели & расчетов.

var sr = new Lazy<decimal>[6, 6]; 
sr[0, 0] = new Lazy<decimal>(() => sr[1, 0].Value - eNumber); 
sr[0, 3] = new Lazy<decimal>(() => sr[0, 4].Value - sr[1, 0].Value - sr[1, 4].Value); 
sr[0, 4] = new Lazy<decimal>(() => sr[0, 0].Value * edD); 
sr[0, 5] = new Lazy<decimal>(() => sr[0, 0].Value); 

sr[1, 0] = new Lazy<decimal>(() => sr[1, 5].Value); 
sr[1, 4] = new Lazy<decimal>(() => sr[1, 0].Value * edD); 
sr[1, 5] = new Lazy<decimal>(() => sr[2, 0].Value + sr[2, 5].Value); 

sr[2, 0] = new Lazy<decimal>(() => eNumber * rRate); 
sr[2, 4] = new Lazy<decimal>(() => sr[2, 0].Value * hdD); 
sr[2, 5] = new Lazy<decimal>(() => sr[1, 5].Value); 

sr[3, 1] = new Lazy<decimal>(() => sr[2, 5].Value); 

sr[4, 2] = new Lazy<decimal>(() => eNumber * (ePc/100) + sr[2, 0].Value * (hlPc/100) - sr[3, 1].Value); 

sr[5, 0] = new Lazy<decimal>(() => (sr[0, 0].Value + sr[1, 0].Value + sr[2, 0].Value)/ePerR); 
sr[5, 2] = new Lazy<decimal>(() => sr[5, 0].Value/rLifecycle); 
sr[5, 4] = new Lazy<decimal>(() => sr[5, 2].Value); 
sr[5, 5] = new Lazy<decimal>(() => sr[5, 0].Value + sr[5, 2].Value - sr[5, 4].Value); 

Однако я получаю следующую ошибку
ValueFactory attempted to access the Value property of this instance.

погуглить ошибка вернула кучу спам-сайтов поиска типа.

Марко

+0

Какая ошибка? Ошибка компилятора или исключение? Усилили ли вы класс Lazy для обнаружения круговой зависимости? Потому что в ваших определениях есть один: sr [1,5] определяется в терминах sr [2,5] и наоборот. – dtb

+0

Привет @dtb - проект компилируется, но я получаю исключение. Я прибегал к использованию .NET 4 (пока я не получу его в исходном классе). Кстати, я не уверен, как расширить класс для Circular Dependencies, это (совсем) немного вне моей области знаний. – Marko

+0

Тогда вы только что столкнулись с обнаружением круговой зависимости класса .NET 4.0 Lazy. Вы можете реализовать его самостоятельно, установив valueCreated в 1 * перед * вызовом valueFactory, до 2 после и путем исключения исключения, если Value вызывается, а valueCreated - 1 ... Двойная проверка, действительно ли на вашем листе excel действительно круговое определение. Если есть, опубликуйте новый вопрос с формулами excel и спросите, как вычислить фиксированную точку в C#. Но я подозреваю, что у вас есть опечатка в вашем коде. – dtb

ответ

6

Посмотрите Ленивый оценки:

var table = new Lazy<int>[2, 2]; 

table[0, 0] = new Lazy<int>(() => table[1, 1].Value * 2); 
table[0, 1] = new Lazy<int>(() => 42); 
table[1, 0] = new Lazy<int>(() => 100); 
table[1, 1] = new Lazy<int>(() => table[0, 1].Value + table[1, 0].Value); 

for (int i = 0; i < 2; i++) 
for (int j = 0; j < 2; j++) 
{ 
    Console.WriteLine("Row = {0} Column = {1} Value = {2}", 
          i,   j,   table[i, j].Value); 
} 

Обратите внимание, как содержание ячеек таблицы определяются в произвольном порядке. Он вычисляет сам порядок, если между ячейками нет круговых зависимостей.

Выход:

 
Row = 0 Column = 0 Value = 284 
Row = 0 Column = 1 Value = 42 
Row = 1 Column = 0 Value = 100 
Row = 1 Column = 1 Value = 142 

Это становится немного более удобным для чтения с LINQ-к-Лазы:

var table = new Lazy<int>[2, 2]; 

table[0, 0] = from t in table.AsLazy() 
       from x in t[1, 1] 
       select 2 * x; 
table[0, 1] = 42.AsLazy(); 
table[1, 0] = 100.AsLazy(); 
table[1, 1] = from t in table.AsLazy() 
       from a in t[0, 1] 
       from b in t[1, 0] 
       select a + b; 

использованием

static class LazyExtensions 
{ 
    public static Lazy<TResult> SelectMany<TSource, TCollection, TResult>(this Lazy<TSource> source, Func<TSource, Lazy<TCollection>> collectionSelector, Func<TSource, TCollection, TResult> resultSelector) 
    { 
     return new Lazy<TResult>(() => resultSelector(source.Value, collectionSelector(source.Value).Value)); 
    } 

    public static Lazy<TSource> AsLazy<TSource>(this TSource value) 
    { 
     return new Lazy<TSource>(() => value); 
    } 
} 

Пользовательская замена для .NET 4.0-х Lazy<T> Class:

sealed class MyLazy<T> 
{ 
    private readonly Func<T> valueFactory; 
    private T value; 
    private bool valueCreated; 

    public MyLazy(Func<T> valueFactory) 
    { 
     if (valueFactory == null) 
     { 
      throw new ArgumentNullException("valueFactory"); 
     } 
     this.valueFactory = valueFactory; 
    } 

    public bool IsValueCreated 
    { 
     get { return this.valueCreated; } 
    } 

    public T Value 
    { 
     get 
     { 
      if (!this.valueCreated) 
      { 
       this.value = this.valueFactory(); 
       this.valueCreated = true; 
      } 
      return this.value; 
     } 
    } 
} 
+0

Lazy пользовательский класс? Я использую WPF (.NET 3.5), и Lazy не существует. – Marko

+0

[Lazy ] (http://msdn.microsoft.com/en-us/library/dd642331.aspx) является новым в .NET Framework 4.0. Вы можете легко создать пользовательскую замену, если вы застряли с 3.5. – dtb

+0

Да, я застрял с 3.5 на этом :(Я пытаюсь теперь с пользовательским, который вы предоставили. – Marko

0

Марко, я думаю, что лучший способ для вас, чтобы наметить отношения между этими клетками. Если этот вопрос касается порядка, в котором Excel мог бы это сделать, я могу указать вас сюда: http://msdn.microsoft.com/en-us/library/bb687891.aspx

0

Лендивое решение, показанное выше, является самым элегантным, с одним предостережением, о котором я расскажу ниже.

План А

Вы можете закодировать свою собственную версию Lazy<T> довольно легко (это непроверенный код):

class Lazy<T> { 
    private bool IsEvaluated; 
    private T Value; 
    private Func<T> Suspension; 
    public Lazy<T>(Func<T> susp) { Suspension = susp; } 
    public static implicit operator T(Lazy<T> thunk) { 
    if (thunk.IsEvaluated) { 
     return thunk.Value; 
    } 
    thunk.Value = thunk.Suspension(); 
    thunk.IsEvaluated = true; 
    return thunk.Value; 
    } 
} 

Конечно, вам нужно определить перегруженные арифметические операторы, как Что ж.

Plan B

Другой способ решения вашей проблемы является сортировать клетки в порядке возрастания зависимостей (где ячейка А зависит от клетки В, если А содержит формулу, которая использует B, прямо или косвенно) и оценка их в этом порядке.

Caveat

Если зависимостей содержит цикл, то ни один из этих подходов гарантированно работать так как вам нужно будет оценить в фиксированной точке. В этом случае вам, вероятно, понадобится что-то вроде Plan B, но сначала разбивайте график зависимостей на сильно связанные компоненты (на этом сайте есть хороший ответ на SCC).

Надеюсь, это поможет.