Вычисление массива значений в C # (преобразование формул из Excel) - PullRequest
1 голос
/ 15 сентября 2010

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

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

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

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

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

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

EDIT После реализации класса Lazy, предоставленного @ 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.

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

Marko

Ответы [ 3 ]

6 голосов
/ 15 сентября 2010

Взгляните на Ленивая оценка :

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-to-Lazy :

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 Ленивый Класс :

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 голосов
/ 15 сентября 2010

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

План A

Вы можете довольно легко кодировать свою собственную версию 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;
  }
}

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

План B

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

Протест

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

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

0 голосов
/ 15 сентября 2010

Марко, я думаю, лучший способ для вас - наметить отношения между этими клетками Если этот вопрос касается порядка, в котором Excel будет делать это, я могу указать вам здесь:

...