https://www.cnblogs.com/gentle-min-601/p/9785812.html

面向对象:MATLAB的自定义类 [MATLAB]

 

  这几天刚刚开始学习MATLAB的面向对象编程。以前做的事情都是用MATLAB写一些简单的脚本或者函数,这方面MATLAB成熟的函数和直截了当的矩阵运算方法和语法都很容易上手,方便人专注于算法本身。前几天写代码的时候想到,在实际用MATLAB进行数值计算时,将数据和函数用一些方法组织起来也会带来很大的便利,否则零散的数据和函数总归看着不舒服。比如,我恰好最近想在MATLAB里面写一点代码让应力张量相关的计算变得简洁一些。

一、应力张量示例的物理背景

  在弹性力学里,应力张量是描述弹性体内部某一点的应力状况的;它和过该点且法向量为 n^n^ 的面上的应力(矢量)关系是:

t=T⋅n^=n^⋅Tt=T⋅n^=n^⋅T

  这里粗写的 tt 是应力矢量;粗写的 TT 是应力张量,是一个二阶张量,可以表示为一个三维矩阵。应力张量显然不是一个可以直接观测/测量的量,对于一般理解来说,应力矢量显然是一个更为直观、物理意义更加明确的物理量,因此已知应力张量求应力矢量应当是一个很基本的操作。

  已知应力张量,求某一个面上的正应力和剪应力大小的方法是:

σn=n^⋅T⋅n^,τn=|t|2−σ2n−−−−−−−√σn=n^⋅T⋅n^,τn=|t|2−σn2

  正应力就是应力矢量和面的法向量再做一次内积,也可以看成是应力张量和法向量的二次内积。剪应力和正应力方向正交,其矢量和为应力矢量,因此用勾股定理就能得到其大小。在弹性材料中,剪应力往往是非常重要的一个物理量,因为许多材料(比如岩石等)都有抗压不抗剪的特点,剪应力的大小将直接决定这些材料是否会发生破裂、如何(沿哪个方向)发生破裂。所以已知应力张量求正应力和剪应力也是很基本的一个操作。

  此外,弹性体中的应力张量总是可以改写成主轴坐标系的形式,其物理意义是总是存在三个正交的主应力方向,以这三个主应力方向为坐标轴表示,则应力张量剪切分量均为零;其数学意义是应力张量总是可以对角化。在主应力坐标轴下表示有特别的简洁性,从而有一些好处,因此求出应力张量的主应力方向和主应力大小都具有重要的意义。

  现在,我有了三个想要实现的操作。当然,因为MATLAB直观人性化的矩阵运算语法,这三个操作在脚本里都很容易实现。比如,我现在有一个应力张量 T,三个操作分别可以用以下的代码实现:

1
2
3
4
5
6
7
8
9
10
11
12
% 求应力矢量,要求为行向量
t = (T * n).';    % 若n为列向量
t = n * T;    % 若n为行向量
 
% 求正应力
sigma = n.' * T * n;    % 若n为列向量
sigma = n * T * n.';    % 若n为行向量
% 求剪应力
tau = sqrt(norm(t)^2 - sigma^2);
 
% 求主应力及其对应方向
[V, D] = eig(T);

  看起来似乎也不复杂,但是这个存在四个不足之处:(一) 求应力矢量和求正应力时必须要判断输入的n是行向量还是列向量,如果每次写一个判断分支语句非常麻烦;(二)求剪应力必须以求正应力为基础,这意味着每次求剪应力都需要手写一遍求正应力的操作,这也包括判断语句;(三)主应力的求解结果为两个矩阵,如果想要直接得到“主应力-主应力方向”的结构怎么办呢?(四)表达不直观,可读性比较差。

  这些不足之处可以用自定义函数来解决,写一个function把代码放进去就可以了。但是这样又有一个问题:这几个操作还是没有发生关联!我们不知道这几个操作都是对于同一个物理量——应力张量进行的操作。如果能像C/C++/Python那样放个类来存放这些函数,然后再用T.X()这样的方法调用岂不是美滋滋?这就是要学习——

