2013-08-12 2 views
3

일반 콜렉션 클래스 템플릿에서 작업하고 있습니다. List(T) 어디에서 PHP의 후기 정적 바인딩과 같은 작업을 수행 할 수 있습니다. 단순화 된 샘플 코드로 가장 잘 설명 될 수 있습니다. 이 코드는 dmd에서와 같이 정상적으로 컴파일되지만 원하는 방식으로 변경해야합니다.후기 정적 바인딩 d

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 = "[email protected]") 
    { 
     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.selectPeopleList 대신 selectByGender의 주석 선언이 올바른지 그래서 List!Person의 인스턴스를 반환하는지 확인하고 가겠어요 어떻게

?

구현 내에서 Object.factory(this.classinfo.name)을 조롱하고 실제로 result에 대한 올바른 유형의 인스턴스를 얻을 수는 있지만 선언 된 반환 유형에 도움이되지 않을 것이라고 생각합니다.

가능한 체인 메서드를 만들고 싶습니다 그래서 어떤 하위 클래스 호출 인스턴스를 반환하도록 컴파일러가 필요합니다 List(T).select 중첩 된 템플릿으로 수행 할 수 있지만 상상할 수 없을 것이라고 상상해 봅니다. 컴파일할만한 것이 무엇인지 생각해보십시오.

내가 실제 생활에서 std.algorithmfilter의 알고받은 의견을 회신

추가 정보; 이 코드는 실제 유스 케이스를 나타내지는 않지만 D와 그 템플릿의 능력/한계에 대해 더 많이 배우려는 생각의 실험입니다.

+0

일명 자바의 클론과 비슷한 것을 원한다. 현재 (런타임) 유형으로 새 인스턴스를 만듭니다. –

+0

기술적으로 "정적 인"정적 바인딩이 아니라는 것을 알고 있습니다.하지만 나중에 복제하는 것은 아닙니다. 난 그냥 슈퍼 클래스 메서드에서 하위 클래스 형식을 반환하려면, 어디 수퍼 클래스 정말 하위 형식에 대해 알고 싶지 않아요. – Kris

+1

어쩌면 당신은 C++ STL 접근법을 사용하고 컨테이너에 상속 대신 컴포지션을 적용해야 할 것입니다. 이것은 당신의 문제를 해결할 것입니다. 또는 상속 가능한 컨테이너가 필요한 좋은 이유가 있습니까? – RedX

답변

5

당신은 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 = "[email protected]") 
    { 
     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); 
    } 
} 
+0

이제 멋진 소식이 있습니다. – Kris

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) 코드는 중복됩니다. 당신은 당신의 사람과 배열 (또는 Person의 다른 range)를 만든 다음 할 수있는 :

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

과 함께 할 수. 이 스타일은 함수 프로그래밍, 파이프 및 필터 또는 D 커뮤니티 내의 구성 요소 프로그래밍 (필수 요소)과 유사합니다. 또한 filter 등은 새 List를 할당하지 않고 원래 배열에서 즉석에서 또는 느리게 또는 스트리밍하여 결과를 생성합니다. std.array에서 array을 사용하여 새 버퍼에 결과를 저장하도록 강제 실행할 수 있습니다.

이것은 상속 계층에서 생각하게하는 것이 가장 우아한 방법이 아닌 기본적인 경우 중 하나입니다. 다음 페이지에서 몇 가지 예제 코드입니다 http://dlang.org/template.html#TemplateThisParameter

에 설명 된대로

+1

d와 가능한 제한 사항을 찾으려고합니다. 특히 아무 것도하지 않습니다. 그냥 여기서 가르쳐 줘. – Kris

관련 문제