Как сделать деревья экспрессии на сложных типах данных - PullRequest
2 голосов
/ 25 июля 2011

Я должен признать, что я абсолютно новичок в деревьях выражений в C #, но в настоящее время есть необходимость привыкнуть к нему. Моя проблема в том, что у меня есть два типа данных, которые содержат массив массивов. По этой причине я создал интерфейс, который называется IArray <>

    /// <summary>
    /// Interface for all classes that provide a list of IArrayData
    /// </summary>
    public interface IArray<ElementDataType>
        where ElementDataType : struct
    {
        /// <summary>
        /// Returns a single data out fo the array container.
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        IArrayData<ElementDataType> GetElementData(int index);

        /// <summary>
        /// Returns the amount of ArrayData.
        /// </summary>
        int Count
        {
            get;
        }

        /// <summary>
        /// Returns the size of a single dataset in number of elements
        /// (not in bytes!).
        /// </summary>
        /// <returns></returns>
        int GetDataSize();

        /// <summary>
        /// Creates a copy of the array data provider, by copying the metainformation, but not the
        /// contents.
        /// </summary>
        /// <returns></returns>
        IArray<ElementDataType> CloneStub();
    }

IArrayData определяется следующим образом:

   /// <summary>
    /// DataProvider that provides the internal data as an array.
    /// </summary>
    /// <typeparam name="NativeDataType"></typeparam>
    public interface IArrayData<NativeDataType> where NativeDataType : struct
    {
        /// <summary>
        /// Returns the data in an arbitrary format.
        /// The implementor must take care of an appropriate cast.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        T[] GetData<T>() where T : struct;

        /// <summary>
        /// Returns the data as float[].
        /// </summary>
        /// <returns></returns>
        float[] GetData();

        /// <summary>
        /// Sets the data in an arbitrary format.
        /// The implementor must take care of an appropriate cast.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="data_in"></param>
        void SetData<T>(T[] data_in) where T : struct;

        /// <summary>
        /// Sets the data as float[].
        /// </summary>
        /// <param name="data_in"></param>
        void SetData(float[] data_in);
    }

Существует два типа, которые реализуют интерфейс. На большинстве данных я должен выполнить математические операции над данными. В настоящее время я создал собственный оценщик выражений, но я бы хотел использовать деревья выражений, потому что мне кажется, что он более гибкий. Как мне реализовать математическую операцию типа +, - на данном интерфейсе?

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

public abstract class VoxelVolume : IArray<float>
{
}

Предположим, у меня есть 3 VoxelVolumes A, B и C. Теперь я хочу выполнить операцию:

VoxelVolume D = (A + B) * C;

В настоящее время я делаю это с перегрузкой операторов, и она работает довольно хорошо. Единственная проблема заключается в том, что выражение оценивается операция за операцией, и чем длиннее выражение, тем больше времени и памяти оно занимает. Я бы предпочел объединить операцию в один шаг. Это то, что моя текущая реализация делает

public static IArray<float> AddMul(IArray<float> A, IArray<float> B, IArray<float> C)
{
    IArray<float> Result = A.CloneStub();

    int L = A.Count;
    int N = A.GetDataSize();

    for (int l = 0; l < L; l++)
    {
        float[] a = A.GetElementData(l).GetData();
        float[] b = B.GetElementData(l).GetData();
        float[] c = C.GetElementData(l).GetData();
        float[] d = new float[a.Length];

        for (int n = 0; n < N; n++)
        {
            d[n] = (a[n] + b[n]) * c[n];
        }

        Result.GetElementData(l).SetData(d);
    }

    return Result;
}

Но, как вы можете заметить, мне приходится много печатать для всех различных операций +, -, *, / и многое другое. По этой причине я хотел бы иметь более общий и гибкий способ выполнения этих операций.

Спасибо Martin

1 Ответ

2 голосов
/ 25 июля 2011

В вашем примере я предполагаю, что было бы разумно выполнить вызовы GetData() в вашем стандартном коде, хотя, поскольку мы не можем знать "глубину", мы можем упростить до зубчатого массива (прямоугольный массив будет работать, но труднее работать на всех этапах, так что давайте не будем этого делать). Итак, представьте, что у нас есть неровный массив вместо a, b, c (хотя мы предположим, что ответы d достаточно просты). Таким образом, нам нужно построить:

= ((jagged[0])[n] + (jagged[1])[n]) * (jagged[2])[n]

то есть как дерево:

= *( +((jagged[0])[n], (jagged[1])[n]), (jagged[2])[n])

Таким образом, мы можем построить выражение (и оценить), используя:

var data = Expression.Parameter(typeof (float[][]));
var n = Expression.Parameter(typeof (int));

var body = 
    Expression.Multiply( // *
        Expression.Add( // +
            Expression.ArrayIndex(
                Expression.ArrayIndex(data, Expression.Constant(0)), n), // a[n]
            Expression.ArrayIndex(
                Expression.ArrayIndex(data, Expression.Constant(1)), n) // b[n]
            ),
        Expression.ArrayIndex(
            Expression.ArrayIndex(data, Expression.Constant(2)), n)  // c[n]
    );
var func = Expression.Lambda<Func<float[][], int, float>>(body, data, n).Compile();


// here, a===jagged[0], b===jagged[1], c===jagged[2]
float[][] jagged = new[] { new[] { 1F, 2F }, new[] { 3F, 4F }, new[] { 5F, 6F } };
for(int i = 0; i < 2; i++)
{
    Console.WriteLine("{0}: {1}", i, func(jagged, i));
}

Очевидно, ваш фактический код должен анализировать ваше входное выражение и гибко строить подобное дерево; но этого должно быть достаточно, чтобы проиллюстрировать общий подход.

...