二、定义应力张量的MATLAB类

  现在,利用MATLAB中的“类”,将应力张量的几个操作集合到一起。那么首先,需要定义一个MATLAB类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
% stressTensor.m
% 关键词classdef,后跟一个自定义类名,存放属性和方法,以end结束
classdef stressTensor
    %    应力张量(stressTensor)类
 
    % 关键词properties:用来存放类的属性(成员变量),以end结束
    properties
        tensorMat
    end
 
    % 关键词methods:用来存放类的方法(成员函数),以end结束
    methods
        % 关键词function:这个见过很多遍了,就是用来定义函数的,写法和直接定义函数一样,以end结束
        % 下为构造函数,名称和类名称相同,返回值必须是stressTensor类,因此在书写时可以直接写"item.tensorMat",即访问其作为stressTensor类具有的成员变量
        function item = stressTensor( A )
            if nargin == 0
                item.tensorMat = zeros(3, 3);
            else
                item.tensorMat = A;
            end
        end
    end
end

  在定义一个类的时候,至少要定义一个构造函数(正如上文已经实现的),否则可能根本无法生成一个实例。这里有个小技巧,在函数中nargin是一个特殊的关键词,它的涵义可以理解为:n-arguments-input,即传入参数的个数。利用nargin,可以在函数结构内部判断输入了几个参数,并且分别做出响应,这等于写一个函数就达到了函数重载的作用。除此之外,MATLAB的函数在定义其实现方法时,可以采用可变参数列表varargin(即 variable-arguments-input),这可以使得无需指明可能的参数个数(更别提类型),在函数结构内部再进行识别和操作。当然,可以想见此时的varargin肯定不是普通的只能组织浮点数数据的向量;它必须能够组织多种多样的数据——元胞数组(cell)。

  存放类的定义的文件一般和类同名(stressTensor.m);在该文件内部需要指明类的属性(成员变量),在“属性(properties)”关键词下完成(见上代码);指明类的方法(成员函数),在“方法(methods)”关键词下完成(同见上代码)。比如,求应力矢量、求正应力、求剪应力:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
function stress = getStress( obj, direction )
    % 求应力矢量
    % 输入一个参数:待求面法向量direction;
    % 返回:该面上应力矢量stress(行向量)
    direction = direction / norm(direction);
    if size(direction, 1) > 1
        stress = (obj.tensorMat * direction).';
    else
        stress = direction * obj.tensorMat;
    end
end
 
function stressNorm = getNormal( obj, vec )
    % 求正应力
    % 输入一个参数:待求面法向量vec;
    % 返回:该面上正应力stressNorm(行向量)
    normVal = obj.getNormalVal(vec);
    stressNorm = normVal*vec/norm(vec);
end
 
function stressShear = getShear( obj, vec )
    % 求剪应力
    % 输入一个参数:待求面法向量vec;
    % 返回:该面上剪应力矢量stressShear(行向量)
    vunit = vec/norm(vec);
    stress = obj.getStress(vec);
    stressVal = dot(stress, vunit);
    stressNorm = stressVal*vunit;
    stressShear = stress - stressNorm;
end

  以及求主应力的方法:

1
2
3
4
5
6
7
8
9
10
11
12
function pAxis = principalAxis(obj)
    % 求主应力及其对应方向
    % 空输入;返回一个3*2元胞数组,第i行为:第i个主应力-第i个主应力对应方向
    pAxis = cell(3, 2);
    A = obj.tensorMat;
    [eigvec, eigval] = eig(A);
    pStress = diag(eigval);
    for i = 1:3
        pAxis{i, 1} = pStress(i);
        pAxis{i, 2} = eigvec(:, i).';
    end
end

  我们注意到在类的函数中有参数obj,这个词表明该函数中需要调用类自身的其他函数或成员变量,这和python中但凡是从属于类的方法(成员函数),在定义类的过程中必须要包含self这个参数所起的作用是相同的。C++中用法类似的是类自身指针this,但是在参数中并不需要包含。包含了obj参数以后就可以在function内部调用自身的属性和方法了,所用的语法分别是obj.property和obj.method(arguments)。就我所知,MATLAB中类函数的定义中obj不是强求包含的,但是如果不包含,这个函数放在类中的意义就不明确了,除类内重载的函数外,往往可以放在类外(对比Python的静态(static)函数)。

