我正在研究一个通用集合类模板,让我们说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
而不是List!Person
的实例,以便selectByGender
的注释声明是正确的?
我可能会在实现中使用Object.factory(this.classinfo.name)
进行模拟,并实际为result
获取正确类型的实例,但我认为这对声明的返回类型没有帮助。
我想让链接方法成为可能,所以我需要编译器允许我返回任何子类调用List(T).select
的实例我想象可以用嵌套模板完成,但是没有能够想出任何可以编译的东西,更不用说看似优雅了。
回复收到的反馈的其他信息
我知道std.algorithm
和filter
,在现实生活中;此代码不代表实际的用例,而是一个思考实验,以了解更多D及其模板的能力/限制。
答案 0 :(得分: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)
代码变得多余。您可以与您的人员一起创建一个数组(或range
的任何其他Person
)然后执行:
foreach (p; people.filter!(p => p.gender == Gender.FEMALE))
{
writeln(p);
}
并完成它。这种风格类似于功能编程,管道和过滤器或组件编程(在D社区内)的基本元素,无论你喜欢什么称呼它。此外filter
等人不会分配新的List,而是在运行中,懒惰地或流式传输中从原始阵列生成结果。您可以使用std.array中的array
强制他们将结果保存到新缓冲区中。
这是强迫自己在继承中思考的基本案例之一,层次结构不是最优雅的方式。
答案 1 :(得分:5)
您可以按照http://dlang.org/template.html#TemplateThisParameter
中的说明使用Template This Parameters
以下是该页面的一些示例代码。
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);
}
}