[二]漫话中文分词:Trie前缀树

在上一篇文章当中,说到了一些匹配的算法,但是算法有了,还得需要一个高效的数据结构,不能只是通过[‘中国人’, ‘中东人’]等结构来进行存放,可以想象一下,如果有几十万的词,那么这个列表的占用的内存非常大。

Trie树,也被称为前缀树,该词源自单词retrieval,发音和try相同,Trie树可以为字典分词提供一种高效的分词数据结构,该结构本质上是一种树状数据结构,比如”中国人”、”中东人”三个字符串构造的Trie树为下图,图中能够很清楚的看见,Trie树结构能够很好的节省相同前缀单词所浪费的空间,因为这两个词都是以”中”开头,所以可以使用同一个父辈节点。

1

除此之外,Trie树还对查询的速度有一定的优化,如果以列表存放词来说,如果列表存放的词达到了20万个,那么最坏的情况是你需要匹配的词在存放于最后,那么就相当于要将这20万个词全部遍历,可想而知浪费了非常多的计算资源。

下图为基于同一份10万左右的词典,使用正向最大匹配算法在列表和Trie两种结构上进行分词的运行时间,从下图可以看出来差距非常大。

而Trie树的查找方式则是通过层层查询,而不是直接遍历词典,比如”中国人”,首先会查找第一层中是否有”中”这个字符,如果没有查询到则返回查询失败,如果有则继续查找”中”字符对应的下一层是否有”国”,如果没有则返回查询识别,如果有则继续查找”国”下一层是否有”人”,此时找到存在”人”这个节点,并且该节点标注为蓝色,表明是一个词,所以返回该字符串为一个词。
Continue reading

人工智能基础名词理解

近期在研究相关的自然语言处理,难免会涉及到相关的机器学习等知识,对于非计算机专业的人来说杠开始一脸迷茫,再经过多次的理解和请教后,有了一个大概的认知,这里做一个记录。

人工智能

人工智能是一个比较广泛的概念,这个概念实际上指的是让机器像人一样思考,其最早由计算机科学之父阿兰图灵在1950年的一篇《计算机器与智能》论文中写出“如果电脑能在5分钟能回答由人类测试者提出的一系列的问题,且超过30%回答让测试者误认为人类所答,则电脑通过测试”,这段话也直接启蒙式的开启了人工智能领域的研究。

而“人工智能”一词,第一次出现在1956年,达特茅斯大学召开的学术会议室,由人工智能之父约翰·麦卡锡首次提出。

通常人工智能被分为弱人工智能和强人工智能,前者可以让机器有一定程度的学习、理解和推理能力,后者则是由自适应能力,比如解决一些之前没有遇见过的问题,我们常在电影里看见的机器人就是一种强人工智能。

机器学习

机器学习为人工智能的一个研究分支,也可以理解为弱人工智能的一种实现,而机器学习做的事情是让机器取模拟和实现人类的学习行为,以获得新的技能和知识。

人工智能领域的先驱Arthur Samuel在1959年给出的机器学习定义为“不直接编程,却能赋予计算机提供能力的方法”,而美国工程院院士Tom Mitchell则给出了一个更明确的含义,指出“机器学习是通过某项人物的经验数据提高了在该人物上的能力”。

机器学习最基本的是利用给出的算法来解析数据,从中学习到一定规则(模式)得到经验,并利用学习到的经验对类似的问题作出预测和判断。
Continue reading

[一]漫话中文分词:最大匹配,双向最大,最小词数

中文分词是指将文本拆分为单词的过程,而结果集合连接起来是等于原始的文本,而中文分词一直作为NLP领域的比较重要的领域,而大多数的文本挖掘都是以分词为基础,但中文不同于英文,英文每个单词是用空格分隔,整体语义上相对于中文难度低很多。

而业务上一直有中文分词的需求,但是之前因为在忙于另外一个项目,所以一直没有研究。

近期稍空闲开始研究了相关的中文分词算法,发现中文分词总体算比较成熟,但是其中对于未登录词或者某个特定专业领域文本大部分算法分词的结果不尽人意,需要结合多种算法或者人工词典才能达到稍微好一点的效果。

中文分词的方式一共有两种,分别为:

  1. 词典分词:如正向最大匹配算法、反向最大匹配算法、双向最大匹配算法、最少词数法等
  2. 字标注分词:如HMM(隐马尔可夫)模型等

而这几种方式很难说出谁好谁坏,比如词典分词的方式速度非常快,但对于未登录词的识别又不太好,而HMM和Pkuseg都能识别部分未登录词,但是运行速度又降下来了,这对于在实际应用场景当中是非常致命的问题,所以最大的优解就是集各家所长,比如结巴分词就使用了词典分词算法识别能识别的词,而不能识别的则继续使用了HMM模型来处理。

词典分词

