Delphi实现数学表达式的计算(逆波兰式法)-四则运算解析

原帖地址:https://www.cnblogs.com/tangqs/archive/2011/11/03/2234715.html

本文主要内容为123发表于 2011-5-31 00:19:55发表的“逆波兰表达式,北大未名湖站 – 编程技术 – 必度!”帖子

http://www2.pekdo.com/forum.php?mod=viewthread&tid=32&page=1

但帖子中的内容很多不够完善,比如左右单目符号、函数名、函数参数、逗号等都没有给出处理方式,

我做较大的改进,用Delphi 7做了测试,并公开源代码供大家参考,相互学习!

程序下载地址ExpCalculator.rar

源代码下载地址ExpCalcV2.3.rar 

   众所周知,计算机处理表达式的难点在于括号的处理,在通常的表达式中,二元运算符总是置于与之相关的两个运算对象之间,所以,这种表示法也称为中缀表示。波兰逻辑学家J.Lukasiewicz于1929年提出了另一种表示表达式的方法。按此方法,每一运算符都置于其运算对象之后,故称为后缀表示。如a+b表达为ab+。这种后缀表达式非常方便去掉运算符优先级的影响与括号,甚至是单目运算符:

示例说明:

1. a*b+c*(d+e)   逆波兰表达式: ab*cde+*+

2. -a+-b*+c        逆波兰表达式: a~b~c@*+

3.a-(-(b-c))       逆波兰表达式: abc-~ –

预处理:

-(负号)处理:用~代替

+(正号)处理:用@代替,或者将其在字符串中删除

数学自然常数 e 与圆周率 pi 的用  (2.718281828459045)  与 (3.1415926535897932384) 代替(包含前后的括弧)

那么计算机怎样通过后缀式来进行运算呢?这里首先假设读取分析表达式的准备工作都已经做好了,那么首先需要做的是把表达式转换成后缀式,也就是逆波兰表达式的构建过程。

构建器由两个主要组件组成,一个是目标表达式的存储器,另一个是一个符号栈。与源表达式的扫描顺序一样,存储器是从左向右储存数据的,而符号栈则遵守后进先出的原则:

* 读入一个数据(数值与函数名非单个字符,需要做判断处理)

1. 如果是左单目运算符或者函数名,直接入符号栈;比如 正负号 ~ @ max sin

2. 如果是右单目运算符,直接入存储器栈;比如 阶乘!与百分号%

3. 如果是运输量,则直接写入存储器;检查符号栈顶是否有单目运算符,有的话则全部出栈,并写入存储器;

4. 如果是左括号”(“,则直接入符号栈;

5. 如果是右括号”)”,则弹出符号栈数据,写入存储器,一直到左括号弹出,再检查栈顶是否为左单目运算符或者函数名,是的话继续弹出,直到遇到双目运算符;

6. 如果是普通运算符,则与栈顶符号比较优先级,若大于栈顶优先级,则入栈;否则弹出栈顶符号并写入存储器,直到栈顶符号的运算优先级较小为止;

