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

时间:2012-10-18 16:44:23

标签: c# data-structures f#

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

在我的计划的不同地方,我希望能够以强烈的方式访问不同维度的“切片”数据。

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

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

Data a = new Data(...)
...
SomeMethodThatOnlyCaresAboutRedThings(a.Red) // takes a IBySize<T>
...
SomeMethodThatOnlyCaresAboutBigThings(a.Big) // takes a IByColour<T>
...

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

我在这里仅使用颜色/尺寸作为例子,为无意中误导任何人认为这些选择有意义而道歉。 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);
    }
}

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

3 个答案:

答案 0 :(得分:5)

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

如果你想坚持使用更多mathy / immutable / F#做事方式,你可以使用List数组Tuple<Type1, Type2, .. TypeN>,这与{{1}基本相同无论如何。

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

[一小时后]嗯,没有来自OP的更新,所以我将继续我使用DataTable并假设对象是服装项目的示例。

List<Tuple<x, y, ..n>>

答案 1 :(得分:2)

您是否考虑过这种方法:

public enum ElementSize
{
    Small,
    Big
}

public enum ElementColor
{
    Green,
    Red,
    Blue
}

public enum Temperature
{
    Hot,
    Cold
}

public class Element<T>
{
    public T Value { get; set; }
    public ElementColor Color { get; set; }
    public Temperature Temperature { get; set; }
    public ElementSize Size { get; set; }
}

public class Data<T>
{
    private readonly IList<Element<T>> list = new List<Element<T>>();

    public T Value
    {
        get
        {
            if ( list.Count == 1 )
                return list[0].Value;
            else
                throw new Exception("Throw a proper exception or consider not implementing this property at all");
        }
    }

    public Data<T> Green
    {
        get { return FilterByColor(ElementColor.Green); }
    }

    public Data<T> Red
    {
        get { return FilterByColor(ElementColor.Red); }
    }

    private Data<T> FilterByColor(ElementColor color)
    {
        return new Data<T>(from x in list where x.Color == color select x);
    }

    //etc...

    public Data<T> Small
    {
        get { return new Data<T>(from x in list where x.Size == ElementSize.Small select x); }
    }

    public Data<T> Cold
    {
        get { return new Data<T>(from x in list where x.Temperature == Temperature.Cold select x); }
    }

    public void Add(Element<T> element)
    {
        list.Add(element);
    }

    public Data(IEnumerable<Element<T>> list)
    {
        this.list = new List<Element<T>>(list);
    }
}

对不起代码质量。这只是为了展示这个想法。

答案 2 :(得分:1)

这是你在F#中可以做的:

/// Use discriminated unions which are safer than enums
type Size = Smal | Big
type Color = Red | Green | Blue

/// Use 'T to demonstrate parameterized records
type Element<'T> = {Value: 'T; Size: Size; Color: Color}

/// Query on a list of elements using pattern matching on records
let getElementsByColor color elements = 
    List.filter (fun {Color = c} -> c = color) elements

let getElementsBySize size elements = 
    List.filter (fun {Size = s} -> s = size) elements

基本上,每个属性都声明为记录类型Element<'T>中的属性。由于记录上的模式匹配,向记录类型添加更多属性不会更改查询。