基于词典的分词算法实际上就是对于类似字典的数据结构进行查询,对于未在词典内的词识别较弱和交集型歧义理解能力也较弱,比如“结婚的和尚未结婚的”,理想的情况是”结婚/的/和/尚未/结婚/的”,而实际中则会被分词为”结婚/的/和尚/未/结婚/的”。

但好在词典分词的速度则非常快,词典分词目前已有非常成熟高效的解决方案,并且有非常多的工具来帮你实现相关的高效数据结构和查询方式,比如Trie树AC自动机,但在这里为了方便理解和记录,只采用了尽可能简单的方式来记录其几种算法的实现和原理。
Continue reading

条件概率

最近在了解朴素贝叶斯定理,发现自己对于这块的知识欠缺较多,在阅读一些关于贝叶斯文章的时候整理出来了非常多的名词,其中条件概率最为重要,所以也单独拿出一篇文章来记录。

本人不是相关专业,所以尽可能的查阅相关的资料并以自己能理解的方式进行记录,如有不专业或者问题之处,还请嘴下留情。

样本空间(Ω)

样本空间通常指实验或随机所有可能的集合,我们常在说一个概率的时候,实际上是默认忽略掉了样本空间,比如说事件A的概率,实际上指样本空间中,事件A的数量与样本空间的占比。

比如丢硬币,硬币只有正面和反面,那么硬币的样本空间则为{正面,反面},这个时候常说的正面的概率为二分之一,实际指的是正面事件的数量与样本空间的占比,也就是1/2。

再比如说丢骰子,一个骰子有6种可能,分别对应1-6不同的数值,那么丢骰子的样本空间则为{1,2,3,4,5,6},这个时候丢到5个事件概率则为数字5在样本空间出现的次数与样本空间总数的占比。

独立事件

独立事件是指不受过去已发生的事件而影响的事件,典型的例子就是抛硬币,不管你抛多少次硬币始终正面或反面的概率为0.5,而该硬币的样本空间如下:

独立事件的概率计算公式为如下:

事件发生的概率(P) = 事件在样本空间中的数量 / 样本空间的事件总数

比如用抛硬币的例子,计算正面的概率则为:

而除了单个独立事件,有些时候也会求多个独立事件的概率,而多个独立事件的概率则是每个独立事件发生的概率的积。

比如掷3次骰子都为6的概率是多少?需要注意因为掷骰子是一个独立事件,即每次掷的骰子样本空间都一样,并且没有因为第一次掷骰子的结果会影响到下一次。

骰子的样本空间为下,从中能够得到单次掷骰子为6的概率为1/6:

而这个时候只需要将三次掷骰子的概率相乘就得到了三次都为6的概率:

Continue reading

连续数据和离散数据

连续数据和离散数据在如今的机器学习当中非常常见,作为一个非专科的人,如果只是奔着如何去使用相关的机器学习框架,到可以简单了解即可使用。而如果想要深入了解,还得下一番功夫来补习一下相关的功课。

文章内容是我的学习笔记,既然是学习笔记所以主观意识就比较强烈,内容在写这篇文章的时候不一定正确。

统计学中,将一种类型的数据总称为变量,而变量的数据称为观测,而变量的具体取值为观测值,比如下面的数据中,age和name都是变量,而18和’大红’都具体的取值被称为观测值。

age,name
18,’大红’
21,’小花’

同理,在统计学中,离散数据也被称为离散变量,连续数据也被称为连续变量,而如何区分两种变量的区别?

连续变量可以理解为取值范围在理论上是连续不断的,而离散变量则可以理解为取值范围是间断不连续的,他们之间的区别并无数量之分,都是无穷个。

比如家庭数量人口只有1、2、3、4个人口,不可能为1.2、1.8、2.4这样来表示人口,所以家庭人口是离散变量。

而年龄取值上通常为了方便而说是18岁、17岁、30岁,但是如果按照实际取值,则可以取为18.32、17.55、30.67岁,17.55岁则表示年龄为17岁6个月18天,而且出生的时间还可以精确到小时、分、秒等单位,所以年龄为连续变量。

参考资料

  1. 关于连续和离散的理解
  2. 定量和定性变量、连续和离散变量,到底怎么分?
  3. 图解概率笔记:葉丙成概率公开课

R语言安装tidyverse

最近因为使用R时载入的数据量非常大,而工作机内存仅仅只有16G,而添加内存在工作机上会导致资源的浪费,因为自己并不是每次都会使用到如此大的内存,所以就琢磨着组装了一台配置较好的机器,并使用了Ubuntu系统和RstudioServer,每个人都可以开通一个账号来共享这台机器的资源。

但是问题随之而来,在Windows上使用Rstudio安装包几乎没有出现什么问题,但是Linux上则需要手动安装许多的包,所以这里就记录一下。

报错1
ERROR: dependencies ‘httr’, ‘rvest’, ‘xml2’ are not available for package ‘tidyverse’

解决方法:

apt install libxml2-dev
报错2
ERROR: dependency ‘openssl’ is not available for package ‘httr’

