33、数据管理与文档编写全解析

数据管理与文档编写全解析

1. 面向对象编程中的对象与方法

在编程里,对象不仅有属性,还有方法。以汽车对象为例,我们可以获取汽车的颜色属性:

print $car->color;

若汽车生产年份早于 1980 年,还能额外输出 “antique”:

print “antique” if $car->year() < 1980;

对象的方法代表对象能执行的动作。比如汽车类可以定义一个 “drive” 方法:

$car->drive(“south”, 10, “km”);

这种面向对象(OOP)的语法相较于非 OOP 语法更简洁。非 OOP 语法可能如下:

CarLibrary::drive($car, “south”, 10, “km”);

要创建特定类的对象,通常会调用构造函数,其名称一般为 “new”。构造函数属于类方法,无需对象即可调用。创建汽车对象的首选方式是:

$car = new Car;

也可以使用更繁琐但能明确 “new()” 函数位置的语法:

$car = Car::new();

创建对象时,常为其赋予一些属性,这些属性有时以列表形式传递,有时以哈希形式传递,这取决于类的编写者和其偏好的风格。例如:

$car = new Car (year => 2010, make => ‘Honda’, model => ‘Civic’);
$van = new Car (1989, ‘Toyota’, ‘Previa’);

对象能将数据的所有属性和方法整合为一个内聚单元,让我们更关注数据间的交互,而非底层实现代码。

2. 数据管理方式
2.1 硬编码数据

最简单的数据类型是硬编码到程序中的数据。例如,可将圆周率 π 和自然常数 e 存储在标量变量中:

my $pi = 3.1415926;
my $e = 2.7182818;

若子例程频繁访问大量数据,可将其存储在子例程外部。以 Kyte - Doolittle 疏水性值表为例:

my %KDH = (
    A => 1.8, C => 2.5, D => -3.5, E => -3.5, F => 2.8,
    G => -0.4, H => -3.2, I => 4.5, K => -3.9, L => 3.8,
    M => 1.9, N => -3.5, P => -1.6, Q => -3.5, R => -4.5,
    S => -0.8, T => -0.7, V => 4.2, W => -0.9, Y => -1.3,
);
sub kd_hydrophobicity {
    my ($prot) = @_;
    my $hydro = 0;
    for (my $i = 0; $i < length($prot); $i++) {
        $hydro += $KDH{substr($prot, $i, 1)};
    }
}

若将表放在子例程内部,每次程序运行时都需初始化数据结构。对于少量数据,将其放在子例程内部可使子例程更独立、易理解。

2.2 END DATA 标记

若有大量数据不想硬编码到变量中,可将其存储在程序末尾,使用 “DATA” 文件句柄读取。例如存储遗传密码:

#!/usr/bin/perl
use strict; use warnings;
my %GeneticCode;
while (<DATA>) {
    my ($codon, $amino_acid) = split;
    $GeneticCode{$codon} = $amino_acid;
}
__END__
AAA Lys
AAC Asp
AAG Lys
AAT Asp
ACA Thr
ACC Thr
:
TTT Phe

可使用 “ END ” 或 “ DATA ” 标记在程序末尾分隔数据与其他内容。若同时使用两者,需先放置 “ END ”,否则包括 “ END ” 标记在内的所有内容都会被视为数据。

2.3 CSV 和 TSV 文件

文本文件是常见的交换格式,易于读取和解析,且比大多数其他文件格式更具前瞻性。常见的两种格式是逗号分隔值(CSV)和制表符分隔值(TSV),它们通常基于记录,文件的每一行对应一种不同的单元。例如,一个基于记录的人员 CSV 文件可能如下:

Korf,Ian,F
Bradnam,Keith,R

使用 “split()” 函数可轻松解析此类文件。内部存储这些记录的自然方式是使用哈希数组:

my @people;
while (<>) {
    my ($last, $first, $mi) = split(/,/, $_);
    push @people, {
        last => $last,
        first => $first,
        mi => $mi,
    };
}

