如何使用条件 windows 而不是基于时间的 windows 将整数流拆分为缓冲区?

How can I split the integer stream into buffers using conditional windows instead of time based windows?

我有一个 returns 整数的 Observable,如下所示:

1, 1, 1, 1, -1, -1, -1, -1, 0, 0, 0, 0, -1, -1, -1, 0, 0, 0

我如何将此 Observable 转换为 return 那些整数数组,而不是按基于时间 windows 而是按基于值的拆分流?

这些整数是Unity更新事件中Touch的fingerId。它对于这项任务并不重要,但为了解释我为什么需要它,我必须提供这些细节。 -1 表示不接触。这就是差距。我需要删除那些 -1 部分并将流拆分为 fingerId 在“无触摸”时刻之间的缓冲区。我也可以这样描述:

Touch0, Touch0, Touch0, no Touch, no Touch, Touch1

无论是整数还是其他类型都没有关系。只需要将流分成缓冲区,删除 «window 值»。

这是我的代码,如果有帮助的话:

var leftSideTouchStream = Observable.EveryUpdate()
            .Scan(-1, (id, _) =>
            {
                if (id < 0)
                {
                    var leftSideTouches = Input.touches
                        .Where(t =>
                            t.phase == TouchPhase.Began
                            && t.position.x < Screen.width / 2
                        );

                    return leftSideTouches.Any() ? leftSideTouches.First().fingerId : -1;

                }
                else
                {
                    var touchEnded = Input.touches
                        .Any(t =>
                            t.fingerId == id &&
                            (t.phase == TouchPhase.Ended || t.phase == TouchPhase.Canceled)
                        );

                    return touchEnded ? -1 : id;
                }
            })
            .Select(id =>
            {
                return Input.touches
                    .Where(t => t.fingerId == id)
                    .Select(t => new Nullable<Touch>(t))
                    .FirstOrDefault();
            });

我需要与 Buffer 函数提供的行为完全相同,但正如我所说的,根据值而不是时间。 如果我有这个流:

1, 1, 1, 1, -1, -1, -1, -1, 0, 0, 0, 0, -1, -1, -1, 0, 0, 0

而«window值»为-1,则结果预计为:

[1, 1, 1, 1], [0, 0, 0, 0], [0, 0, 0]

可能存在一些更好的神奇解决方案,但我想最直接的是

private static int[][] GetArrays(int[] intputArray)
{
    // use lists for dynamically adding elements
    var outputLists = new List<List<int>>();

    // initialize with the ignored value
    var lastValue = -1;

    // iterate over the inputArray
    foreach (var value in intputArray)
    {
        // skip -1 values
        if (value < 0)
        {
            lastValue = -1;
            continue;
        }

        // if a new value begin a new list
        if (lastValue != value)
        {
            outputLists.Add(new List<int>());
        }

        // add the value to the current (= last) list
        outputLists[outputLists.Count - 1].Add(value);

        // update the lastValue
        lastValue = value;
    }

    // convert to arrays
    // you could as well directly return the List<List<int>> instead
    // and access the values exactly the same way
    // but since you speak of buffers I guess you wanted arrays explicitely
    var outputArrays = new int[outputLists.Count][];
    for (var i = 0; i < outputLists.Count; i++)
    {
        outputArrays[i] = outputLists[i].ToArray();
    }

    return outputArrays;
}

所以打电话

var arrays = GetArrays(new int[]{1, 1, 1, 1, -1, -1, -1, -1, 0, 0, 0, 0, -1, -1, -1, 0, 0, 0});

应该导致

arrays[0] => [1, 1, 1, 1]
arrays[1] => [0, 0, 0, 0]
arrays[2] => [0, 0, 0]    

因为你似乎更想动态地一个一个地添加值,所以我根本不会使用数组,而是使用类似

的东西
private List<List<int>> arrays = new List<List<int>>();

private int lastValue;

private void AddValue(int value)
{
    // skip -1 values
    if (value < 0)
    {
        lastValue = -1;
        return;
    }

    // if a new value begin a new list
    if (lastValue != value)
    {
        arrays.Add(new List<int>());
    }

    // add the value to the current (= last) list
    arrays[outputLists.Count - 1].Add(value);

    // update the lastValue
    lastValue = value;
}

我的意思是你必须在某处存放一些东西

不幸的是,我没有找到任何 out-of-the-box 解决方案,所以我想出了自己的 Observable 实现:

using System;
using System.Collections.Generic;
using UniRx;

public static class ObservableFunctions
{
    public static IObservable<T[]> BufferWhen<T>(this IObservable<T> source, Predicate<T> predicate)
    {
        return Observable.Create<T[]>(observer =>
        {
            List<T> buffer = new List<T>();

            source.Subscribe(
                t =>
                {
                    if (predicate(t))
                    {
                        buffer.Add(t);
                    }
                    else
                    {
                        if (buffer.Count > 0)
                        {
                            observer.OnNext(buffer.ToArray());
                            buffer = new List<T>();
                        }
                    }
                },
                e =>
                {
                    observer.OnError(e);
                },
                () =>
                {
                    observer.OnCompleted();
                }
            );

            return Disposable.Empty;
        });
    }
}

幸运的是它非常简单。比在 Google...

中搜索合适的函数更简单