我需要一个多维数据结构,其中每个维度都是一个在设计时已知的小列表。
在我的计划的不同地方,我希望能够以强烈的方式访问不同维度的“切片”数据。
我在下面放了一些示例代码,用于使用嵌套接口的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);
}
}
编辑:澄清我的意思是“更好”,我的解决方案所具有的理想属性,并解释我将如何使用它。
答案 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>
中的属性。由于记录上的模式匹配,向记录类型添加更多属性不会更改查询。