• 一元、二元函数图像绘制


    目录

    概述

    本篇博客主要是在上一篇《每个人都该懂点函数式编程》的基础上,进一步说明“函数”在函数式编程中的重要作用。强调了函数和普通类型一样,可以赋值、存储、传参以及作为另外函数的返回值。

    本文附带了一个Demo,该Demo可以将任意字符串函数表达式解析之后生成对应的函数(一元、二元以及三元),如果你输入的是一元或者二元函数表达式,则可以绘制出相应的函数图像。一元函数图像为平面曲线,二元函数图像为立体曲面。看下图:

    函数表达式中只识别X、Y、Z三个自变量。

    Github源码下载

    字符串表达式解析

    字符串解析是重点。

    怎样去识别一串字符串函数表达式呢?如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:http://download.csdn.net/detail/dragonflies/3418135#comment

    2.函数图像在线生成(为了验证demo生成的图像是否正确)

    3.注意 函数绘制时没有做任何区间验证,比如logX中的X不能为负,否则无效(绘制时异常)。这些需要自己注意。

  • 相关阅读:
    ext2文件系统
    可变宏
    SPI—读写串行 FLASH
    使用命令
    FreeRtos——任务删除,改变任务优先级
    FreeRTOSConfig 配置文件详解
    FreeRtos——空闲任务与空闲任务钩子函数
    Elasticsearch从入门到精通-Elasticsearch是什么
    kibana从入门到精通-Kibana安装
    kibana从入门到精通-Kibana配置详解
  • 原文地址:https://www.cnblogs.com/xiaozhi_5638/p/4794478.html
Copyright © 2020-2023  润新知