解决方法:

apt install libssl-dev 

在解决报错2的时候,可能会出现如下兼容性报错:

libssl-dev : Depends: libssl1.1 (= 1.1.1-1ubuntu2.1~18.04.6) but 1.1.1f-1ubuntu2 is to be installed

可以按照下列的命令安装相应对应的包:

apt install libssl1.1=1.1.1-1ubuntu2.1~18.04.6

参考文档

  1. tidyverse Issues
  2. libssl-dev

R语言连接数据库

最近使用Rstudio Server从数据库获取数据次数越来越多,期间也涉及到了Mysql、SQL server数据库,并且在安装过程中也出现了很多的问题,这里做一个记录和总结,本文为笔记,并不是相关的教程,请见谅。

Mysql/MariaDB

连接mysql和MariaDB数据库可以使用的包有两个,分别为"RMySQL"和"RMariaDB",因为MariaDB数据库是完全兼容mysql,所以这两个包是通用mysql和MariaDB数据库。

而目前通过"RMySQL"包的Github作者介绍,该包将逐渐被"RMariaDB"包被淘汰,所以在安装的时候尽量使用"RMariaDB"来连接mysql。

使用下面的命令来安装这两个包:

install.packages('RMariaDB')

install.packages('RMySQL')

如果安装过程中报错提示如下:

-----------------------------[ ANTICONF ]-----------------------------
Configure could not find suitable mysql/mariadb client library. Try installing:
 * deb: libmariadbclient-dev | libmariadb-client-lgpl-dev (Debian, Ubuntu)
 * rpm: mariadb-connector-c-devel | mariadb-devel | mysql-devel (Fedora, CentOS, RHEL)
 * csw: mysql56_dev (Solaris)
 * brew: mariadb-connector-c (OSX)
If you already have a mysql client library installed, verify that either
mariadb_config or mysql_config is on your PATH. If these are unavailable
you can also set INCLUDE_DIR and LIB_DIR manually via:
R CMD INSTALL --configure-vars='INCLUDE_DIR=... LIB_DIR=...'
--------------------------[ ERROR MESSAGE ]----------------------------
<stdin>:1:10: fatal error: mysql.h: No such file or directory
compilation terminated.
-----------------------------------------------------------------------

则可以根据提示安装相应系统的包

apt install libmariadbclient-dev

然后使用下列命令建立连接

library("RMariaDB")
myConn = dbConnect(MariaDB(), dbname = 'testdb', host = '127.0.0.1',
                            user = 'root', password = 'password')

Continue reading

Git学习笔记

Git是一个版本控制系统(VCS,全称为Version control System),而源码控制管理(SCM,Source Control Manager)也是VCS的另外一种称呼。

GIt主要的功能在于记录和管理开发人员对软件代码所做的更改,GIT一共有三个区域,分别为工作区,缓存区,仓库,下面有分别对该三个区的解释。

💎术语

📌Commit:用于提交快照,就像游戏保存进度点一样,而该快照将会供你所用,可以随时回顾该快照的中的所有文件内容。

📌Repository/repo:仓库用于来保存快照的地方,同时也可以用于向其他人展示你的代码,或者仅给自己用来回顾历史版本

📌Working Directory:工作目录是指当前操作的目录,比如我在/var下操作一个1.txt文件,那么/var就是工作目录,而你提交Git的那个目录,就是当前工作区。

📌Staging Area / Staging Index / Index:缓存区(暂存区)是用于保存即将提交到仓库的文件,而没有进行commit提交的数据。既使用了add但是没有使用commit命令的那一次版本都处于缓存区中。

add  HEAD  stage  commit  master

📌Checkout:Checkout操作会将上一个版本覆盖当前工作区,即撤销更改,比如”git checkout test.txt”会将上一个版本的test文件覆盖到当前工作区。 checkout有两种方式,如果当前缓存区有数据,就拉去缓存区数据恢复,如果没有即从仓库进行拉去恢复。

📌SHA:SHA全称为”安全散列算法”,其由40个字符组成的字符串,其中包含了(0-9和a-f),GIT每次提交的ID就是由SHA生成的字符串为ID,大致为’e2adf8ae3e2e4ed40add75cc44cf9d0a869afeb6’这样的字符串。

📌Branch:分支即是从主线分出一个新分支,也可以说是从主线发散,这样的开发方式可以在不改变主线的情况下继续,比如一个游戏,一边是你预计的路线,一边是冒险的路线,这个时候你可能决定冒险,然后你先保存点,然后创建一条冒险的分支,进行冒险,这样,当你冒险完了,你还可以回到正常路线中。

💎配置

初次使用Git,需要对Git进行配置才能进行使用,一般配置是设置用户名,邮件,并可以配置一些其他的选项。

除此之外,还可以为Git配置默认的编辑器,比如下面代码展示了设置三种编辑器的配置方式:

