Можно ли передать арифметическое выражение в качестве аргумента функции для описания логики в ней? - PullRequest
0 голосов
/ 03 мая 2019

Я работаю над визуализацией множества Мандельброта, а также нескольких других фракталов, и в них много дублированного кода, но повторное использование кода отсутствует.

Одна из функций, которые я использую ниже:

/**
 * determines whether a pixel lies in the set
 * @params x, y - x and y coordinates on R/I axes
 * @param c - a complex number
 */
void calculateSet(int x, int y, Complex c) {
    Complex z = c.clone();
    int n = 0;
    for (; n < maxDepth; n++) {
        if (z.dis() > 4) { break; }
        z = z^2 + c;
    }
    // some code using n to color the set
}

Это следует за множеством Мандельброта:

z_(n+1) = z_n^2 + c

Но посмотрите соответствующий код для набора Burning Ship:

void calculateSet(int x, int y, Complex c) {
    Complex z = c.clone();
    int n = 0;
    for (; n < maxDepth; n++) {
        if (z.dis() > 4) { break; }
        z = abs(z)^2 + c; // ***
    }
    // follows z_(n+1) = abs(z_1)^2 + c
}

Весь код, за исключением помеченной звездочкой, идентичен. Сейчас у меня есть отдельные классы для Mandelbrot, BurningShip и несколько других, с той лишь разницей, что одна строка.

Есть ли способ определить это выражение и перейти к обобщенному Set классу?

Какой-то псевдокод:

class Set {
    // ...
    Set(Type expression) {
        // ...
        // x, y, c initialized
        // ...
        calculateSet(x, y, c, expression);
    }
    void calculateSet(int x, int y, Complex c, Type e) {
        Complex z = c.clone();
        int n = 0;
        for (; n < maxDepth; n++) {
            if (z.dis() > 4) { break; }
            z = e;
        }
    }
};

И я могу просто использовать Set, чтобы описать любой набор, который я хочу?

Set mandelbrot = Set(Type("z^2 + c"));
Set burningship = Set(Type("abs(z)^2 + c"));
// etc

Я мог бы использовать if/else операторы, чтобы иметь только один класс, но он не обобщен.

Ответы [ 3 ]

5 голосов
/ 03 мая 2019

Поскольку вы ограничены C ++ 03, вы можете использовать указатель на функцию относительно безболезненно.

Complex mandlebrotCompute(Complex z, Complex c) {
  return z*z + c;
}

void calculateSet(int x, int y, Complex c, Complex (*func)(Complex, Complex)) {
    Complex z = c.clone();
    int n = 0;
    for (; n < maxDepth; n++) {
        if (z.dis() > 4) { break; }
        z = func(z, c);
    }
}

Используется следующим образом:

Complex foo;
calculateSet(1, 2, foo, mandlebrotCompute);

Это может помочь сделать код более чистым, чтобы использовать typedef для указателя функции .

2 голосов
/ 03 мая 2019

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

typedef Complex (*Function)(const Complex&, const Complex&);

template<Function fn>
class Set
{
    // ...
    void calculateSet(int x, int y, Complex c) {
        Complex z = c;
        int n = 0;
        for (; n < maxDepth; n++) {
            if (z.dis() > 4) { break; }
                z = fn(z, c)
            }
        // some code...
    }
}

Complex mandelbrot_fn(const Complex& z, const Complex& c)
{
    return z^2 + c;
}

Complex burning_fn(const Complex& z, const Complex& c)
{
    return abs(z)^2 + c;
}


Set<mandelbrot_fn> mandelbrot;
Set<burning_fn> burning_ship;
1 голос
/ 03 мая 2019

Я думаю, это то, для чего нужны лямбды.

template<typename Lam>
class Set
{
private:
  Lam lam;

public:
  Set (Lam&& lam) : lam(lam) {}
  void calculateSet(int x, int y, Complex c)
  {
    Complex z = c.clone();
    int n = 0;
    for (; n < maxDepth; n++) {
      if (z.dis() > 4) { break; }
      z = lam(z, c);
    }
  }
};

Вы можете использовать этот класс следующим образом:

auto mandelbrot = Set([](Complex z, Complex c) -> Complex {
  return (z*z) + c;
});


auto burningShip = Set([](Complex z, Complex c) -> Complex {
  return abs((z*z)) + c;
});

mandelbrot.calculateSet(...);
burningShip .calculateSet(...);
...