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

我将如何确保它PeopleList.select也会返回一个实例PeopleList而不是 aList!Person以便注释掉的声明selectByGender是正确的?

我可能可以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,et al 不会分配新的列表,而是动态地、懒惰地或流式地从原始数组中生成结果。array您可以使用std.array强制他们将结果保存在新缓冲区中。

这是强迫自己在继承层次结构中思考并不是最优雅的方法的基本情况之一。

于 2013-08-13T10:35:28.530 回答
5

您可以Template This Parameters按照http://dlang.org/template.html#TemplateThisParameter中的说明使用

这是页面中的一些示例代码。

interface Addable(T) {
    final R add(this R)(T t) {
        return cast(R)this; // cast is necessary, but safe
    }
}

class List(T) : Addable!T {
    List remove(T t) {
        return this;
    }
}

void main() {
    auto list = new List!int;
    list.add(1).remove(1); // ok
}

这是您使用它的代码。

module main;

import std.stdio;
import std.string;

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

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

    // select is now templatized based on the most derived class
    public This select(this This)(bool delegate(T t) dg)
    {
        // auto should be whatever subclass of List(T) is calling this method.
        auto result = new This();
        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
{
    public PeopleList selectByGender(Gender gender)
    {
        return this.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);
    }
}
于 2013-08-14T06:59:02.183 回答