使用 “join()” 函数可方便地打印这些信息,还可包含一个 “separator” 变量,以便轻松以 TSV 或 CSV 格式打印数据:

my $separator = $tab ? “\t” : “,”;
foreach my $p (@people) {
    print join($separator,
        $p->{last},$p->{first}, $p->{mi});
}
2.4 XML 文件

XML 是表示复杂数据的便捷方式,这种基于文本的格式允许数据任意嵌套。例如,包含与上述 CSV 文件相同数据的 XML 文件如下:

<?xml version=‘1.0’?>
<people>
    <person>
        <last>Korf</last>
        <first>Ian</first>
        <mi>F</mi>
    </person>
    <person>
        <last>Bradnam</last>
        <first>Keith</first>
        <mi>R</mi>
    </person>
</people>

与 CSV 格式相比,XML 格式更冗长,每个值都有开始和结束标签及一定程度的缩进。不过,当数据高度嵌套时,XML 变得非常方便。使用免费的 Perl 模块 “XML::Simple” 可轻松读写 XML 文件。例如,解析名为 “people.xml” 的 XML 文件:

#!/usr/bin/perl
use strict; use warnings;
use XML::Simple;
use Data::Dumper;
my $xml = new XML::Simple;
my $data = $xml->XMLin(“people.xml”);
print Dumper($data);

输出结果如下:

$VAR1 = {
    ’person’ => [
        {
            ’first’ => ‘Ian’,
            ’last’ => ‘Korf’,
            ’mi’ => ‘F’
        },
        {
            ’first’ => ‘Keith’,
            ’last’ => ‘Bradnam’,
            ’mi’ => ‘R’
        }
    ]
};

若要遍历 XML 数据中的所有人,可按如下方式操作:

foreach my $p (@{$data->{person}}) {
    print “$p->{first} $p->{last}\n”;
}
2.5 持久化哈希与 dbmopen

若有大型文本数据文件,Perl 可将其导入为某种哈希结构。但每次访问信息时,Perl 脚本都需读取整个文件,即使只修改一个值,也需读取、修改并重新写入文件。Perl 提供了更高效处理大型哈希的方法,可使用 “dbmopen()” 函数将哈希与文件关联,该函数接受三个参数:哈希、文件名和八进制文件权限。例如:

#!/usr/bin/perl
use strict; use warnings;
my %hash;
dbmopen(%hash, “my_database”, 0666);
$hash{`date`} = 1;
foreach my $date (keys %hash) {print $date}

执行 “dbmopen()” 后会创建一个新文件,如上述示例中的 “my_database.db”。该文件为二进制文件,使用 Unix 命令 “less” 查看时无法正常显示。除 “dbmopen()” 外,Perl 还可使用 “DB_File” 模块以其他方式与文件交互。若需进行更复杂的文件操作,建议使用关系数据库。

2.6 Storable.pm 模块

对于比简单哈希更复杂的数据结构,Perl 提供了 “Storable” 包,可使用 “store()” 和 “retrieve()” 函数将任意复杂的数据结构读写到文件系统中。例如,将数据写入文件:

#!/usr/bin/perl
# store.pl
use strict; use warnings;
use Storable;
my %table = (
    array => [1, 2, 5],
    hash => {dog => ‘woof’,cat => ‘meow’},
    string => “hello world”,
);
store(\%table, “db.storable”);

读取数据的脚本如下:

#!/usr/bin/perl
# retrieve.pl
use strict; use warnings;
use Storable;
my $data = retrieve(‘storable_file’);
print $data->{array}[0], “\n”;

从检索角度看,原始哈希的名称并不重要,但需了解哈希结构才能高效提取信息。

3. SQL 数据库

