構造体およびクラスを使用したforeach

  1. ステップサイズは、beginおよびendとともに格納する必要があり、要素の値は、そのステップサイズだけ増加させる必要がある。
    struct NumberRange {
        int begin;
        int end;
        int stepSize;
    
        int opApply(int delegate(ref int) dg) const {
            int result;
    
            for (int number = begin; number != end; number += stepSize) {
                result = dg(number);
    
                if (result) {
                    break;
                }
            }
    
            return result;
        }
    }
    
    import std.stdio;
    
    void main() {
        foreach (element; NumberRange(0, 10, 2)) {
            write(element, ' ');
        }
    }
    D
    foreach_opapply.solution.1
  2. import std.stdio;
    import std.string;
    
    class Student {
        string name;
        int id;
    
        this(string name, int id) {
            this.name = name;
            this.id = id;
        }
    
        override string toString() {
            return format("%s(%s)", name, id);
        }
    }
    
    class Teacher {
        string name;
        string subject;
    
        this(string name, string subject) {
            this.name = name;
            this.subject = subject;
        }
    
        override string toString() {
            return format("%s teacher %s", subject, name);
        }
    }
    
    class School {
    private:
    
        Student[] students;
        Teacher[] teachers;
    
    public:
    
        this(Student[] students, Teacher[] teachers) {
            this.students = students;
            this.teachers = teachers;
        }
    
        /* このopApplyのオーバーライドは、foreach変数が
         * Studentの場合に呼び出される。 */
        int opApply(int delegate(ref Student) dg) {
            int result;
    
            foreach (student; students) {
                result = dg(student);
    
                if (result) {
                    break;
                }
            }
    
            return result;
        }
    
        /* 同様に、このopApplyは、foreach変数が
         * Teacherの場合に呼び出される。 */
        int opApply(int delegate(ref Teacher) dg) {
            int result;
    
            foreach (teacher; teachers) {
                result = dg(teacher);
    
                if (result) {
                    break;
                }
            }
    
            return result;
        }
    }
    
    void printIndented(T)(T value) {
        writeln("  ", value);
    }
    
    void main() {
        auto school = new School(
            [ new Student("Can", 1),
              new Student("Canan", 10),
              new Student("Cem", 42),
              new Student("Cemile", 100) ],
    
            [ new Teacher("Nazmiye", "Math"),
              new Teacher("Makbule", "Literature") ]);
    
        writeln("Student loop");
        foreach (Student student; school) {
            printIndented(student);
        }
    
        writeln("Teacher loop");
        foreach (Teacher teacher; school) {
            printIndented(teacher);
        }
    }
    D
    foreach_opapply.solution.2

    出力:

    生徒ループ
      Can(1)
      Canan(10)
      Cem(42)
      Cemile(100)
    教師のループ
      数学教師 Nazmiye
      文学教師 Makbule

    ご覧のとおり、opApply()の2つのオーバーライドの実装は、反復処理の対象となるスライスを除いてまったく同じである。コードの重複を減らすために、共通の機能を実装関数テンプレートに移動し、2つのopApply()オーバーライドから呼び出すようにすることができる。

    class School {
    // ...
    
        int opApplyImpl(T)(T[] slice, int delegate(ref T) dg) {
            int result;
    
            foreach (element; slice) {
                result = dg(element);
    
                if (result) {
                    break;
                }
            }
    
            return result;
        }
    
        int opApply(int delegate(ref Student) dg) {
            return opApplyImpl(students, dg);
        }
    
        int opApply(int delegate(ref Teacher) dg) {
            return opApplyImpl(teachers, dg);
        }
    }
    D