大三专科实习第一个月——推荐极具有价值的小众网站
一、前言程序员是需要具备不断学习新东西的一个技术性人才。如果一直停滞不前不去学习那么将有一天你会被淘汰。二、正文快速上手新语言的快捷比较工具 Programming-Idioms。在学习一门新语言时,可以对比你自己熟悉的一门语言通过比较双方的差异性与不同的应用场景就可以了解新语言的不同点在什么地方,接下来只需要解决不同点就可以了。以下列举了应用截图programming-idioms.org/about#about…2. LibHunt 帮你你发掘当下最流行的库以及优秀的博客文章, 它还可以做一些类库的对比工作,列举的github上的star数,还有提交最近版本的发布情况,帮助你去做一些选择。 它还可以通过你关注的一些类库,比如操作数据库MySQL.Data库,会自己追踪寻找相似的类库,以及当前这些库在Google上的搜索热度,可以有效的帮助程序员接触新的有质量的东西。 我用C#的库举了一个列子www.libhunt.com这个网站的优势就是在于技术雷达的这个东西。就是他们在这段时间内更新关注的技术点 有解决方案有框架有语言有工作它会分类给你出一份报告并且是支持多语言的。总之这份技术雷达是帮助你了解一些技术动向,非常有价值的报告。www.thoughtworks.com/cn/radar这个就是美团技术团队提供的一些技术分享,他们会把他们积累的一些技术成果都在这里做一个体现。而且深度广度都有。非常适合小白的学习 以及进阶程序员的学习。tech.meituan.com5. 这个网站简陋了很多。它是淘宝团队出了一个数据库的内核月报,它每个月都会推出一个月报,虽然不会推出很多文章,但是每一篇都是很有深度的。非常适合进阶的程序员学习。mysql.taobao.org/monthly/三、面试技巧流程自我介绍什么的我就不说了,只说一下技术,拿Socket举例。常见的小白场景就是面试官:简单的介绍一下Socket 应聘人:你好面试官,不好意思我没用过,好像是通信用的。常见的初级场景就是面试官:简单的介绍一下Socket 应聘人:你好面试官,Socket是一个安全套接字,用于通信。一个发送点一个接收点,有多种通信协议比如UDP/TCP,TCP是三次握手,UDP是不用握手的。UDP比TCP更高效。如果需要交互判断的选择TCP,如果不需要交互判断的选择UDP。以上没有加分项,或许你可以这样,中级场景面试官:简单的介绍一下Socket 应聘人:你好面试官,Socket我了解的是: A=>安全套接字,要想聊Socket就要深入计算机底层我们可以从OSI7层模型说起,描述每一层的作用。 B=>TCP是什么,告诉面试官为什么是面向连接的可靠的传输协议。TCP三次握手都做了哪些事情比如Syn包,Ack包,Syn+Ack包。 C=> 通过交互流程可以展开ddos简单介绍一下表明你懂的比较多。你是个有干货的人,你是个对技术敏感的人。乐于学习的人。告诉面试官Socket在每一层都做了什么以及Socket通信的时候是如何和外界联系的【加分项】
初学者都能学会的ElasticSearch入门实战《玩转ElasticSearch 2》-3
四、倒排索引倒排索引是由单词词典、倒排列表两部分组成,单词词典记录的所有文档的单词,记录单词倒排列表的关联关系倒排列表记录了单词对应的文档结合,由倒排索引项组成,分别为文档ID、词频TF、位置、偏移案例:ElasticSearch可以为json文档中的每个字段设置自己的倒排索引,也可以指定某些字段不做倒排索引若不做倒排索引,虽可以节省存储空间,但字段无法被搜索五、使用Analyzer进行分词首先你得知道什么是分词:Analysis把全文本转换为一系列单词的过程叫做分词Analysis通过Analyzer实现的,可以通过ElasticSearch内置的分析器、或使用定制分析器分词器除了写入时转换此条,查询query时也需要用相同的分析器对查询语句进行分析案例:ElasticSearch kaka通过分词就转化为 elasticSearch和kaka,这里需要注意的是通过分词转化后把单词的首字母变为小写Analyzer的组成Character Fiters :针对原始文本处理,例如去除htmlTokenizer : 按照规则切分单词Token Filter : 将切分的单词进行加工,转为小写,删除stopwords并增加同义词ElasticSearch的内置分词器# Standard Analyzer - 默认分词器,按词切分,小写处理
# 只做单词分割、并且把单词转为小写
get _analyze
{
"analyzer":"standard",
"text":"If you don't expect quick success, you'll get a pawn every day"
}
# Simple Analyzer - 按照非字母切分(符号被过滤),小写处理
# 按照非字母切分例如字母与字母之间的——,非字母的都被去除例如下边的 2
get _analyze
{
"analyzer" :"simple",
"text":"3 If you don't expect quick success, you'll get a pawn every day kaka-niuniu"
}
# Whitespace Analyzer - 按照空格切分,不转小写
# 仅仅是根据空格切分,再无其它
get _analyze
{
"analyzer":"whitespace",
"text":"3 If you don't expect quick success, you'll get a pawn every day"
}
# Stop Analyzer - 小写处理,停用词过滤(the,a, is)
# 按照非字母切分例如字母与字母之间的——,非字母的都被去除例如下边的 2
# 相比Simple Analyze,会把the,a,is等修饰性词语去除
get _analyze
{
"analyzer":"stop",
"text":"4 If you don't expect quick success, you'll get a pawn every day"
}
# Keyword Analyzer - 不分词,直接将输入当作输出
# 不做任何分词,直接把输入的输出,假如你不想使用任何分词时就可以使用这个
get _analyze
{
"analyzer":"keyword",
"text":"5 If you don't expect quick success, you'll get a pawn every day"
}
# Patter Analyzer - 正则表达式,默认\W+(非字符分隔)
# 通过正则表达式进行分词,默认是\W+,非字符的符号进行分割
get _analyze
{
"analyzer":"pattern",
"text":"6 If you don't expect quick success, you'll get a pawn every day"
}
# Language 一提供了30多种常见语言的分词器
# 通过不同语言进行分词
# 会把复数转为单数 ,会把单词的ing去除
get _analyze
{
"analyzer":"english",
"text":"7 If you don't expect quick success, you'll get a pawn every day kakaing kakas"
}
# 中文分词器
# 这个需要安装
# 执行: ./bin/elasticsearch-plugin install analysis-icu
# 重启:nohup ./bin/elasticsearch > /dev/null 2>&1 &
get _analyze
{
"analyzer":"icu_analyzer",
"text":"你好,我是咔咔"
}
其它中文分词用的最多的IK分词,只是自定义词库,支持热更新分词字典清华大学自然语言一套分词器Thulac六、Search Api通过Url query 实现搜索例如:get /movies/_search?q=2012&df=title&sort=year:descq:指定查询语句,使用Query String Syntaxdf:查询字段,不指定时,会对所有字段进行查询sort:排序、from和size用于分页Profile:可以查看查询是如果被执行的指定字段查询、泛查询指定字段查询就是加上df即可、泛查询什么都不加,看案例通过下图右侧信息可得知,指定字段查询的是title中存在2012的数据同样也可以这样来写指定字段查询get /movies/_search?q=2012&df=title
{
"profile":true
}通过下图右侧可得知,泛查询则是在所有字段中查找存在2012的数分组与引号查询若你查询值为Beautiful Mind 则等效于Beautiful OR Mind ,类似于MySQL中的or语句,意思为查询的字段中包含 Beautiful 或者 Mind 都会被查询出来若你查询值为"Beautiful Mind" 则等效于Beautiful AND Mind ,类似于MySQL中的and语句,意思为查询的字段中不仅要包含Beautiful 而且还需要包含 Mind ,跟MySQL中不同的是顺序也不能变注意:这里你乍一眼看过去没啥区别, 其实区别就在于有无引号# PhraseQuery
# 需要字段title中存在beautiful 和 mind,并且两者的顺序不能乱
# "description" : """title:"beautiful mind""""
get /movies/_search?q=title:"Beautiful Mind"
{
"profile":"true"
}
# TermQuery
# 需要字段title中出现beautiful 或 mind 都可以
# "type" : "BooleanQuery",
# "description" : "title:beautiful title:mind",
get /movies/_search?q=title:(Beautiful Mind)
{
"profile":"true"
}
布尔操作可以使用AND / OR / NOT 或者 && / || / ! 这里你会发现使用的都是大写,+表示must(必须存在),-表示not mast(必须不存在)接下来看案例# title 里边必须有beautiful 和 mind
# "description" : "+title:beautiful +title:mind"
get /movies/_search?q=title:(Beautiful AND Mind)
{
"profile":"true"
}
# title里边包含beautiful 必须没有mind
# "description" : "title:beautiful -title:mind"
get /movies/_search?q=title:(Beautiful NOT Mind)
{
"profile":"true"
}
# title里包含beautiful ,必须也包含mind
# "description" : "title:beautiful +title:mind"
get /movies/_search?q=title:(Beautiful %2BMind)
{
"profile":"true"
}
范围查询、通配符查询、模糊匹配# year年份大于1996的电影
# 注意一下[] 为闭区间 {}为开区间
# "description" : "year:[1997 TO 9223372036854775807]"
get /movies/_search?q=year:>1996
{
"profile":"true"
}
# title 中存在b的数据
# "description" : "title:b*"
get /movies/_search?q=title:b*
{
"profile":"true"
}
# 对于模糊匹配还是非常有必要的,因为会存在一起用户会输错单词,我们就可以给做近似度匹配
# "description" : "(title:beautiful)^0.875"
get /movies/_search?q=title:beautifl~1
{
"profile":"true"
}
数据库五章其二第一讲 ——SQL
目录2.1 SQL概述 SQL的组成部分SQL的特点2.2 SQL数据定义语言 创建数据库 含义【例2.1】创建数据库“图书管理”【例2.4】删除数据库【例2.3】删除“图书管理”数据库:【例2.4】删除MY1和MY2数据库:SQL中的数据类型数值型字符串型日期时间类型常量 货币类型在创建表之前需要掌握的知识1. SQL中的数据类型数据完整性约束 举例说明2. 表中的约束条件例一: 例 创建“student”表 例二: 例 创建“stulesson”表 修改表 删除表 创建图书表Book创建图书表Book语句 创建读者表Reader创建读者表Reader语句 创建借阅表Borrow 创建借阅表Borrow语句2.1 SQL概述SQL的组成部分SQL功能命令数据定义语言(Data Definition Language,简称DDL)CREATE 、ALTER 、DROP数据操纵语言(Data Manipulation Language,简称DML)INSERT、DELETE、UPDATE、SELECT数据控制语言(Data Control Language,简称DCL)GRANT、REVOKESQL的特点(1)功能一体化(2)高度非过程化(3)面向集合的操作方式(4)两种使用方式:命令行和嵌入到其他宿主语言(如Java等)方式(5)简洁易学1. SQL 语言集数据定义语言 DDL 、数据操纵语言 DML 、数据控制语言 DCL 于一体;2. 相对于其他过程化语言(如 C 语言等编程语言),使用 SQL 语言进行数据操作,只须描述清楚要做什么,无须告诉计算机怎么做才能得到结果,即数据的存取路径、 SQL 语句操作过程都由系统自动完成。这大大减轻了用户的负担,并有利于提高数据独立性。3.非 关系数据模型采用的是面向记录的操作方式,操作对象是一条记录。而 SQL 采用集合操作方式,其查找对象查找结果都是数据的集合,每次插入删除更新操作的对象也是数据的集合。这种操作方式极大的提高了数据操作的效率。4. SQL 语言可以直接以命令方式与数据库进行交互,也可以作为嵌入式语言嵌入到其他程序设计语言(如 Java 、 C 等)中,并且两种不同使用方式中 SQL 语言的语法结构基本上是一致的。5. SQL 语言虽然功能强大,但其命令数量有限,共 9 个动词,其语法也比较简单,且使用习惯接近于自然语言(英语),因此很容易学习和掌握。2.2 SQL数据定义语言创建数据库• CREATE DATABASE 数据库名数据库如何建库(如下链接)https://developer.aliyun.com/article/928425?spm=a2c6h.13148508.setting.20.861c4f0e2sWXvC含义ON 关键字表示数据库是根据后面的参数来创建的;n 是一个占位符,表明可为新数据库指定多个文件;LOG ON 子句用于指定该数据库的事务日志文件;NAME 用于指定数据库文件的逻辑文件名;FILENAME 用于指定数据库文件的存放位置及在磁盘上的文件名;SIZE 用于指定数据库文件的初始大小,可以加上 MB 或 KB ,默认为 MB ;MAXSIZE 用于指定数据库文件的最大大小,可以加上 MB 或 KB ,默认为 MB 。省略此项表示最大大小无限制;FILEGROWTH 用于指定数据库文件的增加量,可以加上 MB 或 KB 或 % ,默认为 MB 。省略此项表示不自动增长。【例2.1】创建数据库“图书管理” CREATE DATABASE 图书管理 数据库管理系统会创建“图书管理”数据库,并自动为其创建数据文件和日志文件,且使用默认的名称和默认的存储空间分配方案。【例2.4】删除数据库DROP DATABASE 数据库名 [ , … n ]被删除的数据库不能是当前正在使用的数据库。使用数据库删除语句可以一次删除多个数据库。【例2.3】删除“图书管理”数据库: DROP DATABASE 图书管理【例2.4】删除MY1和MY2数据库:DROP DATABASE MY1, MY2SQL中的数据类型数值型字符串型日期型货币型数值型 不同的数据库系统支持的数据类型不完全相同。这里主要讲解SQL Server 2008中支持的数据类型。SQL ServerSQL99说明Bigint8字节,存储从–263 (– 9223372036854775808) 到263-1 (9223372036854775807) 范围的整数。IntInteger4字节,存储从–231(–2,147,483,648 ) 到231-1 ( 2,147,483,647 ) 范围的整数SmallintSmallint2字节,存储从–215(–32,768 ) 到215-1 (32,767 ) 范围的整数Tinyint存储从 0 到 255 之间的整数BitBit存储1或0numeric(p,q)或decimal(p,q)decimal定点精度和小数位数。使用最大精度时,有效值从 –1038 +1 到 1038 -1。其中,p为精度,指定小数点左边和右边可以存储的十进制数字的最大个数。q为小数位数,指定小数点右边可以存储的十进制数字的最大个数,0 <= q <= p。q的默认值为0floatfloat8字节,存储从 –1.79E + 308 到 1.79E + 308 范围的浮点型数real4字节,存储从 –3.40E + 38 到 3.40E + 38 范围的浮点型数 字符串型 普通字符编码:不同国家或地区的编码长度不一样,英文字符占一个字节(8位),中文汉字占2个字节(16位); Unicode编码(统一字符编码):将世界上所有的字符统一进行编码,所有字符均2字节。SQL ServerSQL99说明char(n)character固定长度的字符串类型,n表示字符串的最大长度,取值范围为1~8000varchar(n)character varying可变长度的字符串类型,n表示字符串的最大长度,取值范围为1~8000 text可存储231-1 (2,147,483,647) 个字符的大文本nchar(n)national characte固定长度的 Unicode 数据,n表示字符串的最大长度,取值范围为1~4000nvarchar(n)national character varying可变长度的 Unicode 数据,n表示字符串的最大长度,取值范围为1~4000ntext最多可存储230-1 (1,073,741,823) 个字符的统一字符编码文本binary(n)binary固定长度的二进制字符数据,n表示最大长度,取值范围为1~8000varbinary(n)binary varying可变长度的二进制字符数据,n的取值范围为1~8000image大容量的、可变长度的二进制字符数据,可以存储多种格式的文件,最大约为2GB日期时间类型 SQL Server的日期时间数据类型是将日期和时间合起来存储,它没有单独存储的日期和时间类型,但SQL92或SQL99是将日期和时间类型分开,没有日期时间合起来存储的类型,在SQL92或SQL99中日期是Date类型,时间是Time类型。SQL Server说明Datetime占用8字节空间,存储从1753年1月1日到9999年12月31日的日期和时间数据,精确到百分之三秒(或 3.33 毫秒)Smalldatetime占用4字节空间,存储从1900年1月1日到2079年6月6日的日期和时间数据,精确到分钟常量 数值型数据常量:12313,2.343 字符串类型的数据常量两端需用单引号括起来,如‘how are you’,’1234’,’中国’ 日期时间类型的数据常量也用单引号括起来,书写格式有以下几种:– ‘May 25 2012’– ‘2012-05-25’ (最常用的书写格式)– ‘2012/05/25’– ‘20120525’货币类型货币数据类型表示货币值。货币数据存储的精确度固定为四位小数,实际上货币类型的数据都是有4位小数的decimal类型的数据。SQL92或SQL99没有对应的货币类型。 SQL Server说明money8字节,存储的货币数据值介于–263 (–922,337,203,685,477.5808) 与 263-1 (+922,337,203,685,477.5807) 之间,精确到货币单位的千分之十。最多可以包含19位数字Smallmoney4字节,存储的货币数据值介于 –214,748.3648 与 +214,748.3647 之间,精确到货币单位的千分之十在创建表之前需要掌握的知识1. SQL中的数据类型数据完整性约束主码约束: PRIMARY KEY非空约束: NOT NULL检查约束: CHECK ( 条件表达式 )唯一值约束: UNIQUE默认值约束: DEFAULT 默认值外码约束: FOREIGN KEY (外码列) REFERENCES 表名(主码列)举例说明2. 表中的约束条件创建表的语法结构CREATE TABLE <表名>
(
<列名> <数据类型> [列级完整性约束定义]
{, <列名> <数据类型> [列级完整性约束
定义] … }
[, 表级完整性约束定义 ]
)注释:<表名>:所要定义的基本表的名字<列名>:组成该表的各个属性(列)<列级完整性约束条件>:涉及相应属性列的完整性约束条件<表级完整性约束条件>:涉及一个或多个属性列的完整性约束条件例一: 接下来我们用student表进行举例snonamesexagedepuid09512101李勇男19计算机系0000000109512102刘晨男20计算机系0000000209512103王敏女20计算机系student的结构列名数据类型约束说明snoCHAR(8)主码每个学生学号应非空且唯一nameCHAR(10)非空姓名信息必须要保存sexCHAR(2)检查取“男”或“女”ageINT检查小于10,大于60的学生年龄无意义depVARCHAR(26)default默认值为“计算机系”uidCHAR(18)唯一值每个人的身份证号是唯一例 创建“student”表CREATE TABLE student(
sno CHAR(8) PRIMARY KEY ,
name CHAR(10) NOT NULL ,
sex CHAR(2) CHECK(sex ='男' OR sex ='女') ,
age INT CHECK(age >=10 AND age <=60) ,
dep VARCHAR(26) default '计算机系' ,
uid CHAR(18) UNIQUE
)例二:接下来我们用stulesson(选课表)表进行举例snocnamescore001数据库原理 90003数据库原理 80003c语言 75stulesson 的结构列名数据类型约束说明snoCHAR(8)cnameCHAR(50)非空姓名信息必须要保存scoreINT检查在0~100之间例 创建“stulesson”表CREATE TABLE stulesson(
sno CHAR(8),
cname CHAR(50) NOT NULL,
score INT CHECK(score >=0 AND score <=100),
PRIMARY KEY(sno, cname ),
FOREIGN KEY(sno ) REFERENCES student(sno )
)修改表语法格式:ALTER TABLE <表名>
{[ ALTER COLUMN <列名> <新数据类型>]
-- 修改列定义
| [ ADD <列名> <数据类型> [约束] ]
-- 添加新列
| [ DROP COLUMN <列名> ] }
-- 删除列【例】为student表添加“班级”列,列的定义为 class CHAR(30)ALTER TABLE student ADD class CHAR(30) 【例】将class列修改为varchar(30)ALTER TABLE student ALTER COLUMN class VARCHAR(30)【例】删除class列 ALTER TABLE student DROP COLUMN class删除表语法:DROP TABLE <表名>{[,<表名>]…}【例】删除“student ”DROP TABLE student注意:有外码参照的表只能在外码所在表删除后才可以被删除。下面我们再创建图书表Book列名数据类型约束描述book_IDCHAR(10)主码图书编号nameVARCHAR(30)非空图书的名称authorVARCHAR(10)图书的作者publishVARCHAR(20)出版社priceDECIMAL(6,2)大于0定价classifyVarchar(20)图书分类创建图书表Book语句 CREATE TABLE Book (
book_ID CHAR(10) PRIMARY KEY,
name VARCHAR(30) NOT NULL,
author VARCHAR(10) ,
publish VARCHAR(20),
price DECIMAL(6,2) CHECK(price>0) ,
Classify varchar(20)
) 其中 PRIMARY KEY 为主码约束,CHECK为检查约束下面我们再创建读者表Reader列名数据类型约束描述reader_IDCHAR(10)主码读者编号nameVARCHAR(8)读者姓名sexCHAR(2)读者性别birthdateDATETIME读者的出生日期创建读者表Reader语句 CREATE TABLE Reader (
Reader_ID CHAR(10) PRIMARY KEY,
name VARCHAR(8) ,
sex CHAR(2),
birthdate DATETIME
)下面我们再创建借阅表Borrow列名数据类型约束描述book_IDCHAR(10)组合主码;外码,参照Book表中的book_ID图书编号reader_IDCHAR(10)组合主码;外码,参照Reader表中的reader_ID读者编号borrowdateDATETIME借阅日期returndateDATETIME还书日期创建借阅表Borrow语句CREATE TABLE Borrow(
book_ID CHAR(10),
Reader_ID CHAR(10),
Borrowdate DATETIME,
PRIMARY KEY(book_ID,Reader_ID),
FOREIGN KEY(book_ID) REFERENCES Book(book_ID),
FOREIGN KEY(Reader_ID) REFERENCES
Reader(Reader_ID)
)其中Book_ID 是外码,参照Book表的Book_IDReader_ID 是外码,参照Reader表的Reader_ID
VuePress的多语言支持
前言在我们看Vue的技术文档的时候,都会发现至少有中文版和英文版两个版本,今天小编就带大家一起了解下VuePress的多语言支持,如果对你有帮助的话,记得给小编点个赞!配置用 VuePress 的多语言支持,首先需要使用如下的文件目录结构:docs
├─ README.md
├─ guide.md
├─ study
│ └─ README.md
└─ zh
├─ README.md
├─ guide.md
└─ study
└─ README.md站点多语言配置按照上述设置好目录后,我们在配置文件config.js中设置 locales 选项,可以使用的字段有:VuePress 站点必要的配置文件是 .vuepress/config.js,它应该导出一个 JavaScript 对象。如果你使用 TypeScript ,你可以将其替换为 .vuepress/config.ts ,以便让 VuePress 配置得到更好的类型提示。langtitledescriptionhead配置代码:module.exports = {
locales: {
// 键名是该语言所属的子路径
// 作为特例,默认语言可以使用 '/' 作为其路径。
'/': {
lang: 'en-US',
title: 'VuePress',
description: 'Vue-powered Static Site Generator',
},
'/zh/': {
lang: 'zh-CN',
title: 'VuePress',
description: 'Vue 驱动的静态网站生成器',
},
},
}如果一个语言没有声明 lang, title, description 或者 head ,VuePress 将会尝试使用顶层配置的对应值。如果每个语言都声明了这些值,那么顶层配置中的对应值可以被省略。主题多语言配置VuePress 没有限制主题如何提供多语言支持,因此每个主题可能会有不同的多语言配置方式,而且部分主题可能不会提供多语言支持。如果你使用的是默认主题,那么它提供多语言支持的方式和上述是一致的,本文主要配置以默认主题配置展开。该配置项仅能在默认主题内生效,注意不要和 站点配置 中的 locales 混淆。配置代码:module.exports = {
themeConfig: {
locales: {
'/': {
selectLanguageName: 'English',
},
'/zh/': {
selectLanguageName: '简体中文',
},
},
},
}
VuePress的默认主题配置
前言默认主题的配置通过可以通过 themeConfig选项进行配置,且只对默认主题生效。如果你在使用一个自定义主题,选项可能会有不同。项目创建Step 1: Create and change into a new directorymkdir project-starter
cd project-starterStep2:Initialize your projectgit init
yarn init
# OR
git init
npm initStep3:Install VuePress locallyyarn add -D vuepress@next
# OR
npm install -D vuepress@nextStep 4: Add some scriptsopen in new window to package.json{
"scripts": {
"docs:dev": "vuepress dev docs",
"docs:build": "vuepress build docs"
}
}Step 5: Add the default temp and cache directory to .gitignore fileecho 'node_modules' >> .gitignore
echo '.temp' >> .gitignore
echo '.cache' >> .gitignoreStep 6: Create your first documentmkdir docs
echo '# Hello VuePress' > docs/README.mdStep 7: Serve the documentation site in the local serveryarn docs:dev
## OR
npm run docs:devStep 8: VuePress webpack dev server is listening at http://localhost:8080/目录结构├─ docs
│ ├─ .vuepress
│ │ └─ config.js
│ └─ README.md
├─ .gitignore
└─ package.json配置你可能已经注意到了,在 VuePress 配置中有一项 themeConfig 配置项,在 themeConfig 外部的配置项属于 站点配置 ,而在 themeConfig 内部的配置项则属于 主题配置。站点配置baselangtitledescriptionheadlocales目录配置desttempcachepublic打包工具配置bundlerbundlerConfigMarkdown 配置anchorassetscode开发配置项debughostportopenpagePatternstemplateDevtemplateSSRshouldPreloadshouldPrefetch插件配置plugins主题配置主题配置将会被 VuePress 主题来处理,所以它取决于你使用的主题是什么,也就是说取决于你theme的配置。themethemeConfig默认主题如果你没有设置 VuePress 配置的 theme 配置项,则代表使用的是默认主题。默认主题配置主要从目录结构、首页、导航栏、侧边栏、搜索框等方面进行讲解。目录结构docs
├─.vuepress
│ ├─.cache
│ │ ├─default-development
│ │ └─default-production
│ ├─.temp
│ │ ├─internal
│ │ ├─pages
│ │ │ ├─en
│ │ │ │ ├─guide
│ │ │ │ └─pages
│ │ │ │ ├─about
│ │ │ │ ├─cli
│ │ │ │ ├─components
│ │ │ │ ├─course
│ │ │ │ ├─outsource
│ │ │ │ ├─project
│ │ │ │ │ ├─angular
│ │ │ │ │ ├─micrcservice
│ │ │ │ │ ├─react
│ │ │ │ │ ├─vue
│ │ │ │ │ └─wechat
│ │ │ │ ├─standard
│ │ │ │ ├─store
│ │ │ │ ├─study
│ │ │ │ ├─templates
│ │ │ │ ├─tools
│ │ │ │ └─videos
│ │ │ ├─guide
│ │ │ └─pages
│ │ │ ├─about
│ │ │ ├─cli
│ │ │ ├─components
│ │ │ ├─course
│ │ │ │ ├─backend
│ │ │ │ ├─deploy
│ │ │ │ ├─design
│ │ │ │ ├─frontend
│ │ │ │ ├─operate
│ │ │ │ ├─project
│ │ │ │ ├─prototype
│ │ │ │ ├─test
│ │ │ │ └─ui
│ │ │ ├─outsource
│ │ │ ├─project
│ │ │ │ ├─angular
│ │ │ │ ├─micrcservice
│ │ │ │ ├─react
│ │ │ │ ├─vue
│ │ │ │ └─wechat
│ │ │ ├─standard
│ │ │ ├─store
│ │ │ ├─study
│ │ │ ├─templates
│ │ │ ├─tools
│ │ │ └─videos
│ │ └─styles
│ ├─components
│ ├─config
│ │ ├─headConfig
│ │ ├─localesConfig
│ │ │ ├─en
│ │ │ └─zh
│ │ ├─markdownConfig
│ │ ├─pluginsConfig
│ │ └─themeConfig
│ │ ├─locale
│ │ │ ├─en
│ │ │ └─zh
│ │ ├─navbar
│ │ ├─sidebar
│ │ └─themePlugins
│ ├─dist
│ │ ├─assets
│ │ │ ├─css
│ │ │ ├─img
│ │ │ └─js
│ │ ├─en
│ │ │ ├─guide
│ │ │ └─pages
│ │ │ ├─about
│ │ │ ├─cli
│ │ │ ├─components
│ │ │ ├─course
│ │ │ ├─outsource
│ │ │ ├─project
│ │ │ │ ├─angular
│ │ │ │ ├─micrcservice
│ │ │ │ ├─react
│ │ │ │ ├─vue
│ │ │ │ └─wechat
│ │ │ ├─standard
│ │ │ ├─store
│ │ │ ├─study
│ │ │ ├─templates
│ │ │ ├─tools
│ │ │ └─videos
│ │ ├─ercode
│ │ ├─guide
│ │ ├─images
│ │ │ ├─doc
│ │ │ └─home
│ │ └─pages
│ │ ├─about
│ │ ├─cli
│ │ ├─components
│ │ ├─course
│ │ ├─outsource
│ │ ├─project
│ │ │ ├─angular
│ │ │ ├─micrcservice
│ │ │ ├─react
│ │ │ ├─vue
│ │ │ └─wechat
│ │ ├─standard
│ │ ├─store
│ │ ├─study
│ │ ├─templates
│ │ ├─tools
│ │ └─videos
│ ├─public
│ │ ├─ercode
│ │ └─images
│ │ ├─doc
│ │ └─home
│ ├─styles
│ └─theme
│ ├─components
│ ├─global-components
│ ├─layouts
│ ├─styles
│ └─util
├─en
│ ├─guide
│ └─pages
│ ├─about
│ ├─cli
│ ├─components
│ ├─course
│ ├─outsource
│ ├─project
│ │ ├─angular
│ │ ├─micrcservice
│ │ ├─react
│ │ ├─vue
│ │ └─wechat
│ ├─standard
│ ├─store
│ ├─study
│ ├─templates
│ ├─tools
│ └─videos
├─guide
└─pages
├─about
├─cli
├─components
├─course
├─outsource
├─project
│ ├─angular
│ ├─micrcservice
│ ├─react
│ ├─vue
│ └─wechat
├─standard
├─store
├─study
├─templates
├─tools
└─videos首页默认的主题提供了一个首页(Homepage)的布局,需要使用它需要在项目的docs>README.md的 YAML front matter 指定 home: true,如果不使用的内容你可以通过设置null 来禁用,YAML front matter内容将会以普通的 markdown 被渲染,并插入到 YAML front matter 的后面。---
home: true
heroImage: /hero.png
heroText: 快智岛 标题
tagline: 快智岛副标题
actionText: 快速上手 →
actionLink: /zh/guide/
features:
- title: 简洁至上
details: 以 Markdown 为中心的项目结构,以最少的配置帮助你专注于写作。
- title: Vue驱动
details: 享受 Vue + webpack 的开发体验,在 Markdown 中使用 Vue 组件,同时可以使用 Vue 来开发自定义主题。
- title: 高性能
details: VuePress 为每个页面预渲染生成静态的 HTML,同时在页面被加载的时候,将作为 SPA 运行。
footer: MIT Licensed | Copyright © 2021-present Xunzhaotech
---导航栏导航栏可能包含你的页面标题、搜索框、 导航栏链接、多语言切换、仓库链接,它们均取决于你的配置。导航栏 Logo你可以通过 themeConfig.logo 增加导航栏 Logo ,Logo 可以被放置在公共文件目录示例代码// .vuepress/config.js
module.exports = {
themeConfig: {
logo: '/assets/img/logo.png',
}
}导航栏链接你可以通过 themeConfig.nav 增加一些导航栏链接,外部链接 标签的特性将默认包含target="_blank" rel="noopener noreferrer",你可以提供 target 与 rel,它们将被作为特性被增加到 标签上示例代码// .vuepress/config.js
module.exports = {
themeConfig: {
nav: [
{ text: '首页', link: '/' },
{ text: '示例', link: '/example/',target:'_blank'},
{ text: '扩展', link: 'https://google.com', target:'_self', rel:''},
//通过items数组设置下拉列表
{
text: 'Languages',
ariaLabel: 'Language Menu',
items: [
{ text: 'Chinese', link: '/language/chinese/' },
{ text: 'Japanese', link: '/language/japanese/' }
]
},
// 通过嵌套items在下拉列表中设置分组
{
text: 'Languages',
items: [
{ text: 'Group1', items: [/* */] },
{ text: 'Group2', items: [/* */] }
]
}
]
}
}禁用导航栏你可以使用 themeConfig.navbar 来禁用所有页面的导航栏示例代码// .vuepress/config.js
module.exports = {
themeConfig: {
navbar: false
}
}你也可以通过 YAML front matter 来禁用某个指定页面的导航栏---
navbar: false
---侧边栏侧边栏(Sidebar)生效,需要配置 themeConfig.sidebar,基本的配置,需要一个包含了多个链接的数组搜索框最后更新时间上 / 下一篇链接上一篇和下一篇文章的链接将会自动地根据当前页面的侧边栏的顺序来获取,可以通过过themeConfig.nextLinks 和 themeConfig.prevLinks 来全局禁用它们。js代码配置module.exports = {
themeConfig: {
// 默认值是 true 。设置为 false 来禁用所有页面的 下一篇 链接
nextLinks: false,
// 默认值是 true 。设置为 false 来禁用所有页面的 上一篇 链接
prevLinks: false
}YAML front matter配置---
prev: ./some-other-page
next: false
---Git 仓库和编辑链接当你提供了 themeConfig.repo 选项,将会自动在每个页面的导航栏生成生成一个 GitHub 链接,以及在页面的底部生成一个 "Edit this page" 链接.js代码配置// .vuepress/config.js
module.exports = {
themeConfig: {
// 假定是 GitHub. 同时也可以是一个完整的 GitLab URL
repo: 'vuejs/vuepress',
// 自定义仓库链接文字。默认从 `themeConfig.repo` 中自动推断为
// "GitHub"/"GitLab"/"Bitbucket" 其中之一,或是 "Source"。
repoLabel: '查看源码',
// 以下为可选的编辑链接选项
// 假如你的文档仓库和项目本身不在一个仓库:
docsRepo: 'vuejs/vuepress',
// 假如文档不是放在仓库的根目录下:
docsDir: 'docs',
// 假如文档放在一个特定的分支下:
docsBranch: 'master',
// 默认是 false, 设置为 true 来启用
editLinks: true,
// 默认为 "Edit this page"
editLinkText: '帮助我们改善此页面!'
}YAML front matter配置---
editLink: false
---页面滚动你可以通过 themeConfig.smoothScroll 选项来启用页面滚动效果。但在1.2.0+后配置才生效。js代码配置// .vuepress/config.js
module.exports = {
themeConfig: {
smoothScroll: true
}
}
Python的re模块 --- 正则表达式操作(三)
正则表达式对象 (正则对象)编译后的正则表达式对象支持一下方法和属性:Pattern.search(string[, pos[, endpos]])扫描整个 string 寻找第一个匹配的位置, 并返回一个相应的 匹配对象。如果没有匹配,就返回 None ;注意它和零长度匹配是不同的。可选的第二个参数 pos 给出了字符串中开始搜索的位置索引;默认为 0,它不完全等价于字符串切片; '^' 样式字符匹配字符串真正的开头,和换行符后面的第一个字符,但不会匹配索引规定开始的位置。可选参数 endpos 限定了字符串搜索的结束;它假定字符串长度到 endpos , 所以只有从 pos 到 endpos - 1的字符会被匹配。如果 endpos 小于 pos,就不会有匹配产生;另外,如果 rx 是一个编译后的正则对象, rx.search(string, 0, 50) 等价于 rx.search(string[:50], 0)。>>>
>>> pattern = re.compile("d")
>>> pattern.search("dog") # Match at index 0
<re.Match object; span=(0, 1), match='d'>
>>> pattern.search("dog", 1) # No match; search doesn't include the "d"
Pattern.match(string[, pos[, endpos]])如果 string 的 开始位置 能够找到这个正则样式的任意个匹配,就返回一个相应的 匹配对象。如果不匹配,就返回 None ;注意它与零长度匹配是不同的。可选参数 pos 和 endpos 与 search() 含义相同。>>>
>>> pattern = re.compile("o")
>>> pattern.match("dog") # No match as "o" is not at the start of "dog".
>>> pattern.match("dog", 1) # Match as "o" is the 2nd character of "dog".
<re.Match object; span=(1, 2), match='o'>如果你想定位匹配在 string 中的位置,使用 search() 来替代(另参考 search() vs. match())。Pattern.fullmatch(string[, pos[, endpos]])如果整个 string 匹配这个正则表达式,就返回一个相应的 匹配对象 。 否则就返回 None ; 注意跟零长度匹配是不同的。可选参数 pos 和 endpos 与 search() 含义相同。>>>
>>> pattern = re.compile("o[gh]")
>>> pattern.fullmatch("dog") # No match as "o" is not at the start of "dog".
>>> pattern.fullmatch("ogre") # No match as not the full string matches.
>>> pattern.fullmatch("doggie", 1, 3) # Matches within given limits.
<re.Match object; span=(1, 3), match='og'>3.4 新版功能Pattern.split(string, maxsplit=0)等价于 split() 函数,使用了编译后的样式。Pattern.findall(string[, pos[, endpos]])类似函数 findall() , 使用了编译后样式,但也可以接收可选参数 pos 和 endpos ,限制搜索范围,就像 search()。Pattern.finditer(string[, pos[, endpos]])类似函数 finiter() , 使用了编译后样式,但也可以接收可选参数 pos 和 endpos ,限制搜索范围,就像 search()。Pattern.sub(repl, string, count=0)等价于 sub() 函数,使用了编译后的样式。Pattern.subn(repl, string, count=0)等价于 subn() 函数,使用了编译后的样式。Pattern.flags正则匹配标记。这是可以传递给 compile() 的参数,任何 (?…) 内联标记,隐性标记比如 UNICODE 的结合。Pattern.groups捕获组合的数量。Pattern.groupindex映射由 (?P<id>) 定义的命名符号组合和数字组合的字典。如果没有符号组,那字典就是空的。Pattern.pattern编译对象的原始样式字符串。在 3.7 版更改: 添加 copy.copy() 和 copy.deepcopy() 函数的支持。编译后的正则表达式对象被认为是原子性的。匹配对象匹配对象总是有一个布尔值 True。如果没有匹配的话 match() 和 search() 返回 None 所以你可以简单的用 if 语句来判断是否匹配match = re.search(pattern, string)
if match:
process(match)匹配对象支持以下方法和属性:Match.expand(template)对 template 进行反斜杠转义替换并且返回,就像 sub() 方法中一样。转义如同 \n 被转换成合适的字符,数字引用(\1, \2)和命名组合(\g<1>, \g<name>) 替换为相应组合的内容。在 3.5 版更改: 不匹配的组合替换为空字符串。Match.group([group1, ...])返回一个或者多个匹配的子组。如果只有一个参数,结果就是一个字符串,如果有多个参数,结果就是一个元组(每个参数对应一个项),如果没有参数,组1默认到0(整个匹配都被返回)。 如果一个组N 参数值为 0,相应的返回值就是整个匹配字符串;如果它是一个范围 [1..99],结果就是相应的括号组字符串。如果一个组号是负数,或者大于样式中定义的组数,一个 IndexError 索引错误就 raise。如果一个组包含在样式的一部分,并被匹配多次,就返回最后一个匹配。:>>>
>>> m = re.match(r"(\w+) (\w+)", "Isaac Newton, physicist")
>>> m.group(0) # The entire match
'Isaac Newton'
>>> m.group(1) # The first parenthesized subgroup.
'Isaac'
>>> m.group(2) # The second parenthesized subgroup.
'Newton'
>>> m.group(1, 2) # Multiple arguments give us a tuple.
('Isaac', 'Newton')如果正则表达式使用了 (?P<name>…) 语法, groupN 参数就也可能是命名组合的名字。如果一个字符串参数在样式中未定义为组合名,一个 IndexError 就 raise。一个相对复杂的例子>>>
>>> m = re.match(r"(?P<first_name>\w+) (?P<last_name>\w+)", "Malcolm Reynolds")
>>> m.group('first_name')
'Malcolm'
>>> m.group('last_name')
'Reynolds'命名组合同样可以通过索引值引用>>>
>>> m.group(1)
'Malcolm'
>>> m.group(2)
'Reynolds'如果一个组匹配成功多次,就只返回最后一个匹配>>>
>>> m = re.match(r"(..)+", "a1b2c3") # Matches 3 times.
>>> m.group(1) # Returns only the last match.
'c3'Match.__getitem__(g)这个等价于 m.group(g)。这允许更方便的引用一个匹配>>>
>>> m = re.match(r"(\w+) (\w+)", "Isaac Newton, physicist")
>>> m[0] # The entire match
'Isaac Newton'
>>> m[1] # The first parenthesized subgroup.
'Isaac'
>>> m[2] # The second parenthesized subgroup.
'Newton'3.6 新版功能Match.groups(default=None)返回一个元组,包含所有匹配的子组,在样式中出现的从1到任意多的组合。 default 参数用于不参与匹配的情况,默认为 None。例如>>>
>>> m = re.match(r"(\d+)\.(\d+)", "24.1632")
>>> m.groups()
('24', '1632')如果我们使小数点可选,那么不是所有的组都会参与到匹配当中。这些组合默认会返回一个 None ,除非指定了 default 参数。>>>
>>> m = re.match(r"(\d+)\.?(\d+)?", "24")
>>> m.groups() # Second group defaults to None.
('24', None)
>>> m.groups('0') # Now, the second group defaults to '0'.
('24', '0')Match.groupdict(default=None)返回一个字典,包含了所有的 命名 子组。key就是组名。 default 参数用于不参与匹配的组合;默认为 None。 例如>>>
>>> m = re.match(r"(?P<first_name>\w+) (?P<last_name>\w+)", "Malcolm Reynolds")
>>> m.groupdict()
{'first_name': 'Malcolm', 'last_name': 'Reynolds'}
Match.start([group])Match.end([group])返回 group 匹配到的字串的开始和结束标号。group 默认为0(意思是整个匹配的子串)。如果 group 存在,但未产生匹配,就返回 -1 。对于一个匹配对象 m, 和一个未参与匹配的组 g ,组 g (等价于 m.group(g))产生的匹配是m.string[m.start(g):m.end(g)]注意 m.start(group) 将会等于 m.end(group) ,如果 group 匹配一个空字符串的话。比如,在 m =re.search('b(c?)', 'cba') 之后,m.start(0) 为 1, m.end(0) 为 2, m.start(1) 和 m.end(1) 都是 2, m.start(2) raise 一个 IndexError 例外。这个例子会从email地址中移除掉 remove_this>>>
>>> email = "tony@tiremove_thisger.net"
>>> m = re.search("remove_this", email)
>>> email[:m.start()] + email[m.end():]
'tony@tiger.net'
Match.span([group])对于一个匹配 m , 返回一个二元组 (m.start(group), m.end(group)) 。 注意如果 group 没有在这个匹配中,就返回 (-1, -1) 。group 默认为0,就是整个匹配。Match.pospos 的值,会传递给 search() 或 match() 的方法 a 正则对象 。这个是正则引擎开始在字符串搜索一个匹配的索引位置。Match.endposendpos 的值,会传递给 search() 或 match() 的方法 a 正则对象 。这个是正则引擎停止在字符串搜索一个匹配的索引位置。Match.lastindex捕获组的最后一个匹配的整数索引值,或者 None 如果没有匹配产生的话。比如,对于字符串 'ab',表达式 (a)b, ((a)(b)), 和 ((ab)) 将得到 lastindex == 1 , 而 (a)(b) 会得到 lastindex == 2 。Match.lastgroup最后一个匹配的命名组名字,或者 None 如果没有产生匹配的话。Match.re返回产生这个实例的 正则对象 , 这个实例是由 正则对象的 match() 或 search() 方法产生的。Match.string传递到 match() 或 search() 的字符串。在 3.7 版更改: 添加了对 copy.copy() 和 copy.deepcopy() 的支持。匹配对象被看作是原子性的。正则表达式例子检查对子在这个例子里,我们使用以下辅助函数来更好的显示匹配对象:def displaymatch(match):
if match is None:
return None
return '<Match: %r, groups=%r>' % (match.group(), match.groups())假设你在写一个扑克程序,一个玩家的一手牌为五个字符的串,每个字符表示一张牌,"a" 就是 A, "k" K, "q" Q, "j" J, "t" 为 10, "2" 到 "9" 表示2 到 9。要看给定的字符串是否有效,我们可以按照以下步骤>>>
>>> pair = re.compile(r".*(.).*\1")
>>> displaymatch(pair.match("717ak")) # Pair of 7s.
"<Match: '717', groups=('7',)>"
>>> displaymatch(pair.match("718ak")) # No pairs.
>>> displaymatch(pair.match("354aa")) # Pair of aces.
"<Match: '354aa', groups=('a',)>"
最后一手牌,"727ak" ,包含了一个对子,或者两张同样数值的牌。要用正则表达式匹配它,应该使用向后引用如下>>>
>>> pair = re.compile(r".*(.).*\1")
>>> displaymatch(pair.match("717ak")) # Pair of 7s.
"<Match: '717', groups=('7',)>"
>>> displaymatch(pair.match("718ak")) # No pairs.
>>> displaymatch(pair.match("354aa")) # Pair of aces.
"<Match: '354aa', groups=('a',)>"要找到对子包含的是哪一张牌,应该按照下面的方式使用 group() 方法:>>> pair.match("717ak").group(1)
'7'
# Error because re.match() returns None, which doesn't have a group() method:
>>> pair.match("718ak").group(1)
Traceback (most recent call last):
File "<pyshell#23>", line 1, in <module>
re.match(r".*(.).*\1", "718ak").group(1)
AttributeError: 'NoneType' object has no attribute 'group'
>>> pair.match("354aa").group(1)
'a'模拟 scanf()Python 目前没有一个类似c函数 scanf() 的替代品。正则表达式通常比 scanf() 格式字符串要更强大一些,但也带来更多复杂性。下面的表格提供了 scanf() 格式符和正则表达式大致相同的映射。从文件名和数字提取字符串/usr/sbin/sendmail - 0 errors, 4 warnings你可以使用 scanf() 格式化%s - %d errors, %d warnings等价的正则表达式是:(\S+) - (\d+) errors, (\d+) warnings
search() vs. match()Python 提供了两种不同的操作:基于 re.match() 检查字符串开头,或者 re.search() 检查字符串的任意位置(默认Perl中的行为)。例如>>>
>>> re.match("c", "abcdef") # No match
>>> re.search("c", "abcdef") # Match
<re.Match object; span=(2, 3), match='c'>在 search() 中,可以用 '^' 作为开始来限制匹配到字符串的首位>>>
>>> re.match("c", "abcdef") # No match
>>> re.search("^c", "abcdef") # No match
>>> re.search("^a", "abcdef") # Match
<re.Match object; span=(0, 1), match='a'>注意 MULTILINE 多行模式中函数 match() 只匹配字符串的开始,但使用 search() 和以 '^' 开始的正则表达式会匹配每行的开始>>>
>>> re.match('X', 'A\nB\nX', re.MULTILINE) # No match
>>> re.search('^X', 'A\nB\nX', re.MULTILINE) # Match
<re.Match object; span=(4, 5), match='X'>建立一个电话本split() 将字符串用参数传递的样式分隔开。这个方法对于转换文本数据到易读而且容易修改的数据结构,是很有用的,如下面的例子证明。首先,这里是输入。通常是一个文件,这里我们用三引号字符串语法>>>
>>> text = """Ross McFluff: 834.345.1254 155 Elm Street
...
... Ronald Heathmore: 892.345.3428 436 Finley Avenue
... Frank Burger: 925.541.7625 662 South Dogwood Way
...
...
... Heather Albrecht: 548.326.4584 919 Park Place"""条目用一个或者多个换行符分开。现在我们将字符串转换为一个列表,每个非空行都有一个条目:>>> entries = re.split("\n+", text)
>>> entries
['Ross McFluff: 834.345.1254 155 Elm Street',
'Ronald Heathmore: 892.345.3428 436 Finley Avenue',
'Frank Burger: 925.541.7625 662 South Dogwood Way',
'Heather Albrecht: 548.326.4584 919 Park Place']最终,将每个条目分割为一个由名字、姓氏、电话号码和地址组成的列表。我们为 split() 使用了 maxsplit 形参,因为地址中包含有被我们作为分割模式的空格符:>>> [re.split(":? ", entry, 3) for entry in entries]
[['Ross', 'McFluff', '834.345.1254', '155 Elm Street'],
['Ronald', 'Heathmore', '892.345.3428', '436 Finley Avenue'],
['Frank', 'Burger', '925.541.7625', '662 South Dogwood Way'],
['Heather', 'Albrecht', '548.326.4584', '919 Park Place']]:? 样式匹配姓后面的冒号,因此它不出现在结果列表中。如果 maxsplit 设置为 4 ,我们还可以从地址中获取到房间号:>>> [re.split(":? ", entry, 4) for entry in entries]
[['Ross', 'McFluff', '834.345.1254', '155', 'Elm Street'],
['Ronald', 'Heathmore', '892.345.3428', '436', 'Finley Avenue'],
['Frank', 'Burger', '925.541.7625', '662', 'South Dogwood Way'],
['Heather', 'Albrecht', '548.326.4584', '919', 'Park Place']]文字整理sub() 替换字符串中出现的样式的每一个实例。这个例子证明了使用 sub() 来整理文字,或者随机化每个字符的位置,除了首位和末尾字符>>>
>>> def repl(m):
... inner_word = list(m.group(2))
... random.shuffle(inner_word)
... return m.group(1) + "".join(inner_word) + m.group(3)
>>> text = "Professor Abdolmalek, please report your absences promptly."
>>> re.sub(r"(\w)(\w+)(\w)", repl, text)
'Poefsrosr Aealmlobdk, pslaee reorpt your abnseces plmrptoy.'
>>> re.sub(r"(\w)(\w+)(\w)", repl, text)
'Pofsroser Aodlambelk, plasee reoprt yuor asnebces potlmrpy.'找到所有副词findall() 匹配样式 所有 的出现,不仅是像 search() 中的第一个匹配。比如,如果一个作者希望找到文字中的所有副词,他可能会按照以下方法用 findall()>>>
>>> text = "He was carefully disguised but captured quickly by police."
>>> re.findall(r"\w+ly", text)
['carefully', 'quickly']找到所有副词和位置如果需要匹配样式的更多信息, finditer() 可以起到作用,它提供了 匹配对象 作为返回值,而不是字符串。继续上面的例子,如果一个作者希望找到所有副词和它的位置,可以按照下面方法使用 finditer()>>>
>>> text = "He was carefully disguised but captured quickly by police."
>>> for m in re.finditer(r"\w+ly", text):
... print('%02d-%02d: %s' % (m.start(), m.end(), m.group(0)))
07-16: carefully
40-47: quickly原始字符记法原始字符串记法 (r"text") 保持正则表达式正常。否则,每个正则式里的反斜杠('\') 都必须前缀一个反斜杠来转义。比如,下面两行代码功能就是完全一致的>>>
>>> re.match(r"\W(.)\1\W", " ff ")
<re.Match object; span=(0, 4), match=' ff '>
>>> re.match("\\W(.)\\1\\W", " ff ")
<re.Match object; span=(0, 4), match=' ff '>当需要匹配一个字符反斜杠,它必须在正则表达式中转义。在原始字符串记法,就是 r"\\"。否则就必须用 "\\\\",来表示同样的意思>>>
>>> re.match(r"\\", r"\\")
<re.Match object; span=(0, 1), match='\\'>
>>> re.match("\\\\", r"\\")
<re.Match object; span=(0, 1), match='\\'>写一个词法分析器一个 词法器或词法分析器 分析字符串,并分类成目录组。 这是写一个编译器或解释器的第一步。文字目录是由正则表达式指定的。这个技术是通过将这些样式合并为一个主正则式,并且循环匹配来实现的import collections
import re
Token = collections.namedtuple('Token', ['type', 'value', 'line', 'column'])
def tokenize(code):
keywords = {'IF', 'THEN', 'ENDIF', 'FOR', 'NEXT', 'GOSUB', 'RETURN'}
token_specification = [
('NUMBER', r'\d+(\.\d*)?'), # Integer or decimal number
('ASSIGN', r':='), # Assignment operator
('END', r';'), # Statement terminator
('ID', r'[A-Za-z]+'), # Identifiers
('OP', r'[+\-*/]'), # Arithmetic operators
('NEWLINE', r'\n'), # Line endings
('SKIP', r'[ \t]+'), # Skip over spaces and tabs
('MISMATCH', r'.'), # Any other character
]
tok_regex = '|'.join('(?P<%s>%s)' % pair for pair in token_specification)
line_num = 1
line_start = 0
for mo in re.finditer(tok_regex, code):
kind = mo.lastgroup
value = mo.group()
column = mo.start() - line_start
if kind == 'NUMBER':
value = float(value) if '.' in value else int(value)
elif kind == 'ID' and value in keywords:
kind = value
elif kind == 'NEWLINE':
line_start = mo.end()
line_num += 1
continue
elif kind == 'SKIP':
continue
elif kind == 'MISMATCH':
raise RuntimeError(f'{value!r} unexpected on line {line_num}')
yield Token(kind, value, line_num, column)
statements = '''
IF quantity THEN
total := total + price * quantity;
tax := price * 0.05;
ENDIF;
'''
for token in tokenize(statements):
print(token)这个词法器产生以下输出Token(type='IF', value='IF', line=2, column=4)
Token(type='ID', value='quantity', line=2, column=7)
Token(type='THEN', value='THEN', line=2, column=16)
Token(type='ID', value='total', line=3, column=8)
Token(type='ASSIGN', value=':=', line=3, column=14)
Token(type='ID', value='total', line=3, column=17)
Token(type='OP', value='+', line=3, column=23)
Token(type='ID', value='price', line=3, column=25)
Token(type='OP', value='*', line=3, column=31)
Token(type='ID', value='quantity', line=3, column=33)
Token(type='END', value=';', line=3, column=41)
Token(type='ID', value='tax', line=4, column=8)
Token(type='ASSIGN', value=':=', line=4, column=12)
Token(type='ID', value='price', line=4, column=15)
Token(type='OP', value='*', line=4, column=21)
Token(type='NUMBER', value=0.05, line=4, column=23)
Token(type='END', value=';', line=4, column=27)
Token(type='ENDIF', value='ENDIF', line=5, column=4)
Token(type='END', value=';', line=5, column=9)
2020你应该有自己的知识库
概述俗话说,好记性不如烂笔头,在信息化的今天,笔头我们已经用的很少了,取而代之的是电脑、手机等一系列终端设备,笔头烂不烂已经不重要了,重要的是笔头做的事情才是我们关注的重点,做笔记,把我们的故事记录下来,方便在以后忘记的时候拿出来看看,做做复习,怀念怀念青春。今天小编将为大家介绍如何用当下流行的Vuepress搭建一套属于自己的知识库。VuePress可以让您非常方便的在Markdown 文档中编写 Vue 代码,并且 VuePress 对编译后的 HTML文件做了一些针对搜索引擎的优化。另外 VuePress 针对 Markdown 文件做了一些扩展使其功能更加强大,本文将围绕搭建一个 Github 的静态博客展开。如果对你有帮助,记得点赞!,你的支持是小编最大的创作动力。目录第一部分 认识VuePress第二部分 认识 Nuxt第三部分 认识 Docsify / Docute第四部分 认识 Hexo第五部分 认识 GitBook第六部分 认识 Jekyll第七部分 认识 Hugo你将学到使用VuePress搭建自己的文档库了解搭建文档库的工具有哪些了解搭建文档库工具的优缺点知道为什么会选用Vuepress搭建自己的文档库环境要求注意: 请确保你的 Node.js 版本 >= 8.6常用命令安装VuePress$ npm install -g vuepress
或
$ yarn global add vuepress新建一个 markdown 文件echo '# Hello VuePress!' > README.md开始写作$ vuepress dev .构建静态文件$ vuepress build .运行命令$ npm run docs:dev打包命令$ npm run docs:bulid第一部分 认识VuePress第1章 VuePress 是什么VuePress(Vue 驱动的静态网站生成器)是尤大大4月12日发布的一个全新的基于vue的静态网站生成器,实际上就是一个vue的spa应用,内置webpack,可以用来写文档,是为了支持 Vue及其子项目的文档需求而诞生的。它由两部分组成:第一部分是一个极简静态网站生成器,它包含由 Vue 驱动的主题系统和插件 API第二部分是为书写技术文档而优化的默认主题第2章 VuePress 工作原理VuePress 网站是一个由 Vue、Vue Router 和 webpack 驱动的单页应用。在构建时,为应用创建一个服务端渲染(SSR)的版本,然后通过虚拟访问每一条路径来渲染对应的HTML。第3章 VuePress 特点每一个由 VuePress 生成的页面都带有预渲染好的 HTML,也因此具有非常好的加载性能和搜索引擎优化(SEO)。同时,一旦页面被加载,Vue 将接管这些静态内容,并将其转换成一个完整的单页应用(SPA),其他的页面则会只在用户浏览到的时候才按需加载。简洁至上: 以 Markdown 为中心的项目结构,以最少的配置帮助你专注于写作。Vue 驱动: 享受 Vue + webpack 的开发体验,可以在 Markdown 中使用 Vue 组件,又可以使用 Vue 来开发自定义主题。高性能: VuePress 会为每个页面预渲染生成静态的 HTML,同时,每个页面被加载的时候,将作为 SPA 运行。内置的 Markdown 拓展(为技术文档而优化的内置Markdown拓展)内置的 Markdown 拓展(在Markdown文件中使用Vue组件)自动生成Service Worker(支持PWA)Google Analytics集成Vue驱动的自定义主题系统默认主题多语言支持响应式布局支持PWA模式强大的 Plugin API基于Git的"最后更新时间第4章 VuePress 安装环境请确保你的 Node.js 版本 >= 8.6。安装工具1:yarn(建议)安装工具2:npm一定要使用 yarn add vuepress@next或npm install -g vuepress@next 而不是官网的命令,官网的命令是老版本如果您的项目代码中并没有 package.json文件,请先执行 npm init第5章 VuePress 全局安装如果你只是想尝试一下 VuePress,你可以全局安装它:# 安装
yarn global add vuepress@next # 或者:npm install -g vuepress@next
# 创建项目目录
mkdir vuepress-demo && cd vuepress-demo
# 在项目根目录下新加 docs 文件夹
mkdir docs
# 新建一个 markdown 文件
echo '# Hello VuePress!' > docs/README.md
# 开始写作
vuepress dev .
# 构建静态文件
vuepress build .注意: vuepress dev . 和vuepress build .后面的“.”第6章 VuePress 项目安装如果你想在一个现有项目中使用 VuePress,同时想要在该项目中管理文档,则应该将 VuePress 安装为本地依赖。作为本地依赖安装让你可以使用持续集成工具,或者一些其他服务(比如 Netlify)来帮助你在每次提交代码时自动部署。# 将 VuePress 作为一个本地依赖安装
yarn add -D vuepress@next # 或者:npm install -D vuepress@next
# 新建一个 docs 文件夹
mkdir docs
# 新建一个 markdown 文件
echo '# Hello VuePress!' > docs/README.md
# 开始写作
npx vuepress dev docs注意: 如果你的现有项目依赖了 webpack 3.x,推荐使用 Yarn 而不是 npm 来安装 VuePress。因为在这种情形下,npm 会生成错误的依赖树。用npm安装执行npx vuepress dev docs会报错,用yarn安装vuepress正常。VuePress 对 Markdown 做了一些扩展,使得我们可以在 Markdown 文件中使用 YAML 语法,VuePress 使用 ---来隔离 Markdown 语法。默认的主题提供了一个首页(Homepage)的布局 (用于 这个网站的主页)。想要使用它,需要在你的根级 README.md(docs/README.md) 的 YAML front matter 指定 home: true。配置如下:---
sidebar: auto // 该语法表示使用当前页面标题自动生成侧边栏
home: true
heroImage: /hero.png
heroText: Hero 标题
tagline: Hero 副标题
actionText: 快速上手 →
actionLink: /zh/guide/
features:
- title: 简洁至上
details: 以 Markdown 为中心的项目结构,以最少的配置帮助你专注于写作。
- title: Vue驱动
details: 享受 Vue + webpack 的开发体验,在 Markdown 中使用 Vue 组件,同时可以使用 Vue 来开发自定义主题。
- title: 高性能
details: VuePress 为每个页面预渲染生成静态的 HTML,同时在页面被加载的时候,将作为 SPA 运行。
footer: MIT Licensed | Copyright © 2018-present Evan You
---注意: 1.你可以将相应的内容设置为 null 来禁用标题和副标题。2.任何 YAML front matter 之后额外的内容将会以普通的 markdown 被渲染,并插入到 features 的后面。接着,在 package.json 里加一些脚本:在项目根目录下的 package.json 中添加 scripts :
{
"scripts": {
"docs:dev": "vuepress dev docs",
"docs:build": "vuepress build docs"
}
}
# 本地运行文档
npm run docs:dev
# 编译打包生产静态 HTML 文件
npm run docs:build然后就可以开始写作了:yarn docs:dev # 或者:npm run docs:dev
就会出现一个和Vuepress一样的网站了要生成静态的 HTML 文件,运行:yarn docs:build # 或者:npm run docs:build注意: 默认情况下,文件将会被生成在 .vuepress/dist,当然,你也可以通过 .vuepress/config.js 中的 dest 字段来修改,生成的文件可以部署到任意的静态文件服务器上,参考 VuePress部署 来了解更多。第7章 VuePress 目录结构VuePress 遵循 “约定优于配置” 的原则,推荐的目录结构如下:.
├── docs
│ ├── .vuepress (可选的)
│ │ ├── components (可选的)
│ │ ├── theme (可选的)
│ │ │ └── Layout.vue
│ │ ├── public (可选的)
│ │ ├── styles (可选的)
│ │ │ ├── index.styl
│ │ │ └── palette.styl
│ │ ├── templates (可选的, 谨慎配置)
│ │ │ ├── dev.html
│ │ │ └── ssr.html
│ │ ├── config.js (可选的)
│ │ └── enhanceApp.js (可选的)
│ │
│ ├── README.md
│ ├── guide
│ │ └── README.md
│ └── config.md
│
└── package.json目录说明docs/.vuepress: 用于存放全局的配置、组件、静态资源等。docs/.vuepress/components: 该目录中的 Vue 组件将会被自动注册为全局组件。docs/.vuepress/theme: 用于存放本地主题。docs/.vuepress/styles: 用于存放样式相关的文件。docs/.vuepress/styles/index.styl: 将会被自动应用的全局样式文件,会生成在最终的 CSS 文件结尾,具有比默认样式更高的优先级。docs/.vuepress/styles/palette.styl: 用于重写默认颜色常量,或者设置新的 stylus 颜色常量。docs/.vuepress/public: 静态资源目录。docs/.vuepress/templates: 存储 HTML 模板文件。docs/.vuepress/templates/dev.html: 用于开发环境的 HTML 模板文件。docs/.vuepress/templates/ssr.html: 构建时基于 Vue SSR 的 HTML 模板文件。docs/.vuepress/config.js: 配置文件的入口文件,也可以是 YML 或 toml。docs/.vuepress/enhanceApp.js: 客户端应用的增强。页面访问路由文件的相对路径页面路由地址/README.md//guide/README.md/guide//config.md/config.html命令行使用基本用法vuepress <command> targetDir [options]常用命令vuepress dev docs
vuepress build docs
vuepress eject第8章 VuePress 配置通过 VuePress 配置文件我们可以使用一些自定义的功能,譬如添加侧边栏,添加导航栏等。首先在 docs 目录下新建一个 .vuepress目录,并在该目录下方新建 config.js。VuePress的基本配置主要在docs/.vuepress/config.js: 配置文件的入口文件,也可以是 YAML 或 toml中完成。8.1 基本配置base在使用 VuePress 编写博客并发布到 Github pages 的时候,我们可能会遇到下图所显示的问题,页面已经有了,但是样式和 js 没有加载成功。我们可以通过配置 base 属性来解决这个问题, base 属性的默认值是 /。假如您准备将代码部署到 xunzhaotech.github.io/xz-blog/ , 那么 base属性就应该被设置成 /xz-blog/。注意:base 属性的值总是以 / 开始并以 / 结束。部署站点的基础路径,如果你想让你的网站部署到一个子路径下,你将需要设置它。如 GitHub pages,如果你想将你的网站部署到 foo.github.io/bar/,那么 base 应该被设置成 "/bar/",它的值应当总是以斜杠开始,并以斜杠结束(base 将会作为前缀自动地插入到所有以 / 开始的其他选项的链接中,所以你只需要指定一次)。类型: string默认值: /title在 VuePress 中通过设置 title属性来设置网站的标题,它将会被用作所有页面标题的前缀,在默认主题下,它将应用在导航栏上。网站的标题,它将会被用作所有页面标题的前缀,同时,默认主题下,它将显示在导航栏(navbar)上。类型: string默认值: undefinedhead注入到当前页面的 HTML 中的标签,每个标签都可以以 [tagName, { attrName: attrValue }, innerHTML?]类型: Array默认值: []description网站的描述,它将会以 标签渲染到当前页面的 HTML 中。类型: string默认值: undefinedhost指定用于 dev server 的主机名。类型: string默认值: '0.0.0.0'port指定 dev server 的端口。类型: number默认值: 8080temp指定客户端文件的临时目录。Type: stringDefault: /path/to/@vuepress/core/.tempdestVuePress 默认将文件打包在 .vuepress/dist目录下,我们可以通过 dest指定 vuepress build 的输出目录,例如将文件输出在项目根目录下的 dist文件夹中。如果传入的是相对路径,则会基于 process.cwd() 进行解析。类型: string默认值: .vuepress/distrepo通过设置 repo属性,VuePress 会在导航栏中添加一个 Github 仓库的链接。locales类型: { [path: string]: Object }默认值: undefined要启用 VuePress 的多语言支持,首先需要使用如下的文件结构:docs
├─ README.md
├─ foo.md
├─ nested
│ └─ README.md
└─ zh
├─ README.md
├─ foo.md
└─ nested
└─ README.md然后,在 .vuepress/config.js 中提供 locales 选项:module.exports = {
locales: {
// 键名是该语言所属的子路径
// 作为特例,默认语言可以使用 '/' 作为其路径。
'/': {
lang: 'en-US', // 将会被设置为 <html> 的 lang 属性
title: 'VuePress',
description: 'Vue-powered Static Site Generator'
},
'/zh/': {
lang: 'zh-CN',
title: 'VuePress',
description: 'Vue 驱动的静态网站生成器'
}
}
}注意: 如果一个语言没有声明 title 或者 description,VuePress 将会尝试使用配置顶层的对应值。如果每个语言都声明了 title 和 description,则顶层的这两个值可以被省略。默认主题多语言配置shouldPrefetch一个函数,用来控制对于哪些文件,是需要生成 资源提示的。默认情况下,异步 chunk 中的所有资源都将被预取,因为这是低优先级指令; 然而,为了更好地控制带宽使用情况,你也可以自定义要预取的资源。此选项具有与 shouldPreload 相同的函数签名。详细参考。类型: Function默认值: () => truecacheVuePress 默认使用了 cache-loader 来大大地加快 webpack 的编译速度。此选项可以用于指定 cache 的路径,同时也可以通过设置为 false 来在每次构建之前删除 cache。类型: boolean|string默认值: true提示: 这个选项也可以通过命令行来使用:$ vuepress dev docs --cache .cache # 设置 cache 路径
$ vuepress dev docs --no-cache # 在每次构建前删除 cacheextraWatchFiles指定额外的需要被监听的文件。你可以监听任何想监听的文件,文件变动将会触发 vuepress 重新构建,并实时更新。类型: Array 默认值: []module.exports = {
extraWatchFiles: [
'.vuepress/foo.js', // 使用相对路径
'/path/to/bar.js' // 使用绝对路径
]
}patternsSpecify which pattern of files you want to be resolved.Type: ArrayDefault: ['/*.md', '/*.vue']8.2 导航栏与侧边栏在 VuePress 中如果想要为您的网站添加导航栏,可以通过设置 themeConfig.nav 来添加导航链接,通过设置 themeConfig.sidebar 属性来添加侧边栏。如果您的导航是一个下拉列表,可以通过 items 属性来设置。// dcos/.vuepress/config.js
module.exports = {
themeConfig: {
// 添加导航栏
nav: [
{ text: 'vue', link: '/' },
{ text: 'css', link: '/blog/' },
{ text: 'js', link: '/zhihu/' },
{
text: 'github',
// 这里是下拉列表展现形式。
items: [
{ text: 'focus-outside', link: 'https://github.com/txs1992/focus-outside' },
{ text: 'stylus-converter', link: 'https://github.com/txs1992/stylus-converter' }
]
}
],
// 为以下路由添加侧边栏
sidebar: ['/', '/git', '/vue']
}
}多级配置对于多级目录的侧边栏,我们需要用使用对象描述的写法,下面的 /git/ 表示在 git 目录,默认指向 /git/README.md 文件。docs
├── README.md
├── vue
│ ├─ README.md
│ ├─ one.md
│ └─ two.md
└── css
├─ README.md
├─ three.md
└─ four.md
// dcos/.vuepress/config.js
module.exports = {
themeConfig: {
sidebar: {
'/vue/': [
'one',
'two'
],
'/css/': [
'three',
'four'
]
}
}
}8.3 Stylingpalette.styl如果要对默认预设的样式进行简单的替换,或者定义一些变量供以后使用,你可以创建一个 .vuepress/styles/palette.styl 文件。// 颜色
$accentColor = #3eaf7c
$textColor = #2c3e50
$borderColor = #eaecef
$codeBgColor = #282c34
$arrowBgColor = #ccc
$badgeTipColor = #42b983
$badgeWarningColor = darken(#ffe564, 35%)
$badgeErrorColor = #DA5961
// 布局
$navbarHeight = 3.6rem
$sidebarWidth = 20rem
$contentWidth = 740px
$homePageWidth = 960px
// 响应式变化点
$MQNarrow = 959px
$MQMobile = 719px
$MQMobileNarrow = 419px注意:你应该只在这个文件中定义变量。因为 palette.styl 将在根的 stylus 配置文件的末尾引入,作为配置,它将被多个文件使用,所以一旦你在这里写了样式,你的样式就会被多次复制。index.stylVuePress 提供了一种添加额外样式的简便方法。你可以创建一个 .vuepress/styles/index.styl 文件。这是一个 Stylus 文件,但你也可以使用正常的 CSS 语法。.content {
font-size 30px
}提示::为什么不能把 palette.styl 和 index.styl 合并到一个 API?8.4 Front Matter配置任何包含 YAML front matter 的 Markdown 文件都将由 gray-matter 处理。front matter 必须是 markdown 文件中的第一部分,并且必须采用在三点划线之间书写的有效的 YAML。第9章 VuePress 中注册组件在 VuePress 中编写 Vue 代码,和我们通常的编写单文件的方式一致,有些时候我们有可能需要使用 Vue 的 UI 组件库。例如 Element,Mint 等,通常我们在项目中使用这些 UI 组件库的时候,我们都会在 main.js 或 botostrap.js 文件中统一注册。好在 VuePress 中也支持这种功能,我们可以通过创建一个 .vuepress/enhanceApp.js 文件来做一些应用级别的配置,这个文件 exprot default 一个钩子函数,在这个钩子中你可以做一些特殊处理,例如添加全局路由钩子,注册外部组件库。// .vuepress/enhanceApp.js
// 全局注册 Element 组件库
import Vue from 'vue'
import Element from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
export default ({
Vue,
options,
router
}) => {
Vue.use(Element)
}在 Vue 正常开发中,有些时候我们可能会需要做一些自定义的组件,在 VuePress 中我们可以在 .vuepress/components 目录中编写我们的自定义组件,VuePress 在编译时遍历该目录中所有的 *.vue 文件,并见它们注册为全局组件。// 注册一个自定义组件
// docs/.vuepress/components/Hello.vue
<template>
<div class="xz-hello">Hello VuePress Demo</div>
</template>这样我们在 Markdown 文件编写 Vue 代码的时候就不需要注册注册这些组件,边可以直接在 Markdown 中使用了。// docs/.vuepress/vue/README.md
<template>
<div class="test-demo">
{{ msg }}
<xz-hello></xz-hello>
<el-button>button</el-button>
</div>
</template>
<script>
export default {
data () {
return {
msg: 'Hello VuePress!'
}
}
}
</script>第10章 VuePress 主题theme当这里根据VuePress的默认主题配置,如果默认主题无法满足需求,需要配置。类型: string默认值: undefinedtheme使用来自依赖的主题配置一个主题可以在以 vuepress-theme-xxx 的形式发布到 npm,你可以这样使用它:module.exports = {
theme: 'vuepress-theme-xx'
}
# 主题的缩写
module.exports = {
theme: 'xx'
}这也适用于 Scoped Packages的官方主题module.exports = {
theme: '@org/vuepress-theme-xxx', // 或者一个官方主题: '@vuepress/theme-xxx'
}
# 官方主题的缩写
module.exports = {
theme: '@org/xxx', // 或者一个官方主题: '@vuepress/xxx'
}themeConfig为当前的主题提供一些配置,这些选项依赖于你正在使用的主题。包含导航栏、侧边栏、搜索框等。类型: Object默认值: {}开发主题首先在docs下的.vuepress/theme 目录,接着创建一个 Layout.vue 文件当前的 .md 文件渲染的内容,可以作为一个独特的全局组件 来使用,你可能想要它显示在页面中的某个地方。<template>
<div class="theme-container">
<Content/>
</div>
</template>然后找到该目录 /node_modules/@vuepress/theme-default把 theme-default 中的所有文件复制到刚才的 theme中,注意不是把 theme-default文件夹复制过去或者使用指令:vuepress eject将默认主题复制到 .vuepress/theme 目录,以供自定义。现在,你可以自己修改主题了2. 加入第三方UI框架集成ElementUI进入项目根目录,使用命令 yarn add element-ui -S或者npm i element-ui -S然后在 .vuepress文件夹下的 enhanceApp.js 进行配置,如果没有 enhanceApp.js 文件则新建这个文件> enhanceApp.js
import Element from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
export default ({
Vue, // the version of Vue being used in the VuePress app
options, // the options for the root Vue instance
router, // the router instance for the app
siteData // site metadata
}) => {
Vue.use(Element, { size: 'small', zIndex: 3000 });
}主题说明文档vuepress-theme-antdocsvuepress-theme-reco第11章 VuePress 插件插件通常会为 VuePress 添加全局功能。插件的范围没有限制。使用插件你可以通过在 .vuepress/config.js 中做一些配置来使用插件:module.exports = {
plugins: [
require('./my-plugin.js')
]
}使用来自依赖的插件一个插件可以在以 vuepress-plugin-xxx 的形式发布到 npm,你可以这样使用它:module.exports = {
plugins: [ 'vuepress-plugin-xx' ]
}
复制代码插件的缩写如果你的插件名以 vuepress-plugin- 开头,你可以使用缩写来省略这个前缀module.exports = {
plugins: [ 'xxx' ]
}
# 和下面等价
module.exports = {
plugins: [ 'vuepress-plugin-xxx' ]
}
# 这也适用于 Scoped Packages:
module.exports = {
plugins: [ '@org/vuepress-plugin-xxx', '@vuepress/plugin-xxx' ]
}
等价于:
module.exports = {
plugins: [ '@org/xxx', '@vuepress/xxx' ]
}插件的选项Babel 式插件可以通过在配置内的数组中封装名称和选项对象来指定选项,由于这种风格和 babeld Plugin/Preset Options 一致,我们称之为"Babel 风格"。module.exports = {
plugins: [
[
'vuepress-plugin-xxx',
{ /* options */ }
]
]
}
# 禁止插件
module.exports = {
plugins: [
[ 'xxx', false ] // disabled.
]
}对象式VuePress 也提供了一种更简单的方式来使用来自依赖的插件:module.exports = {
plugins: {
'xxx': { /* options */ }
}
}
# 禁止插件
module.exports = {
plugins: {
'xxx': false // disabled.
}
}开发插件一个插件应该导出一个普通的 JavaScript 对象(样例1),如果插件需要接受配置选项,那么它可以是一个返回对象的函数(样例2),这个函数接受插件的配置选项为第一个参数、包含编译期上下文的 ctx 对象作为第二个参数。一个 VuePress 插件应该是一个 CommonJS 模块,因为 VuePress 插件运行在 Node 端。# 样例1:
module.exports = {
// ...
}
# 样例2
module.exports = (options, ctx) => {
return {
// ...
}
}VuePress 自带的插件@vuepress/plugin-last-updated@vuepress/plugin-register-components默认主题自带的插件@vuepress/plugin-active-header-links@vuepress/plugin-nprogress@vuepress/plugin-searchvuepress-plugin-containervuepress-plugin-smooth-scroll官方插件@vuepress/plugin-active-header-links插件说明文档@vuepress/plugin-blog @vuepress/plugin-active-header-links @vuepress/plugin-back-to-top 第12章 VuePress 部署文档放置在项目的 docs 目录中;使用的是默认的构建输出位置;VuePress 以本地依赖的形式被安装到你的项目中并且配置了如下的 npm scripts:{
"scripts": {
"docs:dev": "vuepress dev docs",
"docs:build": "vuepress build docs"
}
}第二部分 认识 Nuxt第13章 Nuxt是什么VuePress 能做的事情,Nuxt 理论上确实能够胜任,但 Nuxt 是为构建应用程序而生的,而 VuePress 则专注在以内容为中心的静态网站上,同时提供了一些为技术文档定制的开箱即用的特性。第三部分认识 Docsify / Docute第14章 Docsify / Docute是什么Docsify / Docute都是基于 Vue,然而它们都是完全的运行时驱动,因此对 SEO 不够友好。如果你并不关注 SEO,同时也不想安装大量依赖,它们仍然是非常好的选择第四部分 认识 Hexo第15章 Hexo是什么Hexo 一直驱动着 Vue 的文档 —— 事实上,在把我们的主站从 Hexo 迁移到 VuePress 之前,我们可能还有很长的路要走。Hexo 最大的问题在于他的主题系统太过于静态以及过度地依赖纯字符串,而我们十分希望能够好好地利用 Vue 来处理我们的布局和交互,同时,Hexo 的 Markdown 渲染的配置也不是最灵活的。第五部分 认识 GitBook第16章 GitBook是什么GitBook和Vuepress是专注于写文档工具。GitBook 最大的问题在于当文件很多时,每次编辑后的重新加载时间长得令人无法忍受。它的默认主题导航结构也比较有限制性,并且,主题系统也不是 Vue 驱动的。GitBook 背后的团队如今也更专注于将其打造为一个商业产品而不是开源工具。第六部分 认识 Jekyll第17章 Jekyll 是什么Jekyll 是一个简单的免费的Blog生成工具,类似WordPress。但是和WordPress又有很大的不同,原因是jekyll只是一个生成静态网页的工具,不需要数据库支持。但是可以配合第三方服务,例如discuz。最关键的是jekyll可以免费部署在Github上,而且可以绑定自己的域名。第七部分 认识 Hugo第18章 Hugo是什么Hugo 是 Go 编写的静态网站生成器,速度快,易用,可配置。Hugo 有一个内容和模板目录,把他们渲染到完全的 HTML 网站。
Jetbrains即将推出下一代编辑器:Fleet,对标 VS Code,内含官方申请链接!
大家好,我是鸭哥。 JetBrains 这家公司大家知道吧,推出了很多优秀的开发工具。像我们程序员习惯用的 Intellij IDEA、PyCharm、DataGrip等等 IDE 就都是这家公司开发的。这些 IDE 确实都非常好用、功能特别全,但是相对来说就不是那么的轻量化。于是乎微软就推出了 VS Code 这款通用代码编辑器,既有专业 IDE 工具完整的功能又能保证自身的轻量化,相对来说不会那么的臃肿。有了 VS Code 这个成功的例子,就有程序员在网上给 JetBrains 官方提建议了:啥时候 JetBrains 也会推出一个轻量化的编辑器呢?现在,这个建议实现了!就在昨天 Jetbrains 就在官方宣布了一个新的产品:Fleet,这就是对标 VS Code 推出的。官方表示 Fleet 乃是从头开始构建,使用了使用了 IntelliJ 代码处理引擎,具有分布式 IDE 架构和重新设计的 UI。来,鸭哥我带着大家看看具体有什么新特性。轻量级编辑器首次打开 Fleet ,你就会发现这是一个功能齐全的编辑器,具有语法高亮、代码补全等基础功能。智能化具有智能补全、重构、导航、调试等等功能, IDE 有的它都有。一键点击右上角按钮就能够开启这些功能。分布式架构 JetBrains 官方表示在设计之处就考虑到了该编辑器要满足各种配置和工作流,不仅仅是在自己的电脑上运行使用 Fleet,甚至可以在云端部署代码处理流程。协作性协作功能支持共享编辑器,开发者可以共享终端和调试会话、代码审查等等操作。支持多语言在 Fleet 上面通过插件的方式支持多种编程语言,在 LSP 的帮助下,开发者还可以在 Fleet 中使用其他语言服务。关于这款编辑器,鸭哥我是非常期待的,给大家截取部分网友评论。目前还处于申请体验权限的阶段,大家可以到官网填写申请表进行申请,加入 Fleet Explorer。官方申请链接:https://www.jetbrains.com/fleet/preview/
动手学深度学习(十四) NLP注意力机制和Seq2seq模型(下)
引入注意力机制的Seq2seq模型本节中将注意机制添加到sequence to sequence 模型中,以显式地使用权重聚合states。下图展示encoding 和decoding的模型结构,在时间步为t的时候。此刻attention layer保存着encodering看到的所有信息——即encoding的每一步输出。在decoding阶段,解码器的时刻的隐藏状态被当作query,encoder的每个时间步的hidden states作为key和value进行attention聚合. Attetion model的输出当作成上下文信息context vector,并与解码器输入拼接起来一起送到解码器:Image Name下图展示了seq2seq机制的所以层的关系,下面展示了encoder和decoder的layer结构Image Nameimport sys
sys.path.append('/home/kesci/input/d2len9900')
import d2l解码器由于带有注意机制的seq2seq的编码器与之前章节中的Seq2SeqEncoder相同,所以在此处我们只关注解码器。我们添加了一个MLP注意层(MLPAttention),它的隐藏大小与解码器中的LSTM层相同。然后我们通过从编码器传递三个参数来初始化解码器的状态:the encoder outputs of all timesteps:encoder输出的各个状态,被用于attetion layer的memory部分,有相同的key和valuesthe hidden state of the encoder’s final timestep:编码器最后一个时间步的隐藏状态,被用于初始化decoder 的hidden statethe encoder valid length: 编码器的有效长度,借此,注意层不会考虑编码器输出中的填充标记(Paddings)在解码的每个时间步,我们使用解码器的最后一个RNN层的输出作为注意层的query。然后,将注意力模型的输出与输入嵌入向量连接起来,输入到RNN层。虽然RNN层隐藏状态也包含来自解码器的历史信息,但是attention model的输出显式地选择了enc_valid_len以内的编码器输出,这样attention机制就会尽可能排除其他不相关的信息。class Seq2SeqAttentionDecoder(d2l.Decoder):
def __init__(self, vocab_size, embed_size, num_hiddens, num_layers,
dropout=0, **kwargs):
super(Seq2SeqAttentionDecoder, self).__init__(**kwargs)
self.attention_cell = MLPAttention(num_hiddens,num_hiddens, dropout)
self.embedding = nn.Embedding(vocab_size, embed_size)
self.rnn = nn.LSTM(embed_size+ num_hiddens,num_hiddens, num_layers, dropout=dropout)
self.dense = nn.Linear(num_hiddens,vocab_size)
def init_state(self, enc_outputs, enc_valid_len, *args):
outputs, hidden_state = enc_outputs
# print("first:",outputs.size(),hidden_state[0].size(),hidden_state[1].size())
# Transpose outputs to (batch_size, seq_len, hidden_size)
return (outputs.permute(1,0,-1), hidden_state, enc_valid_len)
#outputs.swapaxes(0, 1)
def forward(self, X, state):
enc_outputs, hidden_state, enc_valid_len = state
#("X.size",X.size())
X = self.embedding(X).transpose(0,1)
# print("Xembeding.size2",X.size())
outputs = []
for l, x in enumerate(X):
# print(f"\n{l}-th token")
# print("x.first.size()",x.size())
# query shape: (batch_size, 1, hidden_size)
# select hidden state of the last rnn layer as query
query = hidden_state[0][-1].unsqueeze(1) # np.expand_dims(hidden_state[0][-1], axis=1)
# context has same shape as query
# print("query enc_outputs, enc_outputs:\n",query.size(), enc_outputs.size(), enc_outputs.size())
context = self.attention_cell(query, enc_outputs, enc_outputs, enc_valid_len)
# Concatenate on the feature dimension
# print("context.size:",context.size())
x = torch.cat((context, x.unsqueeze(1)), dim=-1)
# Reshape x to (1, batch_size, embed_size+hidden_size)
# print("rnn",x.size(), len(hidden_state))
out, hidden_state = self.rnn(x.transpose(0,1), hidden_state)
outputs.append(out)
outputs = self.dense(torch.cat(outputs, dim=0))
return outputs.transpose(0, 1), [enc_outputs, hidden_state,
enc_valid_len]现在我们可以用注意力模型来测试seq2seq。为了与第9.7节中的模型保持一致,我们对vocab_size、embed_size、num_hiddens和num_layers使用相同的超参数。结果,我们得到了相同的解码器输出形状,但是状态结构改变了。encoder = d2l.Seq2SeqEncoder(vocab_size=10, embed_size=8,
num_hiddens=16, num_layers=2)
# encoder.initialize()
decoder = Seq2SeqAttentionDecoder(vocab_size=10, embed_size=8,
num_hiddens=16, num_layers=2)
X = torch.zeros((4, 7),dtype=torch.long)
print("batch size=4\nseq_length=7\nhidden dim=16\nnum_layers=2\n")
print('encoder output size:', encoder(X)[0].size())
print('encoder hidden size:', encoder(X)[1][0].size())
print('encoder memory size:', encoder(X)[1][1].size())
state = decoder.init_state(encoder(X), None)
out, state = decoder(X, state)
out.shape, len(state), state[0].shape, len(state[1]), state[1][0].shapebatch size=4
seq_length=7
hidden dim=16
num_layers=2
encoder output size: torch.Size([7, 4, 16])
encoder hidden size: torch.Size([2, 4, 16])
encoder memory size: torch.Size([2, 4, 16])
(torch.Size([4, 7, 10]), 3, torch.Size([4, 7, 16]), 2, torch.Size([2, 4, 16]))训练与第9.7.4节相似,通过应用相同的训练超参数和相同的训练损失来尝试一个简单的娱乐模型。从结果中我们可以看出,由于训练数据集中的序列相对较短,额外的注意层并没有带来显著的改进。由于编码器和解码器的注意层的计算开销,该模型比没有注意的seq2seq模型慢得多。import zipfile
import torch
import requests
from io import BytesIO
from torch.utils import data
import sys
import collections
class Vocab(object): # This class is saved in d2l.
def __init__(self, tokens, min_freq=0, use_special_tokens=False):
# sort by frequency and token
counter = collections.Counter(tokens)
token_freqs = sorted(counter.items(), key=lambda x: x[0])
token_freqs.sort(key=lambda x: x[1], reverse=True)
if use_special_tokens:
# padding, begin of sentence, end of sentence, unknown
self.pad, self.bos, self.eos, self.unk = (0, 1, 2, 3)
tokens = ['', '', '', '']
else:
self.unk = 0
tokens = ['']
tokens += [token for token, freq in token_freqs if freq >= min_freq]
self.idx_to_token = []
self.token_to_idx = dict()
for token in tokens:
self.idx_to_token.append(token)
self.token_to_idx[token] = len(self.idx_to_token) - 1
def __len__(self):
return len(self.idx_to_token)
def __getitem__(self, tokens):
if not isinstance(tokens, (list, tuple)):
return self.token_to_idx.get(tokens, self.unk)
else:
return [self.__getitem__(token) for token in tokens]
def to_tokens(self, indices):
if not isinstance(indices, (list, tuple)):
return self.idx_to_token[indices]
else:
return [self.idx_to_token[index] for index in indices]
def load_data_nmt(batch_size, max_len, num_examples=1000):
"""Download an NMT dataset, return its vocabulary and data iterator."""
# Download and preprocess
def preprocess_raw(text):
text = text.replace('\u202f', ' ').replace('\xa0', ' ')
out = ''
for i, char in enumerate(text.lower()):
if char in (',', '!', '.') and text[i-1] != ' ':
out += ' '
out += char
return out
with open('/home/kesci/input/fraeng6506/fra.txt', 'r') as f:
raw_text = f.read()
text = preprocess_raw(raw_text)
# Tokenize
source, target = [], []
for i, line in enumerate(text.split('\n')):
if i >= num_examples:
break
parts = line.split('\t')
if len(parts) >= 2:
source.append(parts[0].split(' '))
target.append(parts[1].split(' '))
# Build vocab
def build_vocab(tokens):
tokens = [token for line in tokens for token in line]
return Vocab(tokens, min_freq=3, use_special_tokens=True)
src_vocab, tgt_vocab = build_vocab(source), build_vocab(target)
# Convert to index arrays
def pad(line, max_len, padding_token):
if len(line) > max_len:
return line[:max_len]
return line + [padding_token] * (max_len - len(line))
def build_array(lines, vocab, max_len, is_source):
lines = [vocab[line] for line in lines]
if not is_source:
lines = [[vocab.bos] + line + [vocab.eos] for line in lines]
array = torch.tensor([pad(line, max_len, vocab.pad) for line in lines])
valid_len = (array != vocab.pad).sum(1)
return array, valid_len
src_vocab, tgt_vocab = build_vocab(source), build_vocab(target)
src_array, src_valid_len = build_array(source, src_vocab, max_len, True)
tgt_array, tgt_valid_len = build_array(target, tgt_vocab, max_len, False)
train_data = data.TensorDataset(src_array, src_valid_len, tgt_array, tgt_valid_len)
train_iter = data.DataLoader(train_data, batch_size, shuffle=True)
return src_vocab, tgt_vocab, train_iterembed_size, num_hiddens, num_layers, dropout = 32, 32, 2, 0.0
batch_size, num_steps = 64, 10
lr, num_epochs, ctx = 0.005, 500, d2l.try_gpu()
src_vocab, tgt_vocab, train_iter = load_data_nmt(batch_size, num_steps)
encoder = d2l.Seq2SeqEncoder(
len(src_vocab), embed_size, num_hiddens, num_layers, dropout)
decoder = Seq2SeqAttentionDecoder(
len(tgt_vocab), embed_size, num_hiddens, num_layers, dropout)
model = d2l.EncoderDecoder(encoder, decoder)训练和预测d2l.train_s2s_ch9(model, train_iter, lr, num_epochs, ctx)epoch 50,loss 0.104, time 54.7 sec
epoch 100,loss 0.046, time 54.8 sec
epoch 150,loss 0.031, time 54.7 sec
epoch 200,loss 0.027, time 54.3 sec
epoch 250,loss 0.025, time 54.3 sec
epoch 300,loss 0.024, time 54.4 sec
epoch 350,loss 0.024, time 54.4 sec
epoch 400,loss 0.024, time 54.5 sec
epoch 450,loss 0.023, time 54.4 sec
epoch 500,loss 0.023, time 54.7 secfor sentence in ['Go .', 'Good Night !', "I'm OK .", 'I won !']:
print(sentence + ' => ' + d2l.predict_s2s_ch9(
model, sentence, src_vocab, tgt_vocab, num_steps, ctx))Go . => va !
Good Night ! => !
I'm OK . => ça va .
I won ! => j'ai gagné !
动手学深度学习(十四) NLP注意力机制和Seq2seq模型(上)
注意力机制在“编码器—解码器(seq2seq)”⼀节⾥,解码器在各个时间步依赖相同的背景变量(context vector)来获取输⼊序列信息。当编码器为循环神经⽹络时,背景变量来⾃它最终时间步的隐藏状态。将源序列输入信息以循环单位状态编码,然后将其传递给解码器以生成目标序列。然而这种结构存在着问题,尤其是RNN机制实际中存在长程梯度消失的问题,对于较长的句子,我们很难寄希望于将输入的序列转化为定长的向量而保存所有的有效信息,所以随着所需翻译句子的长度的增加,这种结构的效果会显著下降。与此同时,解码的目标词语可能只与原输入的部分词语有关,而并不是与所有的输入有关。例如,当把“Hello world”翻译成“Bonjour le monde”时,“Hello”映射成“Bonjour”,“world”映射成“monde”。在seq2seq模型中,解码器只能隐式地从编码器的最终状态中选择相应的信息。然而,注意力机制可以将这种选择过程显式地建模。Image Name注意力机制框架Attention 是一种通用的带权池化方法,输入由两部分构成:询问(query)和键值对(key-value pairs)。. Query , attention layer得到输出与value的维度一致 . 对于一个query来说,attention layer 会与每一个key计算注意力分数并进行权重的归一化,输出的向量则是value的加权求和,而每个key计算的权重与value一一对应。为了计算输出,我们首先假设有一个函数 用于计算query和key的相似性,然后可以计算所有的 attention scores by我们使用 softmax函数 获得注意力权重:最终的输出就是value的加权求和:Image Name不同的attetion layer的区别在于score函数的选择,在本节的其余部分,我们将讨论两个常用的注意层 Dot-product Attention 和 Multilayer Perceptron Attention;随后我们将实现一个引入attention的seq2seq模型并在英法翻译语料上进行训练与测试。import math
import torch
import torch.nn as nnimport os
def file_name_walk(file_dir):
for root, dirs, files in os.walk(file_dir):
# print("root", root) # 当前目录路径
print("dirs", dirs) # 当前路径下所有子目录
print("files", files) # 当前路径下所有非目录子文件
file_name_walk("/home/kesci/input/fraeng6506")dirs []
files ['_about.txt', 'fra.txt']Softmax屏蔽在深入研究实现之前,我们首先介绍softmax操作符的一个屏蔽操作。def SequenceMask(X, X_len,value=-1e6):
maxlen = X.size(1)
#print(X.size(),torch.arange((maxlen),dtype=torch.float)[None, :],'\n',X_len[:, None] )
mask = torch.arange((maxlen),dtype=torch.float)[None, :] >= X_len[:, None]
#print(mask)
X[mask]=value
return Xdef masked_softmax(X, valid_length):
# X: 3-D tensor, valid_length: 1-D or 2-D tensor
softmax = nn.Softmax(dim=-1)
if valid_length is None:
return softmax(X)
else:
shape = X.shape
if valid_length.dim() == 1:
try:
valid_length = torch.FloatTensor(valid_length.numpy().repeat(shape[1], axis=0))#[2,2,3,3]
except:
valid_length = torch.FloatTensor(valid_length.cpu().numpy().repeat(shape[1], axis=0))#[2,2,3,3]
else:
valid_length = valid_length.reshape((-1,))
# fill masked elements with a large negative, whose exp is 0
X = SequenceMask(X.reshape((-1, shape[-1])), valid_length)
return softmax(X).reshape(shape)masked_softmax(torch.rand((2,2,4),dtype=torch.float), torch.FloatTensor([2,3]))tensor([[[0.5423, 0.4577, 0.0000, 0.0000],
[0.5290, 0.4710, 0.0000, 0.0000]],
[[0.2969, 0.2966, 0.4065, 0.0000],
[0.3607, 0.2203, 0.4190, 0.0000]]])超出2维矩阵的乘法 和 是维度分别为 和的张量,进行 次二维矩阵乘法后得到 , 维度为 。torch.bmm(torch.ones((2,1,3), dtype = torch.float), torch.ones((2,3,2), dtype = torch.float))tensor([[[3., 3.]],
[[3., 3.]]])点积注意力The dot product 假设query和keys有相同的维度, 即 . 通过计算query和key转置的乘积来计算attention score,通常还会除去 减少计算出来的score对维度𝑑的依赖性,如下假设 有 个query, 有 个keys. 我们可以通过矩阵运算的方式计算所有 个score:现在让我们实现这个层,它支持一批查询和键值对。此外,它支持作为正则化随机删除一些注意力权重.# Save to the d2l package.
class DotProductAttention(nn.Module):
def __init__(self, dropout, **kwargs):
super(DotProductAttention, self).__init__(**kwargs)
self.dropout = nn.Dropout(dropout)
# query: (batch_size, #queries, d)
# key: (batch_size, #kv_pairs, d)
# value: (batch_size, #kv_pairs, dim_v)
# valid_length: either (batch_size, ) or (batch_size, xx)
def forward(self, query, key, value, valid_length=None):
d = query.shape[-1]
# set transpose_b=True to swap the last two dimensions of key
scores = torch.bmm(query, key.transpose(1,2)) / math.sqrt(d)
attention_weights = self.dropout(masked_softmax(scores, valid_length))
print("attention_weight\n",attention_weights)
return torch.bmm(attention_weights, value)测试现在我们创建了两个批,每个批有一个query和10个key-values对。我们通过valid_length指定,对于第一批,我们只关注前2个键-值对,而对于第二批,我们将检查前6个键-值对。因此,尽管这两个批处理具有相同的查询和键值对,但我们获得的输出是不同的。atten = DotProductAttention(dropout=0)
keys = torch.ones((2,10,2),dtype=torch.float)
values = torch.arange((40), dtype=torch.float).view(1,10,4).repeat(2,1,1)
atten(torch.ones((2,1,2),dtype=torch.float), keys, values, torch.FloatTensor([2, 6]))attention_weight
tensor([[[0.5000, 0.5000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,
0.0000, 0.0000]],
[[0.1667, 0.1667, 0.1667, 0.1667, 0.1667, 0.1667, 0.0000, 0.0000,
0.0000, 0.0000]]])
tensor([[[ 2.0000, 3.0000, 4.0000, 5.0000]],
[[10.0000, 11.0000, 12.0000, 13.0000]]])多层感知机注意力在多层感知器中,我们首先将 query and keys 投影到 .为了更具体,我们将可以学习的参数做如下映射 , , and . 将score函数定义.然后将key 和 value 在特征的维度上合并(concatenate),然后送至 a single hidden layer perceptron 这层中 hidden layer 为 ℎ and 输出的size为 1 .隐层激活函数为tanh,无偏置.# Save to the d2l package.
class MLPAttention(nn.Module):
def __init__(self, units,ipt_dim,dropout, **kwargs):
super(MLPAttention, self).__init__(**kwargs)
# Use flatten=True to keep query's and key's 3-D shapes.
self.W_k = nn.Linear(ipt_dim, units, bias=False)
self.W_q = nn.Linear(ipt_dim, units, bias=False)
self.v = nn.Linear(units, 1, bias=False)
self.dropout = nn.Dropout(dropout)
def forward(self, query, key, value, valid_length):
query, key = self.W_k(query), self.W_q(key)
#print("size",query.size(),key.size())
# expand query to (batch_size, #querys, 1, units), and key to
# (batch_size, 1, #kv_pairs, units). Then plus them with broadcast.
features = query.unsqueeze(2) + key.unsqueeze(1)
#print("features:",features.size()) #--------------开启
scores = self.v(features).squeeze(-1)
attention_weights = self.dropout(masked_softmax(scores, valid_length))
return torch.bmm(attention_weights, value)测试尽管MLPAttention包含一个额外的MLP模型,但如果给定相同的输入和相同的键,我们将获得与DotProductAttention相同的输出atten = MLPAttention(ipt_dim=2,units = 8, dropout=0)
atten(torch.ones((2,1,2), dtype = torch.float), keys, values, torch.FloatTensor([2, 6]))tensor([[[ 2.0000, 3.0000, 4.0000, 5.0000]],
[[10.0000, 11.0000, 12.0000, 13.0000]]], grad_fn=<BmmBackward>)总结注意力层显式地选择相关的信息。注意层的内存由键-值对组成,因此它的输出接近于键类似于查询的值。