互联网上的大部分信息存储在关系数据库管理系统(RDBMS)中,常见的 RDBMS 包括 Oracle、MySQL、PostgreSQL、DB2 和 SQLite。它们都使用结构化查询语言(SQL),掌握一种数据库的使用方法后,可触类旁通。若处理大量数据,学习 SQL 是非常值得的。

3.1 选择 SQLite 数据库

建议初学者使用 SQLite 数据库,因为设置 RDBMS 时定义所有者、用户和权限会带来麻烦,而 SQLite 数据库包含在单个文件中,只要能读写该文件,就能读写数据库。SQLite 是一种自包含、无服务器、零配置、支持事务的 SQL 数据库引擎,其源代码处于公共领域。可在终端中输入 “sqlite” 查看是否已安装,若未安装,可从 “sqlite.org” 下载并按安装说明操作。

3.2 创建 SQLite 数据库

假设我们有一个包含人员、地址和职业的文件,内容如下:
| Name | Address | Occupation |
| ---- | ---- | ---- |
| Steve Jobs | Cupertino | Tycoon |
| Jennifer Jones | Nottingham | Sales |
| Willy Loman | Brooklyn | Sales |
| Nigel Tufnel | Squatney | Musician |

在终端中输入以下命令创建数据库:

$ sqlite3 test.db

这将创建一个名为 “test.db” 的文件,并进入交互式模式,出现 “sqlite>” 提示符。在提示符下输入以下命令创建 “People” 表:

create table People (
    name TEXT,
    address TEXT,
    occupation TEXT
);

接着插入数据:

insert into People values (“Steve Jobs”, “Cupertino”, “Tycoon”);
insert into People values (“Jennifer Jones”, “Nottingham”, “Sales”);
insert into People values (“Willy Loman”, “Brooklyn”, “Sales”);
insert into People values (“Nigel Tufnel”, “Squatney”, “Musician”);

使用 SQL “select” 语句可显示表中的所有行:

sqlite> select * from People;

输出结果默认以竖线分隔字段:

Steve Jobs|Cupertino|Tycoon
Jennifer Jones|Nottingham|Sales
Willy Loman|Brooklyn|Sales
Nigel Tufnel|Squatney|Musician
3.3 使用 Perl 连接 SQLite 数据库

要使用 Perl 连接 SQLite 数据库,需使用 “DBI” 和 “DBD::SQLite” 模块。以下是一个简单的脚本示例,它连接数据库、插入新数据并进行简单的报告:

#!/usr/bin/perl
use strict; use warnings;
use DBI;
# 连接 SQLite 数据库
my $dbh = DBI->connect(“dbi:SQLite:dbname=test.db”, ““, ““) or die;
# 插入新行
$dbh->do(‘INSERT INTO People VALUES(“Ian Korf”, “UCD”, “Professor”)’);
# 报告数据库中的所有人
my $sth = $dbh->prepare(‘SELECT * FROM People’);
$sth->execute;
while (my $result = $sth->fetchrow_hashref) {
    print “$r->{name} from $r->{address} works as $r->{occupation}\n”;
}

Perl 与 SQL 数据库的结合功能强大,但也有些复杂,建议在混合使用两者之前,先阅读相关书籍。

3.4 数据库关系与规范化

前面创建的数据库并非真正的关系数据库,“occupation” 字段存在冗余数据。可将职业信息提取到单独的表中,使两个表之间建立关系。例如:
| Name | Address | Occupation_id |
| ---- | ---- | ---- |
| Steve Jobs | Cupertino | 1 |
| Jennifer Jones | Nottingham | 2 |
| Willy Loman | Brooklyn | 2 |
| Nigel Tufnel | Squatney | 3 |

Occupation Occupation_id
Tycoon 1
Sales 2
Musician 3

将公共数据提取到单独的表中称为规范化,反之则称为非规范化。为保证数据的最大完整性,应尽可能对数据进行规范化。规范化数据库可更轻松地批量更改记录,且占用内存更少,但查询速度通常比非规范化数据库慢。因此,常见的做法是创建两个包含相同信息的数据库,一个规范化数据库用于保存实时数据,定期创建一个非规范化版本用于搜索。

