Отложенные вычисления в C++

Каков правильный способ C # для представления структуры данных ...

Remeber: «Все модели ошибочны, но некоторые из них полезны». - George EP Box

Нет «правильного пути», только полезный.

Выберите тот, который вам полезен и / ваши пользователи , Вот и все. Развивайте экономически, не переучивайте. Чем меньше кода вы пишете, тем меньше кода вам нужно будет отлаживать. (читайте следующие выпуски).

- Отредактировано

. Мой лучший ответ был бы ... это зависит. Наследование из списка приведет к тому, что клиенты этого класса будут подвергнуты методам, которые могут не отображаться, прежде всего потому, что FootballTeam выглядит как бизнес-объект.

- Издание 2

Я искренне не помню, что я имел в виду в комментарии «не переучивайтесь». Хотя я считаю, что мышление KISS является хорошим ориентиром, я хочу подчеркнуть, что наследование бизнес-класса из списка создаст больше проблем, чем устраняет, из-за утечки абстракции .

С другой стороны, я считаю, что существует ограниченное число случаев, когда просто наследовать из Списка полезно. Как я писал в предыдущем издании, все зависит. Ответ на каждый случай сильно зависит от знания, опыта и личных предпочтений.

Спасибо @kai за то, что он помог мне более точно рассказать о ответе.

58
задан nicael 21 May 2014 в 21:43
поделиться

7 ответов

я задаюсь вопросом, возможно ли реализовать отложенные вычисления в C++ разумным способом. Если да, как Вы сделали бы это?

Да, это возможно и довольно часто сделанное, например, для матричных вычислений. Основной механизм для упрощения этого является перегрузкой оператора. Рассмотрите случай матричного дополнения. Подпись функции обычно выглядела бы примерно так:

matrix operator +(matrix const& a, matrix const& b);

Теперь, для создания этой функции ленивой достаточно возвратить прокси вместо фактического результата:

struct matrix_add;

matrix_add operator +(matrix const& a, matrix const& b) {
    return matrix_add(a, b);
}

Теперь все, что должно быть сделано, должно записать этот прокси:

struct matrix_add {
    matrix_add(matrix const& a, matrix const& b) : a(a), b(b) { }

    operator matrix() const {
        matrix result;
        // Do the addition.
        return result;
    }
private:
    matrix const& a, b;
};

волшебство находится в методе operator matrix(), который является неявным оператором преобразования от matrix_add до плоскости matrix. Таким образом, можно объединить несколько операций в цепочку (путем обеспечения соответствующих перегрузок, конечно). Оценка происходит только, когда конечный результат присвоен matrix экземпляр.

РЕДАКТИРОВАНИЕ я должен был быть более явным. Как это, код не имеет никакого смысла, потому что, хотя оценка происходит лениво, это все еще происходит в том же выражении. В частности, другое дополнение оценит этот код, если matrix_add структура не будет изменена для разрешения последовательного сложения. C++ 0x значительно упрощает это путем разрешения variadic шаблонов (т.е. шаблонные списки переменной длины).

Однако один очень простой случай, где этот код на самом деле обладал бы реальным, прямым преимуществом, следующий:

int value = (A + B)(2, 3);

Здесь, предполагается, что A и B двумерные матрицы и что разыменование сделано в нотации Фортрана, т.е. вышеупомянутое вычисляет одно элемент из матричной суммы. Конечно, расточительно добавить целые матрицы. matrix_add к спасению:

struct matrix_add {
    // … yadda, yadda, yadda …

    int operator ()(unsigned int x, unsigned int y) {
        // Calculate *just one* element:
        return a(x, y) + b(x, y);
    }
};

за Другими примерами недалеко ходить. Я только что помнил, что реализовал что-то связанное недавно. В основном я должен был реализовать строковый класс, который должен придерживаться фиксированного, предопределенного интерфейса. Однако мой конкретный строковый класс имел дело с огромными строками, которые не были на самом деле сохранены в памяти. Обычно, пользователь просто получил бы доступ к маленьким подстрокам от исходной строки с помощью функции infix. Я перегрузил эту функцию для своего строкового типа для возврата прокси, который содержал ссылку на мою строку, наряду с желаемым запуском и конечным положением. Только то, когда эта подстрока на самом деле использовалась, сделало она запрашивает API C для получения этой части строки.

91
ответ дан Craig M. Brandenburg 24 November 2019 в 18:40
поделиться

Повышение. Лямбда очень хороша, но Повышение. Первичный точно , что Вы ищете. Это уже имеет перегрузки весь операторы C++, которые по умолчанию выполняют их обычную функцию, когда proto::eval() назван, но может быть изменен.

32
ответ дан j_random_hacker 24 November 2019 в 18:40
поделиться

То, что уже объяснил Konrad, может быть помещено далее для поддержки вложенных вызовов операторов, все выполняемые лениво. В примере Konrad у него есть объект выражения, который может сохранить точно два аргумента точно для двух операндов одной операции. Проблема состоит в том, что это только выполнится одно подвыражение лениво, которое приятно объясняет понятие в отложенных вычислениях, помещенных простыми словами, но не улучшает производительность существенно. Другой пример показывает также хорошо, как можно применяться operator() для добавления только некоторых элементов с помощью того объекта выражения. Но оценить произвольные сложные выражения, нам нужен некоторый механизм, который может хранилище структура этого также. Мы не можем обойти шаблоны, чтобы сделать это. И название этого expression templates. Идея состоит в том, что один шаблонный объект выражения может сохранить структуру некоторого произвольного подвыражения рекурсивно, как дерево, где операции являются узлами, и операнды являются дочерними узлами. Для очень хорошее объяснение я просто нашел сегодня (спустя несколько дней после того, как я записал ниже кода), см. здесь .