三、类函数的重载和运算符重载

  最后,谈到MATLAB就不能不谈到数值计算,即使是面向对象编程也不例外;谈到数值计算,就必须谈到运算符重载,因为只有这样才能真正地把自己写的类融入到MATLAB的一套运算语法中去。MATLAB里的运算符重载比其他许多语言都显得直截了当得多,因为它的每个运算符都对应一个函数,这个函数和普通的函数没有什么区别。比如+运算符,对应的函数文件就是plus.m,函数名即plus。因此在自定义类中,运算符重载和函数重载别无二致,只要重写一个位于这个类下的同名方法即可,例如:

1
2
3
4
function result = plus( obj1, obj2 )
    A = obj1.tensorMat + obj2.tensorMat;
    result = stressTensor(A);
end

  只要确定plus是从属于stressTensor的方法,就实现了+运算符重载。

  具体有哪些运算符可以重载,对应的函数名是什么可以参见运算符重载参考页。或者下面这个很大的表也提供了相应的信息:

四、检验和实际调用

  接下来,只需要把以上这一系列函数放在类定义体(classdef stressTensor)的方法(methods)关键词下就可以了。我们可以写一个脚本调用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import stressTensor.*;    % 首先,import这个类及其内容,可使用通配符.*导入类下的所有内容
T1 = stressTensor();    % 无参构造函数,参见以上构造函数定义时nargin==0的情形
A = 3*eye(3) + diag([1, 2], 1) + diag([1, 2], -1);
T2 = stressTensor(A);    % 含参构造函数
T = T1 + T2;    % 调用重载的加法
disp(T.tensorMat);    % 显示此时的应力张量
%    输出:
%     3     1     0
%     1     3     2
%     0     2     3
v = [1, 1, 1];
stress = T.getStress(v);    % 调用求应力矢量的方法
disp(stress);
%    输出:
%    2.3094    3.4641    2.8868
disp(T.getShearVal(v));    % 调用求剪应力的方法
%    输出:
%   -0.5774    0.5774   -0.0000  

  得到的结果符合预期。

五、类函数拆分为文件和组织方式

  最后,关于文件的拆分。

  一个类也许会很大,包含一些属性和数量庞大和规模庞大的方法,这肯定会造成我们刚刚写的stressTensor.m这样储存类的信息的文件越来越大、越来越乱,不便于管理。对此,C++的处理方法是,将类的定义和类函数的声明放在头文件里,函数的实现放在源文件里,而且可以通过多个源文件完成函数实现。MATLAB更简单,所有的函数都可以原封不动地拆出来,把所有的代码剪切到一个新文件里就行了,新文件的文件名设置为函数的名称。但是!为了让MATLAB编辑器明白它们都是从属于一个类的函数,所有关于一个类的方法和这个类的定义本身需要放在一个文件夹里,这个文件夹的名字为“@"+类名,@是为了让编译器明白这是一个类的文件夹;比如之前的应力张量,文件夹名为"@stressTensor"。下图就是我的这个stressTensor类的组织方式。