以下是数据管理方式的流程图:

graph LR
    A[数据管理方式] --> B[硬编码数据]
    A --> C[__END__和__DATA__标记]
    A --> D[CSV和TSV文件]
    A --> E[XML文件]
    A --> F[持久化哈希与dbmopen]
    A --> G[Storable.pm模块]
    A --> H[SQL数据库]
    H --> I[选择SQLite数据库]
    H --> J[创建SQLite数据库]
    H --> K[使用Perl连接SQLite数据库]
    H --> L[数据库关系与规范化]

数据管理与文档编写全解析

4. 代码文档编写的重要性

在软件开发过程中,随着软件复杂度的增加,文档编写的重要性愈发凸显。文档可以是给自己的个人笔记、给其他开发者的使用说明,或是给非编程用户的操作手册。以下是缺乏文档可能带来的问题:
- 运行脚本困难 :当需要再次运行脚本,或者他人想要运行你的脚本时,若没有文档,可能只有模糊的使用概念,需要花费大量时间重新查看代码来理解如何运行,还可能面临各种难以回答的问题,如“如何运行脚本实现 X 功能”“是否有同时实现 X 和 Y 的选项”等。
- 编辑代码痛苦 :编辑自己没有注释或文档的脚本时,会惊讶地发现对代码的工作原理记忆甚少,难以理解子例程的具体功能和变量命名的意图。而编辑他人无文档的代码则会更加痛苦,可能需要重写整个脚本才能理解其功能,这会让人对原作者产生不满。

因此,为代码添加文档是非常必要的,即使这可能有些繁琐,但能避免后续的诸多麻烦。

5. 文档编写的方式
5.1 注释

注释主要用于给自己的笔记,不应假设用户是程序员或会阅读源代码,所以不要在注释中为用户编写内容。注释的作用包括:
- 解释问题解决方式 :说明为什么选择某种方式解决问题,尤其是不常见的解决方案。
- 解释代码块用途 :解释子例程和其他大代码块的作用。
- 说明变量数据类型 :解释某些变量包含的数据类型,特别是名称不直观的变量。

例如:

# 计算 Kyte - Doolittle 疏水性值
sub kd_hydrophobicity {
    my ($prot) = @_;
    my $hydro = 0;
    # 遍历蛋白质序列的每个氨基酸
    for (my $i = 0; $i < length($prot); $i++) {
        $hydro += $KDH{substr($prot, $i, 1)};
    }
    return $hydro;
}
5.2 用法说明

用法说明简要告知命令行用户如何运行程序,它是使用软件的提醒,除非程序非常简单,否则不应是唯一的文档来源。以下是一个用法说明的示例:

#!/usr/bin/perl
use strict; use warnings;
use Getopt::Std;

my $version = “1.0b”;
my %opt;
getopts(‘ab:v’, \%opt);

my $usage = “
usage: $0  [options] <file> <threshold>
options:
  -a
  -b <int>
”;
die $usage unless @ARGV == 2;

if ($opt{h}) {die $usage}
if ($opt{v}) {die $version}

# etc.

在这个示例中,用法说明存储在 $usage 变量中,跨多行编写,便于查看最终消息的样子。 $0 是一个特殊变量,包含正在执行的程序名称。好的程序在用户未指定正确命令行参数时会提示用法说明,通常使用 -h -help --help 触发用法说明,使用 -v -version --version 显示版本号。

5.3 END DATA

可将较长的内部文档放在程序末尾,使用 __DATA__ 关键字后的内容可通过 DATA 文件句柄读取。例如:

#!/usr/bin/perl
use strict; use warnings;
# etc.
if ($opt_h) {while (<DATA>) {print}}
__END__
Private info
__DATA__
This program is designed to …
5.4 README 文件