7.如果是函数参数的连接逗号“,”时,则弹出符号栈数据,直到遇到左括弧(或者逗号为止,再将逗号,入符号栈;

8.如果是结束符(表示表达式已全部读完),则符号栈全部弹出并写入存储器,否则读取数据进入下个过程;

此外还有一些处理的技巧,比如定义一个优先级最低的运算符作为表达式结束的标志(用#标示,添加在表达式结尾处),在符号栈里首先加入一个结束标志,那么表达式读完时则自动弹出栈中所有符号,并写入存储器结尾表示成功。

接下来是计算的过程。计算的时候除了刚才构建的数据外,还需要另外一个计算中间值存储栈。

1、首先是从左至右扫描数据段,如果读出的是数据则压入计算中间值存储栈

2、遇到单目运算符号就从计算中间值存储栈弹出一个数据进行运算,再把结果压回计算中间值存储栈

3、遇到双目运算符号就从计算中间值存储栈弹出两个数据进行运算,再把结果压回计算中间值存储栈;这里需要注意减法与除法的计算顺序是第一次弹出的值作为减数和除数,第二次弹出的值作为被减数和被除数。

4、遇到逗号,就从计算中间值存储栈弹出两个数据用“,”连接起来直接将数值字符串压入计算中间值存储栈,不做计算。比如12 13压入13,12

5、遇到函数,弹出计算中间值存储栈的相关数据调用函数进行计算;

这样,返回结果就是栈中唯一的数据,我们完成了逆波兰表达式的全部计算过程。

以下为以上算法的详细演示示例:

基于逆波兰式法的数学表达式计算算法举例演示

http://www.cnblogs.com/tangqs/archive/2012/05/18/2507708.html

 最后还有一点就是检查给定表达式是否正确,就是下面的 

function CheckCalcExp(const ExpT: string; var AInfo: string): boolean;

这样保证计算不会出错,具体见代码。

  /* 表达式计算 */

  /* 调用方式:CalcExp(‘1+max(0.5,sin(1))+sum(1,2^3,mod(5,3))’, res, infoStr)   */

  /* 带符号参数调用方法,先调用符号定义AddSignParam,再调用 CalcExp:*/

  /* AddSignParam([‘a’,’s’], [1, 0.5]);  或者 AddSignParam(‘a=1,s=0.5’)  */

  /* CalcExp(‘1+a+sin(s)’, res, infoStr)  */

  /* 其中res存储计算结果,为double型;infoStr存储计算时的提示信息,为string */

表达式计算器 V2.3 支持以下功能:

 1、四则运算 + – * / 、括弧()、正负(+ -)

 2、百分数 %、求幂 ^ 、整数阶乘 ! (1 至 150)

 3、参数符号计算,示例:a+b @@a=1,b=2 结算结果为3

                  用@@表示表达式中定义符号的值

 4、常数e、圆周率PI

 5、丰富的函数功能:

    统计函数:    max,min,sum,avg,stddev 标准偏差,均支持多参数

    三角函数:     sin,cos,tan,arcsin,arccos,arctan

                     degrad(60)   角度转弧度

                     raddeg(3.14) 弧度转角度

                     costh(a,b,c) 余弦定理 cosC)

    指数对数函数:sqrt,power(x,y),abs,exp,log2,log10,logN(a,N),ln

    数据处理函数:int(x),trunc(x) 取整

                        frac(x) 取小数部分

                        round(x) 四舍五入取整

                        roundto(x,-1) 保留一位小数

                        mod(M,N) 求模

    几何面积函数:s_tria(a,b,c) 三角形面积

                        s_circ(r)     圆形面积

                        s_elli(a,b)   椭圆面积

                        s_rect(a,b)   矩形面积

                        s_poly(a,n)   正多边形面积

    平面几何函数:pdisplanes(x1,y1,x2,y2) 平面两点距离

                        pdisspace(x1,y1,z1,x2,y2,z2) 空间两点

                        p_line(x0,y0, A, B, C) 平面点到线距离

                        p_planes(x0,y0,z0 A, B, C, D)空间点到面距离

       数列求和:    sn(a1, d, n) 等差数列前n项和

                        sqn(a1, q, n) 等比数列前n项和

       个税计算函数:intax(x), arcintax(x) 个税反算

    6 、历史计算记录,双击计算记录可重新修改计算

   示例: sin(1)+(-2+(3-4))*20% , e^63+PI , 15! , log2(max(2,3))

   注: 运算符必须为半角格式,三角函为弧度,输入可用空格间隔

Git Flow 的正确使用姿势

Git Flow 的概念

  在使用Git的过程中如果没有清晰流程和规划,否则,每个人都提交一堆杂乱无章的commit,项目很快就会变得难以协调和维护。 Git版本管理同样需要一个清晰的流程和规范。 Vincent Driessen 为了解决这个问题提出了 A Successful Git Branching Model 以下是基于Vincent Driessen提出的Git Flow 流程图

Git Flow 的常用分支

  • Production 分支

也就是我们经常使用的Master分支,这个分支最近发布到生产环境的代码,最近发布的Release, 这个分支只能从其他分支合并,不能在这个分支直接修改

  • Develop 分支

这个分支是我们是我们的主开发分支,包含所有要发布到下一个Release的代码,这个主要合并与其他分支,比如Feature分支

  • Feature 分支

这个分支主要是用来开发一个新的功能,一旦开发完成,我们合并回Develop分支进入下一个Release

  • Release分支

当你需要一个发布一个新Release的时候,我们基于Develop分支创建一个Release分支,完成Release后,我们合并到Master和Develop分支

  • Hotfix分支

当我们在Production发现新的Bug时候,我们需要创建一个Hotfix, 完成Hotfix后,我们合并回Master和Develop分支,所以Hotfix的改动会进入下一个Release

Git Flow 如何使用

  • Master/Devlop 分支

所有在Master分支上的Commit应该打上Tag,一般情况下Master不存在Commit,Devlop分支基于Master分支创建

  • Feature 分支

Feature分支做完后,必须合并回Develop分支, 合并完分支后一般会删点这个Feature分支,毕竟保留下来意义也不大。

  • Release 分支

Release分支基于Develop分支创建,打完Release分支之后,我们可以在这个Release分支上测试,修改Bug等。同时,其它开发人员可以基于Develop分支新建Feature (记住:一旦打了Release分支之后不要从Develop分支上合并新的改动到Release分支)发布Release分支时,合并Release到Master和Develop, 同时在Master分支上打个Tag记住Release版本号,然后可以删除Release分支了。

  • Hotfix 分支

hotfix分支基于Master分支创建,开发完后需要合并回Master和Develop分支,同时在Master上打一个tag。

Git Flow 命令示例

创建 Devlop

git branch develop
git push -u origin develop

开始 Feature

# 通过develop新建feaeure分支
git checkout -b feature develop
# 或者, 推送至远程服务器:
git push -u origin feature

# 修改md文件
git status
git add .
git commit    

完成 Feature

git pull origin
develop git checkout develop

#--no-ff:不使用fast-forward方式合并,保留分支的commit历史
#--squash:使用squash方式合并,把多次分支commit历史压缩为一次

git merge --no-ff feature
git push origin develop

git branch -d some-feature

# 如果需要删除远程feature分支: 
git push origin --delete feature  

开始 Release

git checkout -b release-0.1.0 develop

完成 Release

git checkout master
git merge --no-ff release-0.1.0
git push

git checkout develop
git merge --no-ff release-0.1.0
git push

git branch -d release-0.1.0
git push origin --delete release-0.1.0 
  
# 合并master/devlop分支之后,打上tag
git tag -a v0.1.0 master
git push --tags

开始 Hotfix

git checkout -b hotfix-0.1.1 master 

完成 Hotfix

git checkout master
git merge --no-ff hotfix-0.1.1
git push

git checkout develop
git merge --no-ff hotfix-0.1.1
git push

git branch -d hotfix-0.1.1
git push origin --delete hotfix-0.1.1

git tag -a v0.1.1 master
git push --tags

使用建议

如果你的代码没有清晰流程和规划,那么强烈推荐使用Vincent Driessen 提出的GIt flow 让你的代码管理骚起来。

结尾

本站文章图片等等来源于网络,仅作为学习之用,版权归原作者所有.如果侵犯了您的权益,请来信告知,我会尽快删除.


作者:qingwenLi
链接:https://www.jianshu.com/p/41910dc6ef29

Opencv-Python 图像透视变换cv2.warpPerspective

# -*- coding:utf-8 -*-
import cv2
import numpy as np
import sys
 
img = cv2.imread("test.jpg")
#cv2.namedWindow('original',2)
#cv2.imshow("original", img)
 
# 可选,扩展图像,保证内容不超出可视范围
img = cv2.copyMakeBorder(img, 200, 200, 200, 200, cv2.BORDER_CONSTANT, 0)
w, h = img.shape[0:2]
 
anglex = 0
angley = 30
anglez = 0  # 是旋转
fov = 42
r = 0
 
def rad(x):
    return x * np.pi / 180
 
def get_warpR():
    global anglex,angley,anglez,fov,w,h,r
    # 镜头与图像间的距离,21为半可视角,算z的距离是为了保证在此可视角度下恰好显示整幅图像
    z = np.sqrt(w ** 2 + h ** 2) / 2 / np.tan(rad(fov / 2))
    # 齐次变换矩阵
    rx = np.array([[1, 0, 0, 0],
                   [0, np.cos(rad(anglex)), -np.sin(rad(anglex)), 0],
                   [0, -np.sin(rad(anglex)), np.cos(rad(anglex)), 0, ],
                   [0, 0, 0, 1]], np.float32)
 
    ry = np.array([[np.cos(rad(angley)), 0, np.sin(rad(angley)), 0],
                   [0, 1, 0, 0],
                   [-np.sin(rad(angley)), 0, np.cos(rad(angley)), 0, ],
                   [0, 0, 0, 1]], np.float32)
 
    rz = np.array([[np.cos(rad(anglez)), np.sin(rad(anglez)), 0, 0],
                   [-np.sin(rad(anglez)), np.cos(rad(anglez)), 0, 0],
                   [0, 0, 1, 0],
                   [0, 0, 0, 1]], np.float32)
 
    r = rx.dot(ry).dot(rz)
 
    # 四对点的生成
    pcenter = np.array([h / 2, w / 2, 0, 0], np.float32)
 
    p1 = np.array([0, 0, 0, 0], np.float32) - pcenter
    p2 = np.array([w, 0, 0, 0], np.float32) - pcenter
    p3 = np.array([0, h, 0, 0], np.float32) - pcenter
    p4 = np.array([w, h, 0, 0], np.float32) - pcenter
 
    dst1 = r.dot(p1)
    dst2 = r.dot(p2)
    dst3 = r.dot(p3)
    dst4 = r.dot(p4)
 
    list_dst = [dst1, dst2, dst3, dst4]
 
    org = np.array([[0, 0],
                    [w, 0],
                    [0, h],
                    [w, h]], np.float32)
 
    dst = np.zeros((4, 2), np.float32)
 
    # 投影至成像平面
    for i in range(4):
        dst[i, 0] = list_dst[i][0] * z / (z - list_dst[i][2]) + pcenter[0]
        dst[i, 1] = list_dst[i][1] * z / (z - list_dst[i][2]) + pcenter[1]
 
    warpR = cv2.getPerspectiveTransform(org, dst)
    return warpR
 
def control():
    global anglex,angley,anglez,fov,r
 
    # 键盘控制
    if 27 == c:  # Esc quit
        sys.exit()
    if c == ord('w'):
        anglex += 1
    if c == ord('s'):
        anglex -= 1
    if c == ord('a'):
        angley += 1
        print(angley)
        # dx=0
    if c == ord('d'):
        angley -= 1
    if c == ord('u'):
        anglez += 1
    if c == ord('p'):
        anglez -= 1
    if c == ord('t'):
        fov += 1
    if c == ord('r'):
        fov -= 1
    if c == ord(' '):
        anglex = angley = anglez = 0
    if c == ord('e'):
        print("======================================")
        print('Rotation Matrix:')
        print(r)
        print('angle alpha(anglex):')
        print(anglex)
        print('angle beta(angley):')
        print(angley)
        print('dz(anglez):')
        print(anglez)
 
 
while True:
 
    warpR = get_warpR()
 
    result = cv2.warpPerspective(img, warpR, (h, w))
    cv2.namedWindow('result',2)
    cv2.imshow("result", result)
    c = cv2.waitKey(30)
    control()
 
cv2.destroyAllWindows()

运行效果:

控制:

  • s控制垂直方向上的形变
  • a和d控制水平方向上的行变
  • u和p控制角度旋转
  • e 输出当前旋转矩阵参数

原文: https://blog.csdn.net/dcrmg/article/details/80273818

DataTables列表移动端适配定义隐藏列

Responsive.breakpoints = [
    { name: 'desktop',  width: Infinity },
    { name: 'tablet-l', width: 1024 },
    { name: 'tablet-p', width: 768 },
    { name: 'mobile-l', width: 480 },
    { name: 'mobile-p', width: 320 }
];

分别表示在什么屏幕下显示该列。
比如desktop表示在PC版大屏幕时才显示该列,否则隐藏。
tablet-l表示1024的大小才显示该列,否则隐藏。
以此类推。

所以修改的代码只需要修改列头th的class名即可,如下:

<thead>
    <tr>
        <th width="8%" class="mobile-l">姓名</th>
        <th width="10%" class="mobile-l">电话</th>
        <th width="10%" class="mobile-l">性别</th>
        <th width="10%" class="desktop">时间</th>
        <th width="8%" class="desktop">操作</th> 
    </tr>
</thead>

datatable实现纵向单元格合并

转载:bird_tp@CSDN

实现效果如下:

在 drawCallback 函数里重画表格,引入以下代码即可

var api = this.api();
var rows = api.rows({ page: 'current' }).nodes();

var idx = 0;    // 第一列进行合并
var last = null;
var tr = null;
var ltd = null;

api.column(idx, { page: 'current' }).data().each(function (group, i) {
    tr = $(rows[i]);
    var td = $("td:eq(" + idx + ")", tr);
    if (last !== group) {
        td.attr("rowspan", 1);
        td.text(group);
        ltd = td;
        last = group;
        td.css("vertical-align", "middle");
    } else {
        ltd.attr("rowspan", parseInt(ltd.attr("rowspan")) + 1);
        td.remove();
    }
});

Delphi XE移动开发,将文件打包到程序中

用Delphi XE做移动开发的时候,有些情况下可能需要我们把一些文件打包到安装程序中,如网页、图片、数据库文件等,那么如何实现呢

1.首先,打开菜单 Project – Deployment

2.点击添加按钮,选择要添加的文件(文件最好放在工程目录中,这样,即使该工程在其他电脑上打开,也能找到该文件)

3.修改Remote Path:

    当开发Android程序时,Remote Path修改为 assets\internal\ 

    当开发iOS程序时,Remote Path修改为 StartUp\Documents

4.程序中使用文件的方法:

    首先,需要引用 System.IOUtils 文件,这样才能使用TPath类

    TPath.Combine(TPath.GetDocumentsPath,’a.htm’)

    或

    TPath.GetDocumentsPath + PathDelim + ‘a.htm’

    获取文件的绝对路径,然后就可以使用这个路径对文件进行操作了

linux下安装Gogs

1. 安装git
sudo apt-get install git
2.创建用户
sudo adduser git
passwd git….
su git
cd ~
3.下载最新版gogs
wget https://dl.gogs.io/gogs_v0.8.10_linux_amd64.zip
unzip ./gogs_v0.8.10_linux_amd64.zip
4.运行gogs
./gogs web
然后打开浏览器,http://服务器IP:3000/,默认就进入安装配置界面,进行简单的配置。

把/usr/local/src/gogs/scripts/init/centos/gogs拷贝到/etc/init.d/,并且增加x权限
5.自动运行
将 scripts/systemd/gogs.service 文件复制到 /lib/systemd/system
systemctl enable gogs.service

C++ 基于OpenCV的照片换背景色

#include <opencv2/opencv.hpp>
#include <iostream>

using namespace std;
using namespace cv;

void ChangeImgBG();
Mat HandleImgData(Mat &img);
/*
图片背景替换
知识点:分水岭分割、高斯模糊
处理步骤:数据组装-KMeans分割-背景消除-生成遮罩-模糊-输出
*/
void ChangeImgBG()
{
    const char *win1 = "window1";
    const char *win2 = "window2";
    const char *win3 = "window3";
    const char *win4 = "window4";
    const char *win5 = "window5";
    const char *win6 = "window6";
    namedWindow(win1, WINDOW_AUTOSIZE); //创建窗口 win1
    namedWindow(win2, WINDOW_AUTOSIZE); //创建窗口 win2
    namedWindow(win3, WINDOW_AUTOSIZE); //创建窗口 win3
    namedWindow(win4, WINDOW_AUTOSIZE); //创建窗口 win4
    namedWindow(win5, WINDOW_AUTOSIZE); //创建窗口 win5
    namedWindow(win6, WINDOW_AUTOSIZE); //创建窗口 win6

    Mat img1, img2;
    //加载图片
    img1 = imread("pph.jpg");
    if (img1.empty())
    {
        cout << "image not found..." << endl;
        exit(0); //如果图片不存在,退出程序
    }
    img2 = img1.clone();
    //显示原始图片
    imshow(win1, img1);
    //组装数据
    Mat points = HandleImgData(img1);

    //Kmeans处理
    int numCluster = 4;
    Mat labels;
    Mat centers;
    TermCriteria termCriteria = TermCriteria(TermCriteria::EPS + TermCriteria::COUNT, 10, 0.1);

    kmeans(points, numCluster, labels, termCriteria, 3, KMEANS_PP_CENTERS, centers);
    //遮罩
    Mat mask = Mat::zeros(img1.size(), CV_8UC1);
    int index = img1.rows * 2 + 2;
    int cindex = labels.at(index, 0); //背景设置为0
    int height = img1.rows;
    int width = img1.cols;

    for (int row = 0; row < height; row++)
    {
        for (int col = 0; col < width; col++)
        {
            index = row * width + col;
            int label = labels.at(index, 0);
            if (label == cindex)
            {
                img2.at(row, col)[0] = 0;
                img2.at(row, col)[1] = 0;
                img2.at(row, col)[2] = 0;
                mask.at(row, col) = 0;
            }
            else
            {
                mask.at(row, col) = 255;
            }
        }
    }

    //腐蚀
    Mat k = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));
    erode(mask, mask, k);
    imshow(win4, mask);

    //高斯模糊
    GaussianBlur(mask, mask, Size(3, 3), 0, 0);
    imshow(win5, mask);

    //通道混合
    RNG rng(12345);

    //背景颜色调整
    Vec3b color;
    /*color[0] = rng.uniform(255, 255);
    color[1] = rng.uniform(255, 255);
    color[2] = rng.uniform(255, 255);*/
    color[0] = 255;
    color[1] = 255;
    color[2] = 255;

    Mat result(img1.size(), img1.type());

    double d1 = 0.0;
    int r = 0, g = 0, b = 0;
    int r1 = 0, g1 = 0, b1 = 0;
    int r2 = 0, g2 = 0, b2 = 0;

    for (int row = 0; row < height; row++)
    {
        for (int col = 0; col < width; col++)
        {
            int m = mask.at(row, col);
            if (m == 255)
            {
                result.at(row, col) = img1.at(row, col); //前景
            }
            else if (m == 0)
            {
                result.at(row, col) = color; //背景
            }
            else
            {
                d1 = m / 255.0;
                b1 = img1.at(row, col)[0];
                g1 = img1.at(row, col)[1];
                r1 = img1.at(row, col)[2];

                b2 = color[0];
                g2 = color[1];
                r2 = color[2];

                b = b1 * d1 + b2 * (1.0 - d1);
                g = g1 * d1 + g2 * (1.0 - d1);
                r = r1 * d1 + r2 * (1.0 - d1);

                result.at(row, col)[0] = b;
                result.at(row, col)[1] = g;
                result.at(row, col)[2] = r;
            }
        }
    }

    //输出
    imshow(win2, mask);
    imshow(win3, img2);
    imshow(win6, result);
    //保存处理后的图片
    imwrite("pph_bg_white.jpg", result);
}

//组装样本数据
Mat HandleImgData(Mat &img)
{
    int width = img.cols;
    int height = img.rows;
    int count1 = width * height;
    int channels1 = img.channels();

    Mat points(count1, channels1, CV_32F, Scalar(10));
    int index = 0;
    for (int row = 0; row < height; row++)
    {
        for (int col = 0; col < width; col++)
        {
            index = row * width + col;
            Vec3b bgr = img.at(row, col);
            points.at(index, 0) = static_cast(bgr[0]);
            points.at(index, 1) = static_cast(bgr[1]);
            points.at(index, 2) = static_cast(bgr[2]);
        }
    }
    return points;
}

int main()
{
    ChangeImgBG();

    waitKey(0);
    return 0;
}