Atom编辑器设置
git config –global core.editor “atom –wait”
Sublime Text 设置
git config –global core.editor “‘/Applications/Sublime Text 2.app/Contents/SharedSupport/bin/subl’ -n -w”
VSCode设置
git config –global core.editor “code –wait”

💎命令

🔵 git init:该命令可以在本地创建一个新的仓库

🔵 git clone:该命令可以将一个仓库克隆和复制到本地计算机

🔵 git status:该命令可以来查看仓库的状态

🔵 git log:该命令可以显示有关现有提交的信息

🔵 git log –oneline:可以将历史提交信息输出在一行

🔵 git log –stat:可以显示历史提交中更改的文件,以及添加或删除的行数(stat是统计的缩写)

🔵 git log –patch/-p:该命令可以显示其某个文件被修改的详细内容是什么

🔵 git log -w:该命令将忽略空白处的变化

🔵 git show:该命令可以显示有关给定提交的信息

🔵 git add:该命令可以将文件从工作区提交到缓存区

🔵 git commit:该命令让缓存区的文件提交到仓库,如果是简单的注释信息只需要加上-m参数,然后输出简单的信息就可以绕开编辑器进行提交。

🔵 git diff:该命令可以显示两个文件版本之间的差异,该命令实际上与git log -p命令输出的几乎一致

因为大多数的项目提交是非常多的,所以在提交消息标题的部分需要各位的简单简明,以便于别人在查看提交版本的时候使用–oneline命令的方便性,提交的一些建议:

Do

  • do keep the message short (less than 60-ish characters)
  • do explain what the commit does (not how or why!)

Do not

  • do not explain why the changes are made (more on this below)

  • do not explain how the changes are made (that’s what git log -p is for!)

  • do not use the word “and”

    • if you have to use “and”, your commit message is probably doing too many changes – break the changes into separate commits
    • e.g. “make the background color pink and increase the size of the sidebar”

The best way that I’ve found to come up with a commit message is to finish this phrase, “This commit will…”. However, you finish that phrase, use that as your commit message.

Above all, be consistent in how you write your commit messages!

💎Git Ignore

如果在一个项目当中,希望有部分文件不被提交,那么就可以使用特别命令的文件”.gitignore”,将此文件添加到git当前工作目录中,这个文件的意义就是列出希望Git不跟踪的文件及文件夹名称。

💎标记

🔵 git tag:该命令可以为特定提交添加标签,标签可以特定额外信息,标注一些特定的标签,比如标记这是beta版本、版本号等用途

git tag 将会对当前版本进行打标签,该语法如下:

对当前版本提交标签
git tag -a v1.0
对特定版本进行提交标签
git tag -a v1.0 SHAID
绕过默认编辑器
git tag -a v1.0 -m ‘xxxx’

如果需要删除错误的标签可以使用下面的语法,默认情况下tag标签的字符串是唯一的,所以需要删除哪个版本的标签,直接输入标签的内容就可以删除了:

git tag -d v1.0
或者
git tag –delete v1.0

默认情况下git log 不会显示标签信息,如果要显示标签信息需要用下面的语法:

git log –decorate

💎分支

默认情况下,git提供的主分支默认名为master,而每当我们提交一个版本到仓库的时候,master标签将会自动移动到最近的版本上面,分支可以在不同的隔离环境当中对同一个项目进行操作,并且不影响其他分支。分支最主要的目的是允许进行一些修改而不影响其主分支。

🔵 git branch:该命令可以从主线分出一个分支,用于并行开发项目的不同功能,如果没有参数将列出当前仓库的所有分支

🔵 git checkout:该命令可以让你在不同的分支和标签之间进行切换工作区

在GIT中,由HEAD指针来指定当前活跃的分支,即当前所操作的分支,比如下面的图片中HEAD指针指向的是MASTER主分支。

tri MINGW64  S git log  -one line  N/Desktop/tt/newproject  .gitignore file  (maste r)  c99f15e  7299982  6e2df1f  07b3560  (HEAD -> master) Add  chang  er text  Add header to  Initial commit ” /></p>
<p>如果要切换活跃分支,将会使用checkout命令,比如下面的命令是切换到sidebar分支,并且当前本地工作区的文件将会被替换为切换分区的文件,在切换分支之前请记住提交更改的内容,因为checkout命令将用最近分支版本的文件覆盖当前工作区的文件,所以一定记得保存,不然会造成数据的丢失。</p>
<pre><code class=git checkout sidebar

除此之外,checkout命令还可以从指定版本上的位置创建分支,比如下面代码,创建了一个test分支,并从id为42a69f的版本上进行创建。

 git checkout test 42a69f  

删除分支可以通过branch命令的-d 命令,但是需要注意的是,无法删除当前所在工作区的分支,以及无法删除主分支,但是需要注意的是,可以删除一个空分支,但是无法删除一个有提交的分支,如果确认要删除有提交的分支,需要使用大写的-D标志才能删除掉,下面的代码删除了一个空分支sidebar和一个有提交的分支test