常见的文档编写方式是包含一个单独的纯文本文件,通常命名为 README 。在较大的程序中,该文件不会造成混淆,同时软件分发时还可能包含 INSTALLATION LICENSE HISTORY 等文件。 README 文件应包含软件的一般信息、使用示例和明确的使用说明。

5.5 Plain Old Documentation (pod)

Perl 有一个强大的文档系统 pod ,它是一种可在程序中使用的标记语言,能将文档和代码放在一个文件中。 pod 的优点是可以导出为多种格式,使软件文档更专业。以下是一个 pod 标记的示例:

package Sequence;
use strict; use warnings;

my %GeneticCode;

=head1 NAME
Sequence

=head1 SYNOPSIS
  use Sequence;
  my $dna = Sequence::read_fasta(file);
  my $pro = Sequence::translate($dna);

=head1 DESCRIPTION
Sequence is a non - OOP library containing functions for
handling DNA, RNA, and protein sequences.

=head2 read_fasta(file)
Reads a fasta file and returns sequence in a single string.

=cut

sub read_fasta { }

=head2 translate(sequence)
# 此处可添加 translate 方法的具体说明
=cut

pod 提供了四个级别的标题和其他段落格式选项,还有用于加粗、斜体等的字体标签。

以下是文档编写方式的表格总结:
| 文档编写方式 | 说明 |
| ---- | ---- |
| 注释 | 给自己的笔记,解释问题解决方式、代码块用途和变量数据类型 |
| 用法说明 | 简要告知命令行用户如何运行程序 |
| END DATA | 将较长内部文档放在程序末尾,通过 DATA 文件句柄读取 |
| README 文件 | 单独的纯文本文件,包含软件一般信息、使用示例和说明 |
| pod | Perl 的标记语言,将文档和代码放在一个文件中,可导出多种格式 |

以下是文档编写流程的 mermaid 流程图:

graph LR
    A[代码文档编写] --> B[注释编写]
    A --> C[用法说明编写]
    A --> D[__END__和__DATA__文档添加]
    A --> E[README文件创建]
    A --> F[pod文档编写]

总之,无论是数据管理还是代码文档编写,都是软件开发过程中不可或缺的重要环节。合理的数据管理方式能提高数据处理的效率和质量,而完善的代码文档则有助于代码的维护、共享和进一步开发。在实际编程中,应根据具体需求选择合适的数据管理方法,并养成良好的文档编写习惯。

基于可靠性评估序贯蒙特卡洛模拟法的配电网可靠性评估研究(Matlab代码实现)内容概要:本文围绕“基于可靠性评估序贯蒙特卡洛模拟法的配电网可靠性评估研究”,介绍了利用Matlab代码实现配电网可靠性的仿真分析方法。重点采用序贯蒙特卡洛模拟法对配电网进行长时间段的状态抽样统计,通过模拟系统元件的故障修复过程,评估配电网的关键可靠性指标,如系统停电频率、停电持续时间、负荷点可靠性等。该方法能够有效处理复杂网络结构设备时序特性,提升评估精度,适用于含分布式电源、电动汽车等新型负荷接入的现代配电网。文中提供了完整的Matlab实现代码案例分析,便于复现和扩展应用。; 适合人群:具备电力系统基础知识和Matlab编程能力的高校研究生、科研人员及电力行业技术人员,尤其适合从事配电网规划、运行可靠性分析相关工作的人员; 使用场景及目标:①掌握序贯蒙特卡洛模拟法在电力系统可靠性评估中的基本原理实现流程;②学习如何通过Matlab构建配电网仿真模型并进行状态转移模拟;③应用于含新能源接入的复杂配电网可靠性定量评估优化设计; 阅读建议:建议结合文中提供的Matlab代码逐段调试运行,理解状态抽样、故障判断、修复逻辑及指标统计的具体实现方式,同时可扩展至不同网络结构或加入更多不确定性因素进行深化研究。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值