Pull to refresh

Comments 34

ЗдОрово. А еще можно переписать без лямбд, тогда код будет работать на компиляторах без поддержки C++11. Вот на примере оператора сложения:

func_t operator+ (const func_t &arg, const func_t &arg2)
{
    struct _sum
    {
        _sum(const func_t &arg, const func_t &arg2)
            : _arg(arg), _arg2(arg2){}

        double operator()(double x)
        {
            return (*_arg.f)(x) + (*_arg2.f)(x);
        }

        const func_t& _arg;
        const func_t& _arg2;
    };

    realfunc realf = _sum(arg, arg2);
    return func_t(realf);
}

Хотя, коряво все это выглядит без лямбд.
Долой переменные!
Виват Функциональное!
Виват Бесточечное!

*(еще вот такое есть — похоже на ваше)
А статический список, если я правильно понял, используется только для вечного хранения realfunc объектов, он всегда растет при конструировании func_t и очищается только по завершении программы? Я бы посмотрел в сторону shared_ptr и хранил бы его вместо realfunc *f, если нет циклических ссылок. Список был бы не нужен.
shared_ptr<realfunc> f;
Угловые скобки съелись.
Да, это тот факт, с которым я смирился перед тем, как начать писать. Можно, конечно, создавать внешние классы для «цепочек» составления функций. Т.е., я решаю в программе какую-то задачу в созданном специально под эту задачу объекте, а потом удаляю его.
Выглядело бы это примерно так:
func_chain T1();
T1::func_t f = ...
...

Но всё равно со статической коллекцией, пусть не одной.

К сожалению, я не имею ни малейшего представления о shared_ptr.
Самое время заиметь.
Я конечно польщён, что моя статья про лямбды натолкнула Вас на такую идею, однако, интуиция мне подсказывает, что можно было всё это сделать проще.

Я правильно понял, что Вы создаёте статическое хранилище функций, которое заполняете в рантайме? о_О Причём, коллекция только заполняется — из неё ничего не удаляется.

Значит, если я создам в своей программе 500 функций, то там будет как минимум 500 объектов (на самом деле больше, т.к. при композиции создаются дополнительные объекты func_t), причём удалятся они только при завершении работы программы.

Как минимум, неплохо бы заменить std::vector<> на std::list<>, а в класс добавить счётчик ссылок, чтобы ненужные функции оттуда удалялись, когда они становятся ненужными.

А вообще, не могли бы Вы ещё раз объяснить (желательно на примере кода) вот этот абзац, ибо мне кажется, я чего-то не уловил:
Скажем, мы хотим, чтобы некая процедура принимала пару функций A и B и возвращала новое выражение, например, 5A + B. Наша процедура создаст лямбду 5A, затем создаст 5A + B, используя 5А и В. Полученное процедура вернет и завершится, в этот момент лямбда 5А пропадет из области видимости и возвращенное выражение попросту не будет работать.
Оу, у Вас и так list<>. Это я провтычил, но ничего от этого не меняется.
Мне кажется, если поверхностно смотреть, то не нужен list вообще, если вставить shared_ptr на место
realfunc *f; //указатель на элемент коллекции
в class func_t.

Может, я чего-то не учел.
В целом да, должно работать.
#include <iostream>
#include <xstddef>
#include <functional>

using namespace std;

typedef tr1::function<double(double)> realfunc;

realfunc go(realfunc &A, realfunc &B)
{
	realfunc A5 = [&](double x) {
		return A(x) * 5;
	};
	realfunc A5pB = [&](double x) {
		return A5(x) + B(x);
	};
	return A5pB;
}

int main()
{
	realfunc A = [&](double x) {
		return x;
	};
	realfunc B = [&](double x) {
		return x;
	};
	realfunc A5pB = go(A, B);
	cout << A5pB(3) << endl;
	getchar();
}


Вот тут рантайм. А если go() возвращает A5, то всё в порядке.

