【问题标题】:Implement class to work with real time data实现类以处理实时数据
【发布时间】:2020-03-31 22:23:40
【问题描述】:

使用C#我需要转换一个时间序列,例如:

Input: 1 2 1 0 3 2 -> Output: 1 3 4 4 7 9

在前面的示例中,输出是当前输入与之前输入的总和。

我会有不同的转换,所以我从一个抽象类开始:

public abstract class Transformation<TInput, TOutput> {

  private IEnumerable<TInput> _inputs { get; set; }

  public Transformation(IEnumerable<TInput> inputs) { 
    _inputs = inputs;
  }

  public abstract IEnumerable<TOutput> Calculate();

}

所以转换应该是这样的:

public class TransformationOne<Int32, Int32> {

  public override IEnumerable<Int32> Calculate();
    // Calculate the outputs with all inputs
  }

}

如果我有所有输入并想要所有输出,这很好用。

但有时我想实时使用我的课程。

所以我会输入一个新的输入并得到一个新的输出。

实现这一点的最佳方法是什么?

【问题讨论】:

  • 实时是什么意思,它与您现在使用的方式有何不同?
  • 您可能想要使用 IEnumerable 的神秘表亲 IObservable 和 Reactive Extensions。 github.com/dotnet/reactive
  • 大概在您的 Calculate 方法中,您正在运行一个循环。您应该能够将循环内容分离到 CalculateStepCalculateNext 方法中,并将每个输入从 CalculateNext 推送到可枚举
  • 您需要做的是构建一个保持其状态的累加器。您创建它时将状态设置为零。然后,你给它一个值,它将它添加到状态中,更新状态并返回结果。您可能还希望有一个Clear 方法来重置状态。要传递一个序列,你需要初始化它,然后一个一个地传递值。
  • @GSerg 我的意思是实时是我可能一次输入一个输入

标签: c#


【解决方案1】:

保持类中的状态并将输入发送到Calculate方法?

class TransformationOne
{
    private int _currentResult;

    public int Calculate(int value)
    {
        _currentResult += value;
        return _currentResult;
    }
}

所以你可以这样做:

var transformationOne = new TransformationOne();

var inputs = new List<int> {1, 2, 1, 0, 3, 2};

foreach (var input in inputs)
{
    var newResult = transformationOne.Calculate(input); 
    Console.WriteLine(newResult); // results: 1 3 4 4 7 9
}

Demo

【讨论】:

  • 我相信 OP 正在寻找一个可以做到这一点的 Enumerable。
【解决方案2】:

我的回答与@Julian 所做的非常相似。但是,我已经编写了我的解决方案,我认为它可以更好地提供 OP 问题的两个方面:

  • 如何对数据序列执行此操作(如最初显示的那样)
  • 如何对一次一个数据使用相同的功能

你不能用泛型轻松做到这一点,没有办法将一个类型限制为一个包含加法的类型(如果你知道一个,现在告诉我)。所以这就是转换类的样子:

public class AccumulateTransformer
{
    private int currentState = 0;

    public void Clear()
    {
        currentState = default(int);
    }

    public int NextValue(int input)
    {
        currentState += input;
        return currentState;
    }

    public int Current => currentState;

    public IEnumerable<int> TransformSequence(IEnumerable<int> inputs, bool clearFirst = true)
    {
        if (clearFirst)
        {
            Clear();
        }

        foreach (var value in inputs)
        {
            yield return NextValue(value);
        }
    }
}

这里有一些代码来测试它(使用你的数据):

 var transformer = new AccumulateTransformer();
 var inputs = new[] {1, 2, 1, 0, 3, 2,};
 var results = transformer.TransformSequence(inputs);
 Debug.WriteLine(string.Join(", ", from result in results select result.ToString() ));

 transformer.Clear();
 Debug.WriteLine(transformer.NextValue(1));
 Debug.WriteLine(transformer.NextValue(2));
 Debug.WriteLine(transformer.NextValue(1));
 Debug.WriteLine(transformer.NextValue(0));
 Debug.WriteLine(transformer.NextValue(3));
 Debug.WriteLine(transformer.NextValue(2));

输出如下:

1、3、4、4、7、9
1
3
4
4
7
9

要使这个通用化,您需要一种不依赖于 T 理解加法的累加运算方法。为此,请将代码更改为如下所示。

它使 T 上的类型泛型(在本例中为 int),但包含一个构造函数,该构造函数采用 Func&lt;T, T, T&gt; 来进行累加。构建实例时,您提供了一个 Func,它只添加了两个数字。

类代码:

public class AccumulateTransformer<T>
{
    private T _currentState = default(T);

    private Func<T, T, T> _accumulator;

    public AccumulateTransformer(Func<T, T, T> accumulator)
    {
        _accumulator = accumulator;
    }

    public void Clear()
    {
        _currentState = default(T);
    }

    public T NextValue(T input)
    {
        _currentState = _accumulator(_currentState, input);
        return _currentState;
    }

    public T Current => _currentState;

    public IEnumerable<T> TransformSequence(IEnumerable<T> inputs, bool clearFirst = true)
    {
        if (clearFirst)
        {
            Clear();
        }

        foreach (var value in inputs)
        {
            yield return NextValue(value);
        }
    }

使用 this 的唯一区别是您必须在构造函数中提供累积 Func:

 var transformer = new AccumulateTransformer<int>((a, b) => a + b);

否则,它的使用方式相同并产生相同的输出。

【讨论】:

  • 我很确定你只是写了一个有点混淆的 LINQ 的 Aggregate 版本。
  • 是的,差不多。它没有那么模糊,是吗?但是,OP 正在寻求一种方法来处理数据序列(一次全部复制,以复制他现有的代码)并进行 onsie 工作。我认为展示它是如何在幕后工作的将是有益的。
【解决方案3】:

我不确定这是否正是您所追求的,但您可以简单地保留类中项目的列表,并提供公共方法来向其中添加新项目:

public class Transformer
{
    private readonly List<int> items = new List<int>();
    public IEnumerable<int> Items => items.ToList();
    public void Add(int item) => items.Add(items.LastOrDefault() + item);

    public void Add(IEnumerable<int> input)
    {
        foreach (var item in input) Add(item);
    }

    public override string ToString()
    {
        return string.Join(", ", Items);
    }
}

在使用中它可能看起来像:

public static void Main(string[] args)
{
    var trans = new Transformer();
    var items = new[] { 1, 2, 1, 0, 3 };

    // Add a bunch of inputs at once:
    trans.Add(items);
    // Display results
    Console.WriteLine($"After adding 5 items: {trans}");
    // Add a single item
    trans.Add(2);
    // Display results
    Console.WriteLine($"After adding 1 more item: {trans}");

    GetKeyFromUser("\nDone! Press any key to exit...");
}

输出

【讨论】:

    【解决方案4】:

    一个简单的 Linq.Select() 可以做到这一点:

    int sum = 0;
    var result = input.Select(x => { sum += x; return sum; });
    

    Live Demo

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-08-03
    • 1970-01-01
    • 2019-05-01
    • 1970-01-01
    • 2012-10-17
    • 1970-01-01
    • 1970-01-01
    • 2013-10-04
    相关资源
    最近更新 更多