1

我目前正在构建一个半复杂的计算器,它基本上是我提供的 Excel 电子表格的转换。

我已经完成了大部分工作,但是 Excel 电子表格中有一部分在 6 行和 7 列之间发生了多次计算,但问题是这些计算没有任何特定的顺序发生。

例如,Row0[Column1]计算使用(Row2[Column4] * Row2[Column5])Row1[Column4]计算使用(Row4[Column2] / Row5[Column1])等等......你明白了。

我曾考虑过使用二维数组,但担心这些值会按特定顺序计算,因此在达到它们时没有任何价值。据我所知,将首先计算 Row1 ,然后Row2Row3等。

因此,如果不为我的 excel 电子表格中的每个单元格创建一个变量(并对其进行适当排序),有没有一种方法可以使用 C# 计算它?

我真的很感激任何帮助、建议、指点,无论你认为什么是可能的——我很想听听!

编辑在实现@dtb提供的 Lazy 类之后,我得到了以下代码。这是我提供的 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.

谷歌搜索错误返回了一堆垃圾搜索类型的网站。

马尔科

4

3 回答 3

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);
}

请注意表格单元格的内容是如何以任意顺序定义的。只要单元格之间没有循环依赖关系,它就会自行计算顺序。

输出:

行 = 0 列 = 0 值 = 284
行 = 0 列 = 1 值 = 42
行 = 1 列 = 0 值 = 100
行 = 1 列 = 1 值 = 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 的Lazy<T> 类的自定义替换:

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;
        }
    }
}
于 2010-09-14T21:16:40.483 回答
0

Marko,我认为最好的方法是让你绘制出这些细胞之间的关系。如果这个问题是关于 Excel 执行此操作的顺序,我可以将您指向此处:http: //msdn.microsoft.com/en-us/library/bb687891.aspx

于 2010-09-14T21:21:58.823 回答
0

上面显示的惰性解决方案是最优雅的,我将在下面提到一个警告。

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 有一个很好的答案)。

希望这可以帮助。

于 2010-09-14T22:58:25.920 回答