空分支删除
git branch -d sidebar
有提交分支删除
git branch -D test

除此之外,还可以使用checkout快速添加分支,比如下面的代码,如果存在head分支则切换至head分支,如果没有则立马创建head分支。

  git checkout -b head  

如果要显示所有分支的信息,那么可以使用all参数,该参数将显示仓库中的所有分支,为了让打印出来的东西更加清晰,可以添加–graph参数,让打印出来的左侧添加指示的线条。

查看所有分支以及详细情况
git log –oneline –decorate –graph –all

💎合并

将分支组合在一起称为合并,Git可以自动将不同分支上的更改合并在一起,这种分支和合并能力使Git 非常强大,可以对分支

进行小的或广泛的更改,然后只需使用Git将这些更改组合在一起。

分支一般分为两种类型,一种为合并,一种为快速合并,合并的时候其HEAD指针指向哪个分区,就会将其需要合并的分支合并到HEAD指向的分区上,比如下图HEAD指针指向的是MASTER分支,这个时候将SIDEBAR合并到当前工作区(即HEAD指向的工作区),这个时候就产生了一个新的版本ID为8,这也是属于正常的合并操作。

而快速合并指的是当需要合并到一起的分支比另一个分支更靠前的时候,这个时候GIT会做一个所谓的快速合并,即将需要合并到一起的分支的指针移动到靠前的分支上面,这个时候GIT则将指针移动到了更靠前的分支位置,比如下图中的SOCIAL-LINK比MASTER分支更靠前,而如果这个时候需要将SOCIAL-LINK分支合并到MASTER,而这个时候GIT则会移动指针到SOCIAL-LIN分支上,来表示新的合并,如下图的图2.

🔵 git merge:该命令可以将不同分支的更改自动合并到一起

🔵 合并发生时,Git会:

  • 🌱看看它要合并的分支
  • 🌱回顾分支的历史记录,找到两个分支在其提交历史记录中都有的单个提交
  • 🌱将在不同分支上更改的代码行组合在一起
  • 🌱提交记录合并

💡合并冲突

大多数的情况下合并并没有什么问题,但是有些情况下是无法自动完全合并的,合并失败被称为合并冲突,如果确实发生了冲突,那么GIT会尽可能的尝试多种组合,并且会留下特殊的标记(比如<<<和>>>),标明需要手动修复的位置。比如当在单独的分支中更改完全相同的行时,会发生合并冲突。

比如这样的例子,Git跟踪文件中的。当在单独的分支中更改完全相同的行时,将发生合并冲突。例如,如果您在alternate-sidebar-style分支上并将侧边栏的标题更改为“关于我”,然后将其更改为另一个分支,并将侧边栏的标题更改为“关于我的信息”,Git会选择哪个标题?你已经改变了两个分支上的标题,所以Git无法知道你真正想要保留哪一个。肯定不会随便随便挑选你!

在正在合并的不同分支上更改相同的一行或多行时,会发生合并冲突。Git会在合并中暂停,告诉您存在冲突,并会告诉您冲突发生在哪个或哪些文件中,要解决文件中的冲突:

  • 找到并删除包含合并冲突指标的所有行
  • 确定要保留什么
  • 保存文件
  • 暂存文件
  • 提交

💎撤销

撤销主要功能可以撤销在于由于一些误操作导致的提交、合并等导致的问题。

🔵git commit –amend:该命令可以更改最近提交的文件,而不会再次多一个提交记录,比如某个提交忘了包含某个文件或者是打错了一个字,就需要调用该命令,该命令可以让你避免再提交一个版本来修改这些很小的错误。

该命令只能修改最近的提交的版本,其大致的步骤为:

  • 🌱编辑文件
  • 🌱保存文件
  • 🌱暂存文件
  • 🌱并运行 git commit –amend

在最后一步执行后,将会覆盖最后的提交版本,如果你没有进行任何的修改,那么默认将打开编辑器修改提交的消息。

比如下面的代码,首先是对文件没有任何修改的提交,该提交只会覆盖版本修改的标题

git commit –amend 打开默认编辑器修改提交注释
git commit –amend -m ‘test’ 将覆盖默认的版本提交注释

下面的代码将修改已提交的最近版本的内容,下面的代码提交前又对index文件进行了一次小改动。

Git commit -m ‘change head text’ 第一次提交版本
Vim index.html 发现有问题,修改了index
Git add . 再次添加到缓存区
Git commit –amend -m ‘change head text’ 再次提交版本,将并修改到最近提交的版本中,而不是单独再提交一次

🔵git revert:git revert是用于“撤销”某一个文件吗,并回到之前的版本,也可以称之为回滚,可以回滚到某个特定的版本上去,并且使用revert回滚,将重新提交一个版本。

比如:先后做了3次提交 commitA、commitB、commitC,分别改了A文件、B文件、C文件,如果在git中查看,当前是commitC:

