有没有更好的方法来创建多维强类型数据结构?

Mar*_*son 11 c# f# data-structures

我需要一个多维数据结构,其中每个维度都是一个在设计时已知的小列表.

在我的程序的不同位置,我希望能够以强类型的方式访问不同维度的"切片"数据.

我在下面放了一些示例代码,用于使用嵌套接口的2D示例,但我想它会在3D或4D中变得非常可怕.正如@kvb所确定的那样,所需的样板代码将呈指数级增长.

有人有更好的建议吗?我的意思是,保持代码简单/简单/易于理解,同时仍保留按以下方式执行操作的能力:

Data a = new Data(...)
...
SomeMethodThatOnlyCaresAboutRedThings(a.Red) // takes a IBySize<T>
...
SomeMethodThatOnlyCaresAboutBigThings(a.Big) // takes a IByColour<T>
...
Run Code Online (Sandbox Code Playgroud)

这避免了那些必须知道与它们无关的数据结构部分的方法,从而使它们更容易测试.

我在这里纯粹使用颜色/尺寸作为一个例子,为无意中误导任何人认为这些选择有意义而道歉.T可以是一个简单的数据项,如浮点数或其他一些简单的数据结构.

标记为F#和C#,因为我对任何一种解决方案感到满意.

public interface IByColour<T>
{
    T Green { get; }
    T Red { get; }
    T Blue { get; }
}

public interface IBySize<T>
{
    T Small { get; }
    T Big { get; }
}

internal class ByColour<T> : IByColour<T>
{
    public T Green { get; private set; }
    public T Red { get; private set; }
    public T Blue { get; private set; }

    internal ByColour(T green, T red, T blue)
    {
        Green = green;
        Red = red;
        Blue = blue;
    }
}

internal class BySize<T> : IBySize<T>
{
    public T Small { get; private set; }
    public T Big { get; private set; }

    internal BySize(T small, T big)
    {
        Small = small;
        Big = big;
    }
}

public class Data<T> : IByColour<IBySize<T>>, IBySize<IByColour<T>>
{
    public IBySize<T> Green { get; private set; }
    public IBySize<T> Red { get; private set; }
    public IBySize<T> Blue { get; private set; }

    public IByColour<T> Small { get; private set; }
    public IByColour<T> Big { get; private set; }

    public Data(IBySize<T> green, IBySize<T> red, IBySize<T> blue)
    {
        Green = green;
        Red = red;
        Blue = blue;

        Small = new ByColour<T>(Green.Small, Red.Small, Blue.Small);
        Big = new ByColour<T>(Green.Big, Red.Big, Blue.Big);
    }
}
Run Code Online (Sandbox Code Playgroud)

编辑:澄清我的意思是"更好",我的解决方案所具有的理想属性,并解释我是如何使用它的.

Jos*_*nig 5

这听起来像是一个很好的老式的好用DataTable.然后,您可以根据需要使用Linq进行切片和切块,并且由所选列的不同组合创建的任何唯一类型都由编译器自动生成.a中的所有列DataTable都是强类型的,对它们的查询结果也是如此.此外,DataColumnsin中DataTable可以包含任何类型,包括复杂对象或您拥有的枚举类型.

如果你想坚持用更mathy /不变/ F#的做事方式,你可以使用数组或ListTuple<Type1, Type2, .. TypeN>,这基本上是同样的事情作为DataTable反正.

如果你给出了一些关于你建模的背景知识,我可以提供一个例子.我不确定你发布的代码是代表衣服,图像(RGB色彩空间)还是完全不同的东西.

[一小时后]嗯,OP没有更新,所以我将继续我使用的例子,List<Tuple<x, y, ..n>>并假设对象是服装项目.

// Some enums
public enum Size { Small, Medium, Large }
public enum Color { Red, Green, Blue, Purple, Brown }
public enum Segment { Men, Women, Boys, Girls, Infants }

// Fetches the actual list of items, where the object
// item is the actual shirt, sock, shoe or whatever object
static List<Tuple<Size, Color, Segment, object>> GetAllItems() {
    return new List<Tuple<Size, Color, Segment, object>> {
        Tuple.Create(Size.Small, Color.Red, Segment.Boys, (object)new { Name="I'm a sock! Just one sock." }),
        Tuple.Create(Size.Large, Color.Blue, Segment.Infants, (object)new { Name="Baby hat, so cute." }),
        Tuple.Create(Size.Large, Color.Green, Segment.Women, (object)new { Name="High heels. In GREEN." }),
    };
}

static void test() {
    var allItems = GetAllItems();

    // Lazy (non-materialized) definition of a "slice" of everything that's Small
    var smallQuery = allItems.Where(x => x.Item1 == Size.Small);

    // Lazy map where the key is the size and the value is 
    // an IEnumerable of all items that are of that size
    var sizeLookup = allItems.ToLookup(x => x.Item1, x => x);

    // Materialize the map as a dictionary the key is the size and the 
    // value is a list of all items that are of that size
    var sizeMap = sizeLookup.ToDictionary(x => x.Key, x => x.ToList());

    // Proof:
    foreach (var size in sizeMap.Keys) {
        var list = sizeMap[size];
        Console.WriteLine("Size {0}:", size);
        foreach (var item in list) {
            Console.WriteLine("  Item: {{ Size={0}, Color={1}, Segment={2}, value={3} }}",
                item.Item1, item.Item2, item.Item3, item.Item4);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)