⼀元、⼆元函数图像绘制
⽬录
概述
本篇博客主要是在上⼀篇的基础上,进⼀步说明“函数”在函数式编程中的重要作⽤。强调了函数和普通类型⼀样,可以赋值、存储、传参以及作为另外函数的返回值。
本⽂附带了⼀个Demo,该Demo可以将任意字符串函数表达式解析之后⽣成对应的函数(⼀元、⼆元以及三元),如果你输⼊的是⼀元或者⼆元函数表达式,则可以绘制出相应的函数图像。⼀元函数图像为平⾯曲线,⼆元函数图像为⽴体曲⾯。看下图:
函数表达式中只识别X、Y、Z三个⾃变量。
字符串表达式解析
字符串解析是重点。
怎样去识别⼀串字符串函数表达式呢?如x^2+sin(x)*cos(y)。之后怎样去计算函数值呢?其实原理很简单,由于每个函数表达式中包含的有效符号是有限的,如X、Y、Z、+、-、*、/以及⼀些函数诸如log、sin、cos等等,只要我们将这些有效符号均识别筛选出来之后,再根据这些符号的优先级别⽣成⼀个函数语法树即可。
如上图所⽰,使⽤⼀个“树结构”去存储最终的语法树。最后带⼊X、Y(⼆元)求得函数值。
表达式解析这块难点是语法树的构建和最终求值。语法树的构建有点复杂,⼤家可以参见源码;最终求值的原理是,判断当前符号(节点)是单⽬运算符号(如cos、sin、负号等)还是双⽬运算符号(如+ - * /等),如果是单⽬运算⽐如cos函数,则先计算⼦节点(只有⼀个⼦节点)的值,然后将得到的值进⾏cos运算(Math.Cos(⼦节点的值));相反,如果是双⽬运算符⽐如+符号,那么先计算左⼦节点和右⼦节点的值,最后将两个值进⾏+操作(左⼦节点的值+右⼦节点的值),依次递归计算得到最终的函数值。
图像绘制
图像绘制这块就⽐较简单了。根据前⼀步得到的语法树,我们可以创建出对应的⼀元函数、⼆元函数以及三元函数(委托的形式)。事先定义的委托结构如下:
/// <summary>
/// ⼀元函数
字符串函数注册登录
/// </summary>
/// <param name="x"></param>
/// <returns></returns>
public delegate double UnaryFunction(double x);
/// <summary>
/// ⼆元函数
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <returns></returns>
public delegate double BinaryFunction(double x,double y);
/// <summary>
/// 三元函数
/
// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <param name="z"></param>
/// <returns></returns>
public delegate double MultiFunction(double x,double y,double z);
很简单就可以看出,⼀元函数接收⼀个参数,返回⼀个值;⼆元函数接收两个参数,返回⼀个值;三元函数接收三个参数,返回⼀个值。⽣成委托的过程如下:
(UnaryFunction)((double x) => { return root.GetValue(x, 0, 0); });
(BinaryFunction)((double x, double y) => { return root.GetValue(x, y, 0); });
(MultiFunction)((double x, double y, double z) => { return root.GetValue(x, y, z); });
最终给出对应的x、y、z调⽤委托,即可得到函数值。
⼀元函数绘制
随便对X取⼀个区间(如[-10,10]),以0.1为间距,计算每个X对应的Y值(函数值)。最终将这些点连接起来,出来的就是对应的⼀元函数图像。
⼆元函数绘制
相似的,随便对X、Y取⼀个区间(如X取[-10,10],Y取[-10,10]),X、Y均以0.1为间距,计算每个(X、Y)对应的Z值(函数值),最后将这些三维点绘制成曲⾯。
函数作为属性赋值
经过前⼏步得到了函数(委托对象),我们直接将委托对象作为属性赋给图像绘制控件,绘图控件更新界⾯。
//⼀元
if (textBox1.Text.ToLower().Contains('x') && !textBox1.Text.ToLower().Contains('y') && !textBox1.Text.ToLower().Contains('z'))
{
UnaryFunction func = (new SyntaxManager().ParseUnaryFunction(textBox1.Text));
unaryFunctionDrawingBoard1.Function = func;
tabControl1.SelectedIndex = 0;
}
//⼆元
else if (textBox1.Text.ToLower().Contains('x') && textBox1.Text.ToLower().Contains('y') && !textBox1.Text.ToLower().Contains('z'))
{
BinaryFunction func = (new SyntaxManager().ParseBinaryFunction(textBox1.Text));
binaryFunctionDrawingBoard1.BinaryFunction = func;
tabControl1.SelectedIndex = 1;
}
//三元
else
{
MultiFunction func = (new SyntaxManager().ParseMultiFunction(textBox1.Text));
MessageBox.Show("三元函数图像⽆法绘制!");
}
如上代码,函数作为属性赋值的⽰例。
参考及说明
1.Demo中有关3D图形绘制参考了⽹上的⼀个OpenTK的demo:
2.(为了验证demo⽣成的图像是否正确)
3.注意函数绘制时没有做任何区间验证,⽐如logX中的X不能为负,否则⽆效(绘制时异常)。这些需要⾃⼰注意。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。