11

各次元が設計時に認識される小さなリストである多次元データ構造が必要です。

私のプログラムのさまざまな場所で、厳密に型指定された方法で、さまざまな次元で「スライス」されたデータにアクセスできるようにしたいと考えています。

ネストされたインターフェイスを使用する 2D の例で機能するサンプル コードを以下にいくつか示しましたが、3D または 4D ではかなり恐ろしいものになると思います。@kvb が特定したように、必要なボイラープレート コードは指数関数的に増加します。

誰かがより良い提案をしていますか? つまり、必要なコードをシンプル/短く/理解しやすいものに保ちながら、次の行に沿って物事を行う能力を維持する必要があります。

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

これにより、これらのメソッドが関連しないデータ構造の部分について知る必要がなくなり、テストが容易になります。

ここで色/サイズを純粋に例として使用しました。これらの選択が意味のあるものであると誤って誤解させたことをお詫びします. T は、float やその他の単純なデータ構造のような単純なデータ項目である可能性があります。

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);
    }
}

編集:「より良い」とはどういう意味か、私のソリューションが持つ望ましい特性を明確にし、それをどのように使用したいかを説明します。

4

3 に答える 3

5

これは、古き良き時代のDataTable. 次に、Linq を使用して必要に応じてスライス アンド ダイスを行うことができます。選択した列のさまざまな組み合わせによって作成された一意の型は、コンパイラによって自動的に生成されます。a のすべての列は、DataTableそれらに対するクエリの結果と同様に、厳密に型指定されています。また、 は複雑なオブジェクトや独自の列挙型を含め、任意の型を持つことができますDataColumnsDataTable

より数学的/不変/F# なやり方に固執したい場合は、 array またはListof を使用できますTuple<Type1, Type2, .. TypeN>。これは基本的にとにかくと同じことDataTableです。

あなたがモデリングしているものについてもう少し背景を説明していただければ、例を提供できます。あなたが投稿したコードが、衣服、画像 (RGB カラー スペース)、またはまったく異なるものを表すものであるかどうかはわかりません。

List<Tuple<x, y, ..n>>[1時間後] さて、OPからの更新はないので、オブジェクトが衣料品であると仮定して使用する例を進めます.

// 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);
        }
    }
}
于 2012-10-18T16:55:06.290 に答える
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);
    }
}

コードの品質については申し訳ありません。アイデアを示すだけです。

于 2012-10-18T19:11:21.137 に答える
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>。レコードタイプにプロパティを追加しても、レコードのパターンマッチングのおかげで、クエリはあまり変更されません。

于 2012-10-18T20:06:25.617 に答える