template<typename Lhs, typename Rhs>
struct AddOp {
    Lhs const& lhs;
    Rhs const& rhs;

    AddOp(Lhs const& lhs, Rhs const& rhs):lhs(lhs), rhs(rhs) {
        // empty body
    }

    Lhs const& get_lhs() const { return lhs; }
    Rhs const& get_rhs() const { return rhs; }
};

, Который сохранит любую операцию сложения, даже вложенную один, как видно по следующему определению оператора + для простого типа точки:

struct Point { int x, y; };

// add expression template with point at the right
template<typename Lhs, typename Rhs> AddOp<AddOp<Lhs, Rhs>, Point> 
operator+(AddOp<Lhs, Rhs> const& lhs, Point const& p) {
    return AddOp<AddOp<Lhs, Rhs>, Point>(lhs, p);
} 

// add expression template with point at the left
template<typename Lhs, typename Rhs> AddOp< Point, AddOp<Lhs, Rhs> > 
operator+(Point const& p, AddOp<Lhs, Rhs> const& rhs) {
    return AddOp< Point, AddOp<Lhs, Rhs> >(p, rhs);
}

// add two points, yield a expression template    
AddOp< Point, Point > 
operator+(Point const& lhs, Point const& rhs) {
    return AddOp<Point, Point>(lhs, rhs);
}

Теперь, если Вы имеете

Point p1 = { 1, 2 }, p2 = { 3, 4 }, p3 = { 5, 6 };
p1 + (p2 + p3); // returns AddOp< Point, AddOp<Point, Point> >

, теперь просто необходимо перегрузить оператор = и добавить, что подходящий конструктор для Точки вводит и принимает AddOp. Измените его определение:

struct Point { 
    int x, y; 

    Point(int x = 0, int y = 0):x(x), y(y) { }

    template<typename Lhs, typename Rhs>
    Point(AddOp<Lhs, Rhs> const& op) {
        x = op.get_x();
        y = op.get_y();
    }

    template<typename Lhs, typename Rhs>
    Point& operator=(AddOp<Lhs, Rhs> const& op) {
        x = op.get_x();
        y = op.get_y();
        return *this;
    }

    int get_x() const { return x; }
    int get_y() const { return y; }
};

И добавляют соответствующий get_x и get_y в AddOp как функции членства:

int get_x() const {
    return lhs.get_x() + rhs.get_x();
}

int get_y() const {
    return lhs.get_y() + rhs.get_y();
}

Примечание, как мы не создали временных файлов Точки типа. Это, возможно, была большая матрица со многими полями. Но в то время, когда результат необходим, мы вычисляем его лениво .

27
ответ дан Giel 24 November 2019 в 18:40
поделиться

У меня нет ничего для добавления к сообщению Konrad, но можно посмотреть Собственный для примера сделанных правильно отложенных вычислений в приложении реального мира. Это - симпатичное воодушевление страха.

10
ответ дан 24 November 2019 в 18:40
поделиться

C++ 0x хорош и все...., но для тех из нас живущий в подарке у Вас есть библиотека лямбды Повышения и Повышение Финикс. Оба с намерением обеспечения больших объемов функционального программирования к C++.

2
ответ дан Hippiehunter 24 November 2019 в 18:40
поделиться

Что-либо возможно.

Это зависит от точно, что Вы имеете в виду:

class X
{
     public: static X& getObjectA()
     {
          static X instanceA;

          return instanceA;
     }
};

Здесь у нас есть влияние глобальной переменной, которая лениво оценена при первом использовании.

, Как недавно требуется в вопросе.
И крадущий дизайн Konrad Rudolph и расширение его.

Ленивый объект:

template<typename O,typename T1,typename T2>
struct Lazy
{
    Lazy(T1 const& l,T2 const& r)
        :lhs(l),rhs(r) {}

    typedef typename O::Result  Result;
    operator Result() const
    {
        O   op;
        return op(lhs,rhs);
    }
    private:
        T1 const&   lhs;
        T2 const&   rhs;
};

, Как использовать его:

namespace M
{
    class Matrix
    {
    };
    struct MatrixAdd
    {
        typedef Matrix  Result;
        Result operator()(Matrix const& lhs,Matrix const& rhs) const
        {
            Result  r;
            return r;
        }
    };
    struct MatrixSub
    {
        typedef Matrix  Result;
        Result operator()(Matrix const& lhs,Matrix const& rhs) const
        {
            Result  r;
            return r;
        }
    };
    template<typename T1,typename T2>
    Lazy<MatrixAdd,T1,T2> operator+(T1 const& lhs,T2 const& rhs)
    {
        return Lazy<MatrixAdd,T1,T2>(lhs,rhs);
    }
    template<typename T1,typename T2>
    Lazy<MatrixSub,T1,T2> operator-(T1 const& lhs,T2 const& rhs)
    {
        return Lazy<MatrixSub,T1,T2>(lhs,rhs);
    }
}
2
ответ дан Ajay 24 November 2019 в 18:40
поделиться

Поскольку это будет сделанным в C++ 0x лямбда-выражениями.

1
ответ дан 2 revs 24 November 2019 в 18:40
поделиться
Другие вопросы по тегам:

Похожие вопросы: