数据管理与文档编写全解析
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文档编写]
总之,无论是数据管理还是代码文档编写,都是软件开发过程中不可或缺的重要环节。合理的数据管理方式能提高数据处理的效率和质量,而完善的代码文档则有助于代码的维护、共享和进一步开发。在实际编程中,应根据具体需求选择合适的数据管理方法,并养成良好的文档编写习惯。
超级会员免费看
85万+

被折叠的 条评论
为什么被折叠?