commitC
commitB
commitA
想要撤销B文件的更改
git revert commitB
想要撤销C文件的更改
git revert commitC

🔵git reset:该命令可以将HEAD指针指向的活跃分区恢复上一个版本或者上上个版本的状态,该命令是为数不多的删除命令之一,该命令会删除当前工作区以及仓库的内容,所以请谨慎。但是即使删除git还是有一个专门的回收站来保存之前的所有记录,保存记录的时间大概为30天,该回收站可以通过git reflog查看。

📌Git reset 可以使用做“Ancestry References”的特殊字符,用来表示比如上个版本或者上上个版本这样的字符,这些字符是:

^ 表示父提交
~ 表示第一个父提交

通过上面的符号,我们可以用来灵活运用,比如像以下都表示为回滚父提交(即上一个提交):

git reset HEAD^
git reset HEAD~
git reset HEAD~1

下面的代码可以回滚祖父提交(即上上个提交):

git reset HEAD^^
git reset HEAD~2

下面的代码可以回滚曾祖父提交(即上上上个提交):

git reset HEAD^^^
git reset HEAD~3

除此之外,由于合并分支会造成父提交可能有两个祖父提交,比如说下图:

该图中HEAD指针所指向的位置的为93c05ca,父级为db7387a,而796ddb0的父级则有两个,第一个为父级为所在的分支为4c9749e,第二个父级则为合并的分支0c5975a,如果要选择796ddb0的第二个父级,那么从当前HEAD表示该为”HEAD^^^2″。下面的代码展示了针对上图做出的一些代码解释:

HEAD^ 是db7e87a提交
HEAD~1 是db7e87a提交
HEAD^^ 是796ddb0提交
HEAD~2 是796ddb0提交
HEAD^^^ 是0c5975a提交
HEAD~3 是0c5975a提交
HEAD^^^2 是4c9749e提交(这是祖父母的(HEAD^^)第二个父(^2))

除此之外,git reset的擦除方式有三种,分别为:

  1. –mixed

  2. –soft

  3. –hard

这三种方式可以通过下面的视频来大致了解一下其工作原理:

git有四个区,分别为仓库、工作目录、缓存区、回收站,当使用git reset命令恢复到上一个版本的时候,其当前版本的文件有可能会存在工作目录、缓存区、回收站的之中的任何一个区,而具体存在哪个区域则是根据git reset的运行方式。

🌱–mixed参数是reset命令默认的参数,当reset至上一个版本的时候,当前版本所提交的更改将保存至工作目录中,就如下图一样

🌱–soft参数当reset至上一个版本的时候,当前版本所提交的更改将保存至缓存区,就如下图一样

🌱–hard参数当reset至上一个版本的时候,当前版本所提交的更改都将移动到回收站,就如下图一样

💡需要注意的是,git reset会清楚当前分支的修改,如果你需要重置的时候,你可以在当前提交上创建一个可用的备份分支,以达到一个备份的作用,比如像下面这样,在重置前创建一个为backup的分支

git  branch backup 

当然如果你需要恢复备份的话,就可以通过上面创建的分支进行合并,如果你工作区有更改,需要使用checkout –命令,该命令会将工作区文件恢复到最近一次的版本,保持文件属于仓库同步内容,然后确保了没有其他意外的情况下就可以合并分支。比如下面的代码,让Index文件删除更改,然后再合并分支进行恢复。

git checkout — index.html
git merge backup

💎Github

github是git的托管服务网站,任何人都可以注册账户将代码上传至github,相当于云端一样,但github更多的作用是远程协作,比如网上有很多开源的项目,如果你想贡献代码,那么用邮件来发送是一个噩梦,所以你可以通过github来创建你自己的分支,又不会影响主项目。而托管在github上面的仓库叫远程仓库。

管理远程仓库git有专门的命令进行管理,分别为:

🔵Git remote:该命令用于管理远程仓库

将分支发送到远程有两种方法:

  1. 将所有分支合并为一个主分支然后提交
  2. 将所有分支共同发送到远程仓库提交
  3. 单独通过git push 远程仓库名称 本地分支名称

Git remote 命令默认情况下是没有返回内容,除非你配置了远程仓库或者clone了仓库,就会有显示,默认情况下显示的为origin,即原点,这里的原地一词被称为“短名称”。短名称只是一种简单易行的方式来引用远程存储库的位置,短名称是当前

储库的本地名称(如本地存储库),如下图:

tri MINGW64  S git remote  origin  N/Desktop/tt/1ighthouse (master)

如果要查看完整的远程仓库git路径则可以使用参数-v,比如”git remote -v”,结果如下图:

N/Desktop/tt/1ighthouse (master)  -NMA2D9K MINGW64  S git remote  lori gin https ://gi thub . com/Goog1ech rome/l i ghthouse (fetch)  ori gin https ://gi thub . com/Goog1ech rome/l i ghthouse (push)

