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)
编辑:澄清我的意思是"更好",我的解决方案所具有的理想属性,并解释我是如何使用它的.
这听起来像是一个很好的老式的好用DataTable
.然后,您可以根据需要使用Linq进行切片和切块,并且由所选列的不同组合创建的任何唯一类型都由编译器自动生成.a中的所有列DataTable
都是强类型的,对它们的查询结果也是如此.此外,DataColumns
in中DataTable
可以包含任何类型,包括复杂对象或您拥有的枚举类型.
如果你想坚持用更mathy /不变/ F#的做事方式,你可以使用数组或List
的Tuple<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)