3

私はジェネリック コレクション クラス テンプレートに取り組んでいます。たとえばList(T)、php の遅延静的バインディングのようなことができるようにしたいと考えています。いくつかの単純化されたサンプル コードで最もよく説明されるかもしれません。このコードは dmd でそのままコンパイルできますが、希望どおりにするには少し変更する必要があります。

module main;

import std.stdio;
import std.string;

class List(T)
{
    private T[] _list;

    public void append(T t)
    {
        _list ~= t;
    }

    // this is where some help is needed...
    public List select(bool delegate(T t) dg)
    {
            // auto should be whatever subclass of List(T) is calling this method.
        auto result = new List!T();
        foreach(t; _list)
        {
            if (dg(t)) result.append(t);
        }
        return result;
    }

    int opApply(int delegate(ref T) dg)
    {
        int result = 0;

        for (int i = 0; i < _list.length; i++)
        {
            result = dg(_list[i]);
            if (result)
                break;
        }
        return result;
    }
}

enum Gender
{
    MALE,
    FEMALE,
    SECRET
}

class Person
{
    private string _firstName;
    private string _lastName;
    private string _email;
    private Gender _gender;

    @property public string firstName() {return _firstName;}
    @property public string lastName() {return _lastName;}
    @property public string email() {return _email;}
    @property public Gender gender() {return _gender;}


    public this()
    {
    }

    public this(string firstName, string lastName, Gender gender = Gender.SECRET, string email = "info@example.com")
    {
        this();

        this._firstName = firstName;
        this._lastName = lastName;
        this._gender = gender;
        this._email = email;
    }

    override public string toString()
    {
        if (email.length > 0)
        {
            return "%s %s <%s>".format(firstName, lastName, email);
        }
        else
        {
            return "%s %s".format(firstName, lastName);
        }
    }
}

class PeopleList : List!Person
{
    // I would like to be able to make this: public PeopleList selectByGender(Gender gender)
    public List!Person selectByGender(Gender gender)
    {
        return select(p => p.gender == gender);
    }
}

void main(string[] args)
{
    auto people = new PeopleList();
    people.append(new Person("Kris", "Herlaar", Gender.MALE));
    people.append(new Person("John", "Doe", Gender.MALE));
    people.append(new Person("Steve", "Wozniak", Gender.MALE));
    people.append(new Person("Walter", "Bright", Gender.MALE));
    people.append(new Person("Amelia", "Earhart", Gender.FEMALE, null));
    people.append(new Person("Susan", "Anthony", Gender.FEMALE, null));

    foreach(p; people.selectByGender(Gender.FEMALE))
    {
        writeln(p);
    }
}

コメントアウトされた宣言が正しいように、 a ではなくのPeopleList.selectインスタンスも返すことを確認するにはどうすればよいですか?PeopleListList!PersonselectByGender

おそらくObject.factory(this.classinfo.name)実装内でモックを作成して、実際に正しいタイプのインスタンスを取得できますresultが、宣言された戻り値の型には役立たないと思います。

メソッドの連鎖を可能にしたいので、呼び出しList(T).selectているサブクラスのインスタンスを返すことができるようにするコンパイラが必要です。ネストされたテンプレートを使用して実行できると思いますが、それを実現することはできませんでした。エレガントに見えることは言うまでもなく、コンパイルされます。

受け取ったフィードバックに対する追加情報

私は、実生活std.algorithmでのことを知っています。filterこのコードは実際のユースケースを表すものではなく、D とそのテンプレートの機能/制限をさらに学習するための思考実験です。

4

2 に答える 2

6

これは継承の残念な使い方です。PersonList存在すべきではありません: 決してポリモーフィックではありません。

あなたがしようとしているのは、ヘルパー メソッドを提供することです。つまり、リストから性別で人々を選択します。D には統一関数呼び出し構文と呼ばれるものがあり、最初のパラメーターが実際のthisインスタンスであるかのように自由な関数を呼び出すことができます。したがって、次のようにコードを書き直すことができます。

public List!People selectByGender(List!People list, Gender gender)
{
    return list.select(p => p.gender == gender);
}

void main(string[] args)
{
    auto people = new List!People();

    // ...

    foreach(p; people.selectByGender(Gender.FEMALE))
    {
        writeln(p);
    }
}

std.algorithm をまだ調べたかどうかはわかりません。ただし、基本的にすべてのList(T)コードが冗長になります。人を使って配列 (またはその他rangePerson) を作成し、次のようにすることができます。

foreach (p; people.filter!(p => p.gender == Gender.FEMALE))
{
    writeln(p);
}

そしてそれで終わります。このスタイルは、関数型プログラミング (の本質的な要素)、パイプとフィルター、またはコンポーネント プログラミング (D コミュニティ内) など、好きなように呼びます。またfilter、その他は新しいリストを割り当てませんが、その場で、または遅延して、またはストリーミングで元の配列から結果を生成します。arrayfrom std.arrayを使用して、結果を新しいバッファに保存するように強制できます。

これは、継承階層で考えるように強制することが最もエレガントな方法ではない基本的なケースの 1 つです。

于 2013-08-13T10:35:28.530 に答える