而添加远程仓库则是使用下面的命令,add后面是远程仓库的本地名称

除此之外还可以重置远程仓库的名称,rename

🔵Git push:该命令将修改推送到远程仓库

使用push命令可以将本地分支提交到远程仓库,比如下面的代码将使用远程仓库的短名称提交master分支

git  push origin master  

跟踪分支指的是远程仓库有一个分支同样指向当前的提交,比如下图中的标识符origin/master就是跟踪标识,该标记是origin/master并且被称为跟踪分支。跟踪分支的名称包括远程存储库的短名称以及分支的名称。因此跟踪分支origin/master告诉我们远程origin有一个master指向提交的分支9b7d28f(并包括之前的所有提交9b7d28f)。这非常有用,因为这意味着我们可以在本地跟踪远程存储库的信息!

🔵Git fetch:该命令通过远程仓库进行获取更新

如果你不想自动将本地分支与跟踪分支进行合并,那么可以使用fetch命令,该命令将会把拉取的更新作为单独的分支origin/master存在,而本地的master也作为单独的分支存在,这样就不会存在相互的影响,如果现在想将远程仓库的更新合并,就使用git merge origin/master。比如下图所示:

⚠️如果本地仓库在未知情的情况下提交了新的版本,但是仓库也提交新的版本,这个时候就可能会存在下面这样的情况,如果存在下面这样的情况 git pull是不会生效的:

处理上面的情况只能依靠于fetch命令,使用过后大概是下图所示:

而在使用过后再使用merge合并就能恢复到正常情况上。

然后处理完合并后记得第一时间提交到远程仓库进行同步

💎fork

在版本控制中fork指的是分裂,即分裂出一个和这个仓库一模一样的副本,fork不是git的功能,而是其他git托管商所提供的功能,比如github等。使用fork,可以让你对一个开源的项目有着完全的控制权限,你可以随意的修改其fork项目。

💎git shortlog

Git shortlog命令可以让我们查看该仓库有哪些贡献者,贡献了多少条以及每一天的注释标题是什么,比如下图:

除此之外,还可以使用-s仅显示提交数量以及-n以数字的方式对他们的名字排序(而不是使用名称排序)

除了上面的参数,还可以使用作者过滤,比如这个代码”git log –author=Surma”过滤只要作者为Surma的提交情况,输出结果如下:

⚠️除此之外,还可以使用grep参数来过滤其内容,比如这个代码”git log –grep=bug”过滤了bug的提交消息。

💎pull request

一个pull request是对一个请求原或源代码库的维护者,以包括在他们的项目,你在你自己的项目叉所做的更改。您要求他们提取您所做的更改。

当我们没有权限修改目标仓库的时候,最好的做法就是fork,然后进行修复bug,修复完成后,可以通过pull request提交给原作者,看原作者是否接受该修复,如果原作者接受,就当提交至原作者的仓库中。

💎git rebase

Git rebase(压缩)命令可以将几个提交合并为一个提交,即将多个commit合并为一个commit,比如最近的几个commit都是一些小的更改,例如拼写错误等问题,这个时候这几个commit就显得有一些臃肿了。

比如下面的代码可以将最近的三个改动合并为一个新改动并提交,下图中的HEAD~3表示之前三个提交:

执行过后将会从上图中的sha为8的提交开始一直到b合并为一个,并保留提交8,然后删除8之后的提交,并生成新版本:

响应式设计学习笔记

在响应式设计中,浏览器并不是根据物理硬件的像素宽度而工作的,而是根据DIPs宽度工作,DIPs全称为“Device Independent Pixels”,DIP也是一种计量单位,该值将像素值与实际距离联系起来,dip的值表示的是屏幕上同样大小的空间,即比如两个手机的2dip,这个值不管设备的像素密度是多少,显示的大小都是一样。

但其中有一个转换的比值叫DPR(Device Pixels Ratio),可以通过浏览器的window.devicePixelRatio属性查询到。比如其Chrome book的DPR为2,而chrome book的硬件像素为2560,那么实际Dpis为1280。

从上面的内容得知,视窗宽度(即css像素值)是由下列公式组成:
\frac{实际像素(物理像素)}{像素比}
可以通过设置视窗的值来让浏览器知道我们的意图,比如下面的代码:

  <meta  name="viewport"  content="width=device-width,initial-scale=1">  

其中,name的值为viewport,即告诉浏览器设置视窗,其content中的内容则是设置视窗的值,其中有两个值:

  1. 第一个值”content=width=device”的意思是告诉浏览器将视窗的大小设置为设备的宽度,这样就能够让该页面匹配不同屏幕尺寸
  2. 第二个值”initial-scale=1″的意思是添加初始化缩放比例属性,即告诉浏览器相对像素与css像素的比例为1:1,这样的好处在于,如果不设置该缩放的值,有可能设备在变化横置或者纵置的时候,还是保持之前的大小,以及在缩放的时候也无法自动调整布局。