面向对象:MATLAB的自定义类 [MATLAB]的更多相关文章

  1. Matlab调用Java类

    第一步:定位Matlab中Java环境的ext目录 新建一个M script文件,或者直接在Matlab的交互式命令行中输入: >> disp(java.lang.System.getPr ...

  2. JS面向对象(1) -- 简介,入门,系统常用类,自定义类,constructor,typeof,instanceof,对象在内存中的表现形式

    相关链接: JS面向对象(1) -- 简介,入门,系统常用类,自定义类,constructor,typeof,instanceof,对象在内存中的表现形式 JS面向对象(2) -- this的使用,对 ...

  3. matlab——之class类(详细总结)

    https://blog.csdn.net/qinze5857/article/details/80545885 开篇:搜了一下网上介绍matlab的class类,信息不全,且总结不全面,于是单独he ...

  4. Python面向对象6:抽象类和自定义类

    抽象类- 抽象方法: 没有具体实现内容的方法成为抽象方法- 抽象方法的主要意义是规范了子类的行为和接口- 抽象类的使用需要借助abc模块 import abc - 抽象类:包含抽象方法的类叫抽象类,通 ...

  5. python面向对象反射-框架原理-动态导入-元类-自定义类-单例模式-项目的生命周期-05

    反射 reflect 反射(reflect)其实是反省,自省的意思 反省:指的是一个对象应该具备可以检测.修改.增加自身属性的能力 反射:通过字符串获取对象或者类的属性,进行操作 设计框架时需要通过反 ...

  6. .NET 基础 一步步 一幕幕[面向对象之对象和类]

    对象和类 本篇正式进入面向对象的知识点简述: 何为对象,佛曰:一花一世界,一木一浮生,一草一天堂,一叶一如来,一砂一极乐,一方一净土,一笑一尘缘,一念一清静.可见"万物皆对象". ...

  7. cocos2dx lua 绑定之二:手动绑定自定义类中的函数

    cococs2dx 3.13.1 + vs2013 + win10 1.首先按照<cocos2dx lua 绑定之一:自动绑定自定义类>绑定Student类 2.在Student类中增加一 ...

  8. Extjs-4.2.1(二)——使用Ext.define自定义类

    鸣谢:http://www.cnblogs.com/youring2/archive/2013/08/22/3274135.html --------------------------------- ...

  9. Javascript自定义类

    JavaScript并不是严格的面向对象的语言,但是带有面向对象的一些特性,我们可以通过这些特性创建js中的自定义类. JavaScript中的类其实是function关键字包裹的一系列变量和方法. ...

随机推荐

  1. InputString 转换成 BufferedImage 和 byte[]

    获取网络的一张图片,但是某种需要,要把获取的这段流输入换为BufferedImage流,有的地方还需要转换为byte[]. 获得图片地址,获得了一个图片输入流,例如: Url img = new UR ...

  2. Win10环境下载安装MySQL Community 8.0.12

    1.下载MySQL Community 8.0.12的免安装版,下载地址:https://dev.mysql.com/downloads/mysql/ 2.解压到D:\Program Files\My ...

  3. git操作笔记《二》:github更新缓慢问题的解决办法

    从GitHub上拉取代码速度十分之慢,百度了一下,说是github的某些域名的dns解析被污染了. 解决方法: 方案一:可以花钱购买VPN服务,但是这对于学生党来说是不划算的. vpn 方案二:绕过d ...

  4. [OpenCV-Python] OpenCV 中摄像机标定和 3D 重构 部分 VII

    部分 VII摄像机标定和 3D 重构 OpenCV-Python 中文教程(搬运)目录 42 摄像机标定 目标 • 学习摄像机畸变以及摄像机的内部参数和外部参数 • 学习找到这些参数,对畸变图像进行修 ...

  5. Linux 添加到环境变量

    在Linux下使用源码安装软件的时候,通常只能在软件安装目录下使用该软件命令,这样太麻烦,我们希望全局使用,可以将软件安装路径添加到系统环境变量里. 添加环境变量有2种方法: 1. 使用export命 ...

  6. xss的一般防护措施(及CreateDefaultBuilder源码)

    从上个礼拜开始,公司的安全小组就开始排查公司项目的安全性,首屈一指的就是xss问题,为此我总结了下我的经验. 1.对后台程序的输出数据做html编码处理,前端做简单的替换处理 2.如果业务需要,后台可 ...

  7. Elasticsearch学习笔记二

    PS:上一篇已经介绍了ES的一些基础概念以及单机版ES的安装,配置,本文主要介绍ES的集群管理,CRUD以及简单聚合查询. 集群管理 ES的集群部署起来也很方便,将单机版SCP复制几分,修改elast ...

  8. centos官网下载地址

    CentOS 7官方下载地址:https://www.centos.org/download/ 源自博友的博客:https://blog.csdn.net/yf9595/article/details ...

  9. angular.formJson()

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  10. Go九九乘法表

    package main import "fmt" func main(){ ; i < ; i ++ { k ++ ; j ++ { { fmt.Printf(" ...