算术表达式可以作为参数传递给函数来描述其中的逻辑吗?

Posted

技术标签:

【中文标题】算术表达式可以作为参数传递给函数来描述其中的逻辑吗?【英文标题】:Can an arithmetic expression be passed as argument to a function to describe the logic therein? 【发布时间】:2019-05-03 07:00:30 【问题描述】:

我正在对 Mandelbrot 集以及其他一些分形进行可视化,其中有很多重复的代码,但没有代码重用。

我正在使用的功能之一如下:

/**
 * 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

这遵循 Mandelbrot 集:

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

但是看燃烧船套装的相关代码:

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

除星号行之外的所有代码都是相同的。现在,我为MandelbrotBurningShip 和其他一些课程设置了单独的课程,唯一的区别就是这一行。

有没有办法定义这个表达式并传递给一个通用的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 语句来只拥有一个类,但它不是一概而论的。

【问题讨论】:

您使用的是什么版本的 C++? C++17 有一些您可以利用的不错的功能。编辑:另外,你对性能有多敏感? @druckermanly 我仅限于 C++03。性能很差,但我可以牺牲更多。 您可以将函数和类似函数的对象作为 C++ 中的参数传递。 将代码作为字符串传递?您将为此创建自己的解析器。 @StackDanny 底部的代码只是为了说明我想要做什么。如果可能的话,我宁愿记下我自己的字符串解析器代码。 【参考方案1】:

由于仅限于 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);

这可能有助于使use a typedef for the function pointer 的代码更简洁。

【讨论】:

【参考方案2】:

您可以制作模板,将函数作为模板参数。 我相信这是提供最多内联机会的方法。

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;

【讨论】:

我比我更喜欢这个 :)【参考方案3】:

我猜这就是 lambdas 的用途。

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(...);

【讨论】:

lambda 需要 C++11,模板参数推导需要 C++17。 我知道;在我输入答案时进行了编辑。我想其他人通过谷歌找到这个仍然有用,所以我会把它放在这里。 评论不是要删除那个答案。但要澄清它可以在什么情况下使用。

以上是关于算术表达式可以作为参数传递给函数来描述其中的逻辑吗?的主要内容,如果未能解决你的问题,请参考以下文章

您可以将子类作为参数传递给具有超类参数的函数吗? C++

我可以将类型作为参数传递给这个函数吗?

角度指令可以将参数传递给指令属性中指定的表达式中的函数吗?

如何将带有 args 的成员函数作为参数传递给另一个成员函数?

我可以将类引用作为参数传递给 VB Net 中的函数吗?

我可以将数组作为参数传递给 Java 中具有可变参数的方法吗?