=(
#include <cstddef>
#include <functional>
#include <iostream>

using namespace std;

typedef function<double(double)> realfunc;

realfunc go(realfunc A, realfunc B)
{
	return [=](double x) {
		return A(x) * 5 + B(x);
	};
}

int main()
{
	realfunc A = [](double x) {
		return x;
	};
	realfunc B = [](double x) {
		return x;
	};
	auto A5pB = go(A, B);
	cout << A5pB(3) << endl;

	return EXIT_SUCCESS;
}

Ну так вы в фукнции go() создаете единственный объект и его же копию возвращаете. В моем примере проблема была в потере А5.
Сделайте лямбду [=] вместо [&] и всё будет работать.
Да, но идея передавать везде все по копированию мне изначально не понравилась. Например, я создам одну гигантскую функцию и сотню маленьких, использующих ее. В моем случае гигансткая будет в одном экземпляре в коллекции, а в вашем, наверное, не в одном.
Сейчас вы скажете, что я совершенно не понимаю механизма всех этих штук, и я не возражу.
Однозначного ответа я Вам не дам, но не исключаю вариант, что компилятор оптимизирует подобные места за счёт Copy Elision хотя бы.
Попробуйте оба варианта в релизе с максимальной настройкой оптимизации компилятора, сделайте замеры производительности на хорошо нагруженном примере и сравните. Если [=] окажется не менее производительным, чем [&], выбрасывайте список, код упростится.
Размер функции не зависит от размера ее кода, только от того, что она захватывает. И проще сделать так, чтобы гигантская функция захватывала по ссылке нужные данные (и shared_ptr лучше static), а не ее саму захватывать по ссылке.
Зато в случае захвата = будет рядом храниться все что надо и это будет быстрее. Память дешевая, дорого обходятся кэш-промахи и локальность данных важнее.
А лучший подход — boost phoenix, если динамичность не нужна
Вот если бы оно умело какие-нибудь простейшие преобразования типа a — a = 0, a + 0 = a, a * (b + c) = a * c + b * c. Коли речь зашла о производных, то было бы полезно выводить производные элементарных функций, а не пытаться их аппроксимировать, да еще и не очень точно.
Ну, преобразование «а — а => 0» работало бы только если это было бы одно и то же «а». Если это были бы разные экземпляры одной и той же мат. функции, выяснить такое мне не представляется возможным. Как и проверить, является ли произвольная функция нулем, чтобы сократить ее при сложении.
Это крайне похоже на Expression Templates, которые используются давно (лет 15) и замечательно обходятся без лямбд.
Название статьи прочитал как «Динамический мат». Задумался
Мне кажется, вместо указателя удобнее использовать итератор, можно будет тогда и из списка удалять при необходимости. И, как уже говорили, посмотрите в сторону shared_ptr.
Вот конструктор копирования угнетает. Зачем Вы еще одну копию кидаете в список? Можно ведь просто скопировать себе указатель.
В констукторе копирования я как раз просто копирую указатель, не трогая список.
И правда, извиняюсь, перепутал с конструктором от realf.
[quote]
Скажем, мы хотим, чтобы некая процедура принимала пару функций A и B и возвращала новое выражение, например, 5A + B. Наша процедура создаст лямбду 5A, затем создаст 5A + B, используя 5А и В. Полученное процедура вернет и завершится, в этот момент лямбда 5А пропадет из области видимости, и возвращенное выражение попросту не будет работать.
[/quote]

Можно создать лямбду 5A внутри результирующей 5A + B, тогда этой проблемы не будет.
Я даю пользователю класса свободу действий. Пусть делает как хочет, и будет работать.
Да просто скажите прямо — в плюсах не работают замыкания.
По ссылке не работают конечно. И это хорошо.
Всё работает так, как и должно работать.
Математические функции еще отличаются чистотой, а переменные — иммутабельностью. Короче говоря берите любой функциональный язык и наслаждайтесь.
>Ну да, как это использовать.
>Например вот так:
>func_t f1 = cos(5 * func_t() + 8);
>это создаст, как видно, функцию
>f1(x) = cos(5x + 8)

Для тренировки полезно: пока все это заработает, надо достаточно много тонких моментов вспомнить. Только в реальных проектах этим лучше не пользоваться. Лучше:

double f1 (double x) { return cos(5*x + 8); }

=)
Ну так это не динамически же.
Sign up to leave a comment.

Articles