因为在不同设备上的css像素取值多种多样,所以不能只用视窗宽度属性就能解决所有的显示问题,因为过大的css宽度或者绝对定位,会让元素太大不适应屏幕,而这个时候就得使用相对单位,因为所有的相对单位都是属于基于某个元素。

css允许其元素溢出其容器,如果设置的大小超过了其容器的大小,就会溢出,如果在其他小于设置大小的屏幕上浏览的时候,就会产生左右滑动的溢出,所以设置相对单位就可以保证其元素小于其容器。

需要注意的是:css选项中max-width(height)会覆盖其单独设置的height和width的值。

正常的手指为10mm,大约等于40个css像素,所以在考虑设置按钮像素的时候可以设置为48*48px,而最好保持按钮和按钮之间有大概40px的间隔,以保证不会触发误按。

而在设计响应式的时候尽量做到不用直接用height和width值,因为他们是固定值,所以应该尽量用max和min设置大小。

设计响应式的页面的时候应该是从小到大的设备进行设计,因为这样可以考虑到在小屏幕下,会仔细的考虑哪些信息的重要性,而从大到小,可能会漏掉很多的关键信息,还有可能就是,从小到大的时候,做到中间尺寸的设备的时候,你可能也就不需要更大尺寸的设备了。并且有部分老的设备是不支持媒体查询的,所以从小到大,会有很多优势。

单位em是一个相对单位,常见用于设置内间距,比如按钮或者a标签内的文字,比如下面的这段代码,就代表了相对于当前a标签的字体的1.5倍间距,而第二个值inherit则表示继承自父组件,意思就是上下由我们定义为字体大小的1.5倍,而左右的内间距属性则继承父级。

  footer a {   padding: 1.5em;  }  

除此之外,只是使用视窗设置和相对单位,会造成由于小屏幕看着很好的页面,而到大页面的时候会造成损失,比如图片过于拉伸,页面内容显示单一等问题,这个时候就需要采用媒体查询,媒体查询的作用是可以让我们用一些简单的逻辑来选择性的加载css文件,比如下面的代码设置了屏幕尺寸大于500的css像素才加载该css。

  <link  rel="stylesheet" media="screen and (min-width:500px)"  href="/css/master.css">  

Continue reading

Javascript学习笔记(四)

导论

Javascript是一门面向对象的编程语言,面向对象的思想贯穿整个Javascript语言,所以了解对象是什么是很重要的。

面向对象编程(Object Oriented Programming,缩写为OOP)是将真实世界中的各种复杂的关系,抽象为一个个的对象,然后由对象之间的分工合作完成对真实世界的模拟。

每一个对象都是功能中心,具有明确分工,可以完成接受信息、处理数据、发出信息等任务。对象可以复用,通过继承机制还可以定制。因此,面向对象编程具有灵活、代码可复用、高度模块化等特点,容易维护和开发,比起由一系列函数或指令组成的传统的过程式编程(procedural programming),更适合多人合作的大型软件项目。

对象是单个实物的抽象

可以将对象想象为一个人、一辆车、一个网页、一个数据库等等,当实物被抽象为对象后,实物之间的关系就变成了对象和对象之间的关系,从而就可以模拟现实的情况,针对对象进行编程。

而对象也是一个容器,封装了各种属性、方法用于处理不同的事情,设置不同的属性,可以将这些属性想象为人的身高、体重,皮肤颜色,而方法则可以想象人的技能,如开车、打篮球、绘画等。

而继承,你可以想象为孩子,孩子会遗传一部分父亲母亲的颜色、单眼皮等,而在对象中也是如此,继承对象会继承一些父对象的方法、属性。

构造函数

面向对象的第一步就是生成对象,对象是一个个实物的单个抽象,而生成一个对象实例则需要一个模板来表示一类对象,比如人、猪、猫等都是单独的模版,在Javascript,这个模板就是构造函数,而继承则是原型。

典型的面相对象编程的语言C++和Java等,都有类的概念,所谓的类就是对象一个模版,对象就是类的实例,但是在Javascript中的对象不是基于类,而是基于构造函数(constrcutor)和原形(prototype)。

Javascript语言中的构造函数则是对象的模板,而构造函数则是专门用来生成实例对象的函数,而每个构造函数可以生成多个对象实例,而这个构造函数则定义了这一类对象的基础属性和方法。

构造函数和普通函数定义没有什么区别,包括函数名也同样遵守表示符的定义,所以为了区分构造函数与普通函数的区别,所以一般构造函数的一个字母都是大写,即普通函数user,而构造函数为User

构造函数还有两个特征:

  • 构造函数内部使用了this关键字,用来代表将要生成的对象实例
  • 生成对象的时候,必须使用new操作符,如果没有new操作符就是普通的函数调用

下面定义了一个很简单的构造函数,名为Js

function Js () {
    this.name = 'Javascript';
}

Continue reading