<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>雨雪霏霏</title>
  
  <link href="/atom.xml" rel="self"/>
  
  <link href="www.snovey.com/"/>
  <updated>2017-09-20T14:07:51.974Z</updated>
  <id>www.snovey.com/</id>
  
  <author>
    <name>雨雪霏霏</name>
    
  </author>
  
  <generator uri="http://hexo.io/">Hexo</generator>
  
  <entry>
    <title>使用 Git 的一些经验</title>
    <link href="www.snovey.com/2017/09/use-git.html"/>
    <id>www.snovey.com/2017/09/use-git.html</id>
    <published>2017-09-18T14:07:46.000Z</published>
    <updated>2017-09-20T14:07:51.974Z</updated>
    
    <content type="html"><![CDATA[<p>接触 Git 很久了，期间也看过各种博客，或浅显或深奥，但是看完依然不懂 Git，倒是最近用 Git 比较频繁，一点点也了解了，算是入了个门。</p><p>Git 非常强大，强大的同时伴随的时学习成本的提升。现在感触比较深的教训就是初学 Git 时不要想太多，把各个功能分离开来。我最初学习的时候总是在想如果我修改了一些工作区文件，然后切换了分支，那么工作区的文件会怎样？如果我撤销了修改，本地的暂存区会怎样？现在回想起来，其实有这种想法本身就是一个错误。<br><a id="more"></a><br>根据我自己经常用的一些操作，我把 Git 的操作分为三类：</p><ul><li>一些基础的操作（包括对工作区、暂存区的操作及一些查询操作）</li><li>与撤销相关的操作</li><li>与分支相关的操作</li></ul><p>下面来依次进行说明。</p><h2 id="基础操作"><a href="#基础操作" class="headerlink" title="基础操作"></a>基础操作</h2><p>提交修改：<br></p><figure class="highlight plain"><table><tr><td class="code"><pre><div class="line"># 将文件提交到暂存区</div><div class="line">$ git stage|add &lt;file&gt;</div><div class="line">#提交</div><div class="line">$ git commit</div><div class="line"></div><div class="line"># 把所有跟踪的文件暂存起来一起提交，相当于合写</div><div class="line">$ git commit -a</div></pre></td></tr></table></figure><p></p><p>查看状态：<br></p><figure class="highlight plain"><table><tr><td class="code"><pre><div class="line">$ git status</div></pre></td></tr></table></figure><p></p><p>建议在 stage 之前以及 commit 之前都进行 <code>git status</code>，这样有问题能够及时发现，比如有文件忘了提交，如果是在 commit 之前发现，只需要 stage 一下就好，如果是在 commit 之后发现，可以用新的提交覆盖掉：<br></p><figure class="highlight plain"><table><tr><td class="code"><pre><div class="line"># 覆盖之前的提交</div><div class="line">$ git commit --amend</div><div class="line"></div><div class="line"># 如果跟上次提交时的信息一样，又懒得再输一遍</div><div class="line">$ git commit --amend --no-edit</div></pre></td></tr></table></figure><p></p><p>当然，不排除可能某一天心血来潮查看 commit 历史发现两个 commit 应该合起来，那就需要 <code>rebase -i</code> 了，这里不多说。</p><p>通常我在提交前还会进行 diff 操作，diff 比较简单，像这样：<br></p><figure class="highlight plain"><table><tr><td class="code"><pre><div class="line"># 比较工作区与暂存区</div><div class="line">$ git diff &lt;file&gt;</div><div class="line"></div><div class="line"># 比较暂存区与当前 HEAD 所指向的提交</div><div class="line">$ git diff --stage|--cached &lt;file&gt;</div></pre></td></tr></table></figure><p></p><p>当然，不排除有想比较两个版本中文件的冲动，这当然是可行的：<br></p><figure class="highlight plain"><table><tr><td class="code"><pre><div class="line">$ git diff &lt;commit 1&gt;:&lt;file&gt; &lt;commit 2&gt;:&lt;file&gt;</div></pre></td></tr></table></figure><p></p><p>查看项目历史也是非常需要的，众所周知需要 <code>git log</code> 命令，但是默认的 <code>git log</code> 往往不尽如人意，尽如人意的 <code>git log</code> 命令往往又长到让人吐血，这时候就需要 alias 啊，通常一两个关于 log 的 alias 就够用了：<br></p><figure class="highlight plain"><table><tr><td class="code"><pre><div class="line">$ git config --global alias.history &quot;log --pretty=format:&apos;%Cred%h%Creset %C(bold blue)&lt;%an&gt;%Creset%C(yellow)%d%Creset %Cgreen(%cr)%Creset%n%w(80,8,8)%s&apos; --graph&quot;</div></pre></td></tr></table></figure><p></p><p>与标签相关的操作虽然不常用但还是有必要了解的：<br></p><figure class="highlight plain"><table><tr><td class="code"><pre><div class="line"># 列出标签</div><div class="line">$ git tag</div><div class="line"></div><div class="line"># 默认是按字母排序，在高版本 Git 中可以按时间排序</div><div class="line">$ git tag --sort -l --sort=v:refname # In git &gt;= 2.0</div><div class="line"></div><div class="line"># 打标签</div><div class="line">$ git tag -a &lt;tagname&gt; -m &apos;message&apos;</div><div class="line"></div><div class="line"># push 到远端</div><div class="line">$ git push origin &lt;tagname&gt;</div></pre></td></tr></table></figure><p></p><h2 id="与撤销相关的操作"><a href="#与撤销相关的操作" class="headerlink" title="与撤销相关的操作"></a>与撤销相关的操作</h2><p>在讲 Git 的撤销操作之前，特别提醒：<strong>Git 中版本库中的文件同步到工作区中间一定会途经暂存区，同理，工作区的文件提交到版本库也一定会经过暂存区，暂存区作为一个缓冲带，我没有发现哪条命令可以跨越。</strong></p><p>平时误操作基本都是 <kbd>Ctrl+Z</kbd> 或者 <kbd>u</kbd> 解决，但是这并不适用于所有场景。</p><p>把 Git 的撤销操作分为对文件的撤销操作与对 commit 的撤销操作，先说文件的，最常见的莫过于 <code>git status</code> 的提醒：</p><blockquote><p>(use “git checkout –<file>…” to discard changes in working directory)</file></p></blockquote><p>不用多说，这条命令就是将暂存区的文件同步到工作区。当然，实际需求可能不止如此，比如，把某个 commit 中的文件同步到工作区，这样操作：<br></p><figure class="highlight plain"><table><tr><td class="code"><pre><div class="line"># 将 commit 中的对应文件同步到暂存区</div><div class="line">$ git reset &lt;commit&gt; &lt;file&gt;</div><div class="line"></div><div class="line"># 此时是一个执行 git diff 的好时机哦，做好准备后就可以同步到工作区了</div><div class="line">$ git checkout -- &lt;file&gt;</div></pre></td></tr></table></figure><p></p><p>对于 commit 的撤销，分几种情况：<br>1.在公共分支上，撤销后依然先于远端或者跟远端相同<br></p><figure class="highlight plain"><table><tr><td class="code"><pre><div class="line"># --soft, --mixed, --hard 根据情况进行选择</div><div class="line">$ git reset &lt;commit&gt;</div><div class="line">$ git push</div></pre></td></tr></table></figure><p></p><p>2.在公共分支上，撤销后当前 commit 会滞后于远端，这时不要使用 reset，改用 revert（如果选择 reset 会影响到别人）：<br></p><figure class="highlight plain"><table><tr><td class="code"><pre><div class="line">$ git revert &lt;commit&gt;</div><div class="line">$ git push</div></pre></td></tr></table></figure><p></p><p>3.在个人分支上，这种情况是怎么折腾都无所谓的，为了有一个干净的历史，建议这样操作：<br></p><figure class="highlight plain"><table><tr><td class="code"><pre><div class="line">$ git reset &lt;commit&gt;</div><div class="line">$ git push</div><div class="line"></div><div class="line"># 如果 push 失败，强制一下</div><div class="line">$ git push -f</div></pre></td></tr></table></figure><p></p><p>忘了还有一种常见的场景，如果想要撤销全部修改，恢复到上次同步的状态，只需两条命令：<br></p><figure class="highlight plain"><table><tr><td class="code"><pre><div class="line"># 将版本库同步到暂存区合工作区</div><div class="line">$ git reset HEAD --hard</div><div class="line"></div><div class="line"># 清空未跟踪的文件和文件夹</div><div class="line">$ git clean -df</div></pre></td></tr></table></figure><p></p><p>这样本地的项目就又崭新如初了。</p><p>最后，如果找不到想要恢复的 commit，比如 reset 之后后悔，想要撤销 reset 操作，可以通过 <code>git reflog</code> 命令查找相应的 ID，这个 <code>git reflog</code> 会记录每一次对 HEAD 的操作。</p><h2 id="与分支相关的操作"><a href="#与分支相关的操作" class="headerlink" title="与分支相关的操作"></a>与分支相关的操作</h2><p>特别提醒：<strong>在进行切换分支操作之前，一定要保持工作区和暂存区干净，如果当前的状态是可以提交的，就 <code>git commit</code>，如果还未开发完成，就 <code>git stash</code>，切回来之后 <code>git stash pop</code>。</strong></p><p>先说远程分支同步的事情，如果比较懒，通常会直接 <code>git pull</code>，<code>git pull</code> 相当于：<br></p><figure class="highlight plain"><table><tr><td class="code"><pre><div class="line">$ git fetch &lt;remote&gt;</div><div class="line">$ git merge</div></pre></td></tr></table></figure><p></p><p>实际项目中很难预期 Git 是否会进行快速合并，所以我更推荐这样操作：<br></p><figure class="highlight plain"><table><tr><td class="code"><pre><div class="line">$ git fetch &lt;remote&gt;</div><div class="line">$ git rebase</div></pre></td></tr></table></figure><p></p><p>当然，如果希望能一条命令完成，这是可行的：<br></p><figure class="highlight plain"><table><tr><td class="code"><pre><div class="line">git pull --rebase &lt;remote&gt;</div></pre></td></tr></table></figure><p></p><p>但是你可能连 <code>--rebase</code> 都懒的打，依然可行：<br></p><figure class="highlight plain"><table><tr><td class="code"><pre><div class="line">git config --global branch.autosetuprebase always # In git &lt; 1.7.9</div><div class="line">git config --global pull.rebase true              # In git &gt;= 1.7.9</div></pre></td></tr></table></figure><p></p><p>接下来说一下分支合并时对于 merge 和 rebase 的选择吧，其实这是一个个人的喜好问题，如果用不明白 rebase 全都改用 merge 也无所谓，但是如果这样的话，切记不要在两个长期分支上来回 merge，那样项目历史会非常的混乱。</p><p>我对 merge 和 rebase 的选择依据非常简单，在个人分支上 rebase 公共分支，在公共分支上 merge 个人分支。</p><p>偶尔，对合并要求可能比较奇葩，要求只与指定的 commit 进行合并，这时候 <code>cherry-pick</code> 就派上用场了<br></p><figure class="highlight plain"><table><tr><td class="code"><pre><div class="line">git cherry-pick &lt;commit 1&gt; &lt;commit 2&gt; ...</div></pre></td></tr></table></figure><p></p><p>Git 的命令还有很多，但是于我而言，上面的命令已经能满足绝大部分的场景了。</p><p>参考：</p><ul><li><a href="https://git-scm.com/book/zh/v2" target="_blank" rel="external">Git-Book</a></li><li><a href="https://github.com/geeeeeeeeek/git-recipes" target="_blank" rel="external">Git recipes in Chinese. 高质量的 Git 中文教程.</a></li></ul><blockquote>文档信息<br><hr>本作品采用 <a rel="external" href="http://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank">知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议</a> 进行许可。<br>本文链接：www.snovey.com/2017/09/use-git.html</blockquote>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;接触 Git 很久了，期间也看过各种博客，或浅显或深奥，但是看完依然不懂 Git，倒是最近用 Git 比较频繁，一点点也了解了，算是入了个门。&lt;/p&gt;&lt;p&gt;Git 非常强大，强大的同时伴随的时学习成本的提升。现在感触比较深的教训就是初学 Git 时不要想太多，把各个功能分离开来。我最初学习的时候总是在想如果我修改了一些工作区文件，然后切换了分支，那么工作区的文件会怎样？如果我撤销了修改，本地的暂存区会怎样？现在回想起来，其实有这种想法本身就是一个错误。&lt;br&gt;
    
    </summary>
    
      <category term="Git" scheme="www.snovey.com/categories/Git/"/>
    
    
      <category term="Git" scheme="www.snovey.com/tags/Git/"/>
    
  </entry>
  
  <entry>
    <title>对 JavaScript 中 forEach 的一点理解</title>
    <link href="www.snovey.com/2017/09/javascrpt-foreach.html"/>
    <id>www.snovey.com/2017/09/javascrpt-foreach.html</id>
    <published>2017-09-04T16:51:10.000Z</published>
    <updated>2017-09-05T15:08:27.390Z</updated>
    
    <content type="html"><![CDATA[<p>之前对 forEach 的理解就是 for 循环的另一种写法，直到遇到问题才发现自己的理解有问题。<br>先看一下 MDN 上对 forEach 的介绍：<br><a id="more"></a></p><blockquote><p>forEach() 方法对数组的每个元素执行一次提供的函数。</p></blockquote><p>然后 MDN 还给出了一段示例代码：<br></p><figure class="highlight javascript"><table><tr><td class="code"><pre><div class="line"><span class="keyword">let</span> a = [<span class="string">'a'</span>, <span class="string">'b'</span>, <span class="string">'c'</span>]</div><div class="line"></div><div class="line">a.forEach(<span class="function"><span class="keyword">function</span> (<span class="params">element</span>) </span>&#123;</div><div class="line">    <span class="built_in">console</span>.log(element)</div><div class="line">&#125;)</div><div class="line"></div><div class="line"><span class="comment">// a</span></div><div class="line"><span class="comment">// b</span></div><div class="line"><span class="comment">// c</span></div></pre></td></tr></table></figure><p></p><p>到这里都没有什么问题，接下来我写一个例子：<br></p><figure class="highlight javascript"><table><tr><td class="code"><pre><div class="line"><span class="keyword">let</span> a = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>]</div><div class="line"></div><div class="line">a.forEach(<span class="function"><span class="keyword">function</span> (<span class="params">element</span>) </span>&#123;</div><div class="line">    <span class="keyword">return</span> element</div><div class="line">)</div><div class="line"><span class="built_in">console</span>.log(a) <span class="comment">// [1, 2, 3]</span></div></pre></td></tr></table></figure><p></p><p>return 无效，再换个方式：<br></p><figure class="highlight javascript"><table><tr><td class="code"><pre><div class="line"><span class="keyword">let</span> a = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>]</div><div class="line"></div><div class="line">a.forEach(<span class="function"><span class="keyword">function</span> (<span class="params">element</span>) </span>&#123;</div><div class="line">    ++element</div><div class="line">)</div><div class="line"><span class="built_in">console</span>.log(a) <span class="comment">// [1, 2, 3]</span></div></pre></td></tr></table></figure><p></p><p>仍然无效，再来看一下 MDN 的文档：<br></p><figure class="highlight javascript"><table><tr><td class="code"><pre><div class="line">array.forEach(callback (currentValue, index, array) &#123;</div><div class="line">    <span class="comment">//do something</span></div><div class="line">&#125;, <span class="keyword">this</span>)</div><div class="line"></div><div class="line">array.forEach(callback[, thisArg])</div></pre></td></tr></table></figure><p></p><p>介绍：</p><blockquote><p>forEach 方法按升序为数组中含有效值的每一项执行一次callback 函数</p></blockquote><p>文档中还写到 callback 函数的返回值为 undefined，到这里原因已经呼之欲出了，因为函数传递参数时基本数据类型按值传递，所修改值并不会影响到数组本身，而且 callback 函数的返回值也与数组本身无关。</p><p>解决办法有两种：</p><p>1.获取到数组的引用<br></p><figure class="highlight javascript"><table><tr><td class="code"><pre><div class="line"><span class="keyword">let</span> a = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>]</div><div class="line"></div><div class="line">a.forEach((element, index, arr) =&gt; &#123;</div><div class="line">    arr[index] = element + <span class="number">1</span></div><div class="line">&#125;)</div><div class="line"><span class="built_in">console</span>.log(a) <span class="comment">// [2, 3, 4]</span></div></pre></td></tr></table></figure><p></p><p>2.返回新的数组<br></p><figure class="highlight javascript"><table><tr><td class="code"><pre><div class="line"><span class="keyword">let</span> a = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>]</div><div class="line"></div><div class="line">a = a.map(element =&gt; element + <span class="number">1</span>)</div><div class="line"><span class="built_in">console</span>.log(a) <span class="comment">// [2, 3, 4]</span></div></pre></td></tr></table></figure><p></p><p>参考：</p><ul><li><a href="//developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach">Array.prototype.forEach()</a></li><li><a href="//stackoverflow.com/questions/12482961/is-it-possible-to-change-values-of-the-array-when-doing-foreach-in-javascript">is it possible to change values of the array when doing foreach in javascript?</a></li></ul><blockquote>文档信息<br><hr>本作品采用 <a rel="external" href="http://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank">知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议</a> 进行许可。<br>本文链接：www.snovey.com/2017/09/javascrpt-foreach.html</blockquote>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;之前对 forEach 的理解就是 for 循环的另一种写法，直到遇到问题才发现自己的理解有问题。&lt;br&gt;先看一下 MDN 上对 forEach 的介绍：&lt;br&gt;
    
    </summary>
    
      <category term="JavaScript" scheme="www.snovey.com/categories/JavaScript/"/>
    
    
      <category term="JavaScript" scheme="www.snovey.com/tags/JavaScript/"/>
    
  </entry>
  
  <entry>
    <title>制作图案解锁</title>
    <link href="www.snovey.com/2017/04/pattern-lock.html"/>
    <id>www.snovey.com/2017/04/pattern-lock.html</id>
    <published>2017-04-02T04:38:07.000Z</published>
    <updated>2017-08-09T15:28:36.487Z</updated>
    
    <content type="html"><![CDATA[<p>参加数字公司的校招，先是留了一个大作业，要求用网页制作一个图案解锁。<br>经过一番奋战，总算做了出来。</p><a id="more"></a><p>放张图：<br><img class="lazyload"  data-src="//blog.snovey.com/image/pattern-lock.gif" alt="pattern-lock"><br>想感受一下效果么？戳这里：<a href="//snovey.github.io/Web-crafts/pattern-lock/demo.html">pattern-lock</a><br>要制作一个图案解锁，第一步要想好设计，对外开放哪些接口，如果我是一位开发者，我希望初始化的时候可以自定义行数列数，以及颜色大小等参数，如果用户不定义要有一套默认的参数，我希望可以调用方法获取用户的输入，并且提供一些额外的 API。就像买煎饼果子告诉老板不要加辣多放香菜，如果省略某些信息老板还要有自己的默认配置，最后给你你想要的煎饼果子。<br>JavaScript 实现类似这样：<br></p><figure class="highlight javascript"><table><tr><td class="code"><pre><div class="line"><span class="keyword">constructor</span>(obj) &#123;</div><div class="line">  <span class="keyword">this</span>.row    = +obj.row || <span class="number">3</span>;</div><div class="line">  <span class="keyword">this</span>.column = +obj.column || <span class="number">3</span>;</div><div class="line">  <span class="keyword">this</span>.backgroundColor = obj.backgroundColor || <span class="string">'whitesmoke'</span>;</div><div class="line">  <span class="keyword">this</span>.opacity = obj.opacity || <span class="number">0.0</span>;</div><div class="line">  <span class="keyword">this</span>.container = obj.container;</div><div class="line">  <span class="keyword">this</span>.lineColor = obj.lineColor || <span class="string">'springgreen'</span>;</div><div class="line">  <span class="keyword">this</span>.lineWidth = obj.lineWidth || <span class="number">3</span>;</div><div class="line">  <span class="keyword">this</span>.pointBackColor = obj.pointBackColor || <span class="string">'white'</span>;</div><div class="line">  <span class="keyword">this</span>.pointBorderColor = obj.pointBorderColor || <span class="string">'grey'</span>;</div><div class="line">  <span class="keyword">this</span>.radius = obj.radius || <span class="string">'auto'</span>;</div><div class="line">&#125;</div></pre></td></tr></table></figure><p></p><p>想好之后就是实现的问题了，图案有斜线等元素，比较复杂，所以采用 canvas 实现，以常见的三排三列为例，大致需要画出如下的图案，采取坐标轴如下（别问我为什么是左手系）：</p><p><img class="lazyload"  data-src="//blog.snovey.com/image/pattern-lock.png" alt="pattern-lock"></p><p>这里定义 $xunit$ 与 $yunit$ 作为单位长度，方便后续的计算，这两个值根据 canvas 的宽高以及小圆的个数计算：<br>$$<br>\begin{cases}<br>xunit = \frac{width}{2 \times column} \\<br>yunit = \frac{height}{2 \times row}<br>\end{cases}<br>$$<br>根据 $xunit$ 与 $yunit$ 就可以计算出第 $i$ 排第 $j$ 列的圆的圆心的坐标：<br>$$<br>point[i][j] = (2 \times (i+1) \times xunit, 2 \times (j+1) \times yunit)<br>$$<br>有了公式后进行初始化，计算 $xunit$ 与 $yunit$，使用循环把各个点的坐标存入二维数组中，同时标记节点未触摸过。JavaScript 代码：<br></p><figure class="highlight javascript"><table><tr><td class="code"><pre><div class="line">init () &#123;</div><div class="line">  <span class="keyword">this</span>.input = [];</div><div class="line">  <span class="keyword">this</span>.canvas = <span class="built_in">document</span>.getElementById(<span class="keyword">this</span>.container);</div><div class="line">  <span class="keyword">this</span>.context = <span class="keyword">this</span>.canvas.getContext(<span class="string">'2d'</span>);</div><div class="line">  <span class="keyword">let</span> width = <span class="built_in">parseInt</span>(<span class="keyword">this</span>.canvas.getAttribute(<span class="string">'width'</span>));</div><div class="line">  <span class="keyword">let</span> height = <span class="built_in">parseInt</span>(<span class="keyword">this</span>.canvas.getAttribute(<span class="string">'height'</span>));</div><div class="line">  <span class="keyword">this</span>.xunit = width / (<span class="number">2</span>*<span class="keyword">this</span>.column);</div><div class="line">  <span class="keyword">this</span>.yunit = height / (<span class="number">2</span>*<span class="keyword">this</span>.row);</div><div class="line">  <span class="keyword">this</span>.radius = (<span class="keyword">this</span>.radius === <span class="string">'auto'</span> ? <span class="built_in">Math</span>.min(<span class="keyword">this</span>.xunit, <span class="keyword">this</span>.yunit) / <span class="number">2</span> : <span class="keyword">this</span>.radius);</div><div class="line">  <span class="keyword">this</span>.coor = [];</div><div class="line">  <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; <span class="keyword">this</span>.column; i++) &#123;</div><div class="line">    <span class="keyword">this</span>.coor[i] = [];</div><div class="line">    <span class="keyword">for</span> (<span class="keyword">let</span> j = <span class="number">0</span>; j &lt; <span class="keyword">this</span>.row; j++) &#123;</div><div class="line">      <span class="keyword">this</span>.coor[i].push(&#123;</div><div class="line">        x: <span class="keyword">this</span>.xunit * (<span class="number">2</span>*i+<span class="number">1</span>),</div><div class="line">        y: <span class="keyword">this</span>.yunit * (<span class="number">2</span>*j+<span class="number">1</span>),</div><div class="line">        visit: <span class="literal">false</span></div><div class="line">      &#125;);</div><div class="line">    &#125;</div><div class="line">  &#125;</div><div class="line">  <span class="keyword">this</span>.drawCircle();</div><div class="line">  <span class="keyword">this</span>.bindEvent();</div><div class="line">&#125;</div></pre></td></tr></table></figure><p></p><p>接下来是绑定三种事件， <code>touchstart</code>，<code>touchmove</code> 和 <code>touchend</code>，这三个事件处理的事情差不多，无论哪个事件首先都要获取位置，判断是否与圆相交，这里通过<br>$$<br>\begin{cases}<br>recentX = \left(2 \times \left \lfloor \frac{touchX}{2 \times xunit}\right \rfloor+1 \right) \times xunit \\\\<br>recentY = \left( 2 \times \left \lfloor \frac{touchY}{2 \times yunit}\right \rfloor + 1 \right) \times yunit<br>\end{cases}<br>$$<br>算出与触摸的点最近的圆心，然后计算这两个点的距离的平方， 与半径的平方进行比较即可得知是否在圆内。每个事件都要有这个操作，所以提出来做单独的函数，JavaScript 代码：<br></p><figure class="highlight javascript"><table><tr><td class="code"><pre><div class="line">getPosition (evt) &#123;</div><div class="line">  <span class="keyword">let</span> touchX  = evt.touches[<span class="number">0</span>].clientX - evt.currentTarget.getBoundingClientRect().left;</div><div class="line">  <span class="keyword">let</span> touchY  = evt.touches[<span class="number">0</span>].clientY - evt.currentTarget.getBoundingClientRect().top;</div><div class="line">  <span class="keyword">let</span> indexX  = <span class="built_in">Math</span>.floor(touchX / (<span class="number">2</span>*<span class="keyword">this</span>.xunit));</div><div class="line">  <span class="keyword">let</span> indexY  = <span class="built_in">Math</span>.floor(touchY / (<span class="number">2</span>*<span class="keyword">this</span>.yunit));</div><div class="line">  <span class="keyword">let</span> recentX = (<span class="number">2</span>*indexX+<span class="number">1</span>) * <span class="keyword">this</span>.xunit;</div><div class="line">  <span class="keyword">let</span> recentY = (<span class="number">2</span>*indexY+<span class="number">1</span>) * <span class="keyword">this</span>.yunit;</div><div class="line">  <span class="keyword">let</span> hit = <span class="built_in">Math</span>.pow(recentX-touchX, <span class="number">2</span>)+<span class="built_in">Math</span>.pow(recentY-touchY, <span class="number">2</span>) &lt; <span class="built_in">Math</span>.pow(<span class="keyword">this</span>.radius, <span class="number">2</span>);</div><div class="line">  <span class="keyword">let</span> pos = &#123;</div><div class="line">    indexX: indexX,</div><div class="line">    indexY: indexY,</div><div class="line">    touchX: touchX,</div><div class="line">    touchY: touchY,</div><div class="line">    recentX: recentX,</div><div class="line">    recentY: recentY,</div><div class="line">    hit: hit</div><div class="line">  &#125;;</div><div class="line">  <span class="keyword">return</span> pos;</div><div class="line">&#125;</div></pre></td></tr></table></figure><p></p><p>如果在圆内并且未触摸过该点，则标记该点，并将该点存入数组中。</p><p>当然，记得每次 <code>touchmove</code> 发生时都要清空画布重绘图案！重新绘制时需要将走过的节点两两连线，这里用一个 <code>reduce</code> 函数就搞定了：<br></p><figure class="highlight javascript"><table><tr><td class="code"><pre><div class="line">self.input.reduce((prev, next) =&gt; self.drawLine(prev, next));</div></pre></td></tr></table></figure><p></p><p>最后，整个流程最消耗资源的就是每次发生 <code>touchmove</code> 事件时的重绘画布了，这里需要做一下函数节流和防抖，不过我做的时候出了点问题，就先搁置了。</p><p>想了解详细的使用请移步我的 <a href="//github.com/snovey/Web-crafts/tree/master/pattern-lock">GitHub</a></p><blockquote>文档信息<br><hr>本作品采用 <a rel="external" href="http://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank">知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议</a> 进行许可。<br>本文链接：www.snovey.com/2017/04/pattern-lock.html</blockquote>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;参加数字公司的校招，先是留了一个大作业，要求用网页制作一个图案解锁。&lt;br&gt;经过一番奋战，总算做了出来。&lt;/p&gt;
    
    </summary>
    
    
      <category term="JavaScript" scheme="www.snovey.com/tags/JavaScript/"/>
    
  </entry>
  
  <entry>
    <title>计算机中的一词多义</title>
    <link href="www.snovey.com/2016/11/polysemy.html"/>
    <id>www.snovey.com/2016/11/polysemy.html</id>
    <published>2016-11-21T14:12:17.000Z</published>
    <updated>2017-08-27T13:37:02.721Z</updated>
    
    <content type="html"><![CDATA[<p>学习中常常遇到同一个词语出现在多个地方，其中有一部分词语让我相当的困惑：<br>为什么两个词是一样的，但是感觉没有什么联系？比如「闭包」。</p><a id="more"></a><ul><li>数学中的「闭包」与计算机科学中的「闭包」有什么关系？</li><li>内存管理中的「堆」与数据结构中的「堆」有什么关系？</li></ul><p>我目前就想到这两个，那我就说说这两个，先说答案：他们没什么关系。</p><h2 id="闭包"><a href="#闭包" class="headerlink" title="闭包"></a>闭包</h2><p>数学中的「闭包」：</p><blockquote><p>数学中，若对某个集合的成员进行一种运算，生成的仍然是这个集合的成员，则该集合被称为 <strong>在这个运算下闭合</strong> 。<br>当一个集合 S 在某个运算下不闭合的时候，我们通常可以找到包含 S 的最小的闭合集合。这个最小闭合集合被称为 S 的（关于这个运算的） <strong>闭包</strong> 。<br><span style="display:block;text-align:right">中文维基百科</span></p></blockquote><p>计算机科学中的「闭包」：</p><blockquote><p>In programming languages, closures (also lexical closures or function closures) are techniques for implementing lexically scoped name binding in languages with first-class functions<br><span style="display:block;text-align:right"><em>Wikipedia</em></span></p></blockquote><p>渣翻一下，大意是：</p><blockquote><p>在编程语言中， <strong>闭包</strong> （也叫 <strong>词法闭包</strong> 或 <strong>函数闭包</strong> ）是在语言中使用一等函数实现词法作用域名称绑定的技术。</p></blockquote><p>看定义他们就没什么关系，Peter J. Landin 在 1964 年将术语 <strong>闭包</strong> 定义为一种包含 <strong>环境成分</strong> 和 <strong>控制成分</strong> 的实体，来指代某些其开放绑定（自由变量）已经由其语法环境完成闭合（或者绑定）的 lambda 表达式，从而形成了 <strong>闭合的表达式</strong> ，或称闭包。在 <em>SICP</em> 中也提到了这个：</p><blockquote><p>The use of the word “closure” here comes from abstract algebra, where a set of elements is said to be closed under an operation if applying the operation to elements in the set produces an element that is again an element of the set. <strong>The Lisp community also (unfortunately) uses the word “closure” to describe a totally unrelated concept</strong>: A closure is an implementation technique for representing procedures with free.<br><span style="display:block;text-align:right"><em>Structure and Interpretation of Computer Programs, 2nd ed. p.133</em></span></p></blockquote><p>注意加粗的部分。这里需要注意一下，维基百科对闭包的定义与我们通常意义上的闭包是有所区别的，我们通常说的闭包往往是指：</p><blockquote><p>调用一个函数 A，返回一个函数 B，其中函数 B 引用了 A 中的自由变量。我们称函数 A 使用了闭包。</p></blockquote><p>维基百科对闭包的定义是从理论角度出发的，而我们通常意义上所指的闭包是从实践角度出发的，实践角度的闭包是理论角度的一个特例。</p><p>「闭包」这个词不仅仅出现这两个地方，我再举几个相关的词。</p><p>集合论中的「自反闭包」：</p><blockquote><p>在数学中，集合 X 上的二元关系 R 的 <strong>自反闭包</strong> 是 X 上包含 R 的最小的自反关系。<br>集合 X 上的关系 R 的自反闭包 S 的定义为<br>$$<br>S=R\cup \left\lbrace(x,x):x\in X\right\rbrace<br>$$<br>换言之，R 的自反闭包是 R 与 X 上的恒等关系的并集。</p></blockquote><p>集合论中的「对称闭包」：</p><blockquote><p>在数学中，集合 X 上的二元关系 R 的 <strong>对称闭包</strong> 是 X 上包含 R 的最小的对称关系。<br>集合 X 上的关系 R 的对称闭包 S 的定义为<br>$$<br>S=R\cup \left\lbrace(x,y):(y,x)\in R\right\rbrace.\,<br>$$<br>换言之，R 的对称闭包是 R 与 X 上的逆关系的并集。</p></blockquote><p>集合论中的「传递闭包」：</p><blockquote><p>在数学中，集合 X 上的二元关系 R 的 <strong>传递闭包</strong> 是 X 上包含 R 的最小的传递关系。<br>集合 X 上的关系 R 的传递闭包 S 的定义为<br>$$<br>S=\bigcup_{i\in \lbrace 1,2,3,\ldots\rbrace} R^i.<br>$$<br>其中<br>$$<br>\begin{cases}<br>R^1 = R\,\\[2ex]<br>R^{i+1} = R \circ R^i<br>\end{cases}<br>$$<br>换言之，R 的传递闭包是 R 与 X 上的传递关系的并集。</p></blockquote><p>形式语言中的「Kleene 闭包」：</p><blockquote><p>假定<br>$$<br>V_{0}=\lbrace\epsilon \rbrace\,<br>$$<br>递归的定义集合<br>$$<br>V_{i+1}=\lbrace wv:w\in V_{i}\wedge v\in V\rbrace\, 这里的\ i&gt;0\,<br>$$<br>如果 $V$ 是一个形式语言，集合 $V$ 的第 $i$ 次幂是集合 $V$ 同自身的 i 次串接的简写。就是说， $V_{i}$ 可以被理解为是从 $V$ 中的符号形成的所有长度为 $i$ 的字符串的集合。<br>所以在 $V$ 上的 Kleene 星号的定义是</p></blockquote><p>$$<br>V^{*}=\bigcup_{i=0}^{+\infty} V_i=\left\lbrace\varepsilon \right\rbrace\cup V\cup V^{2}\cup V^{3}\cup \ldots<br>$$</p><p>就是说，它是从 $V$ 中的符号生成的所有可能的有限长度的字符串的并集。</p><p>自动机理论中的 「ε-闭包」：</p><blockquote><p>对于任何 $p\in Q$，从 p 可到达的状态的集合叫做 p 的 <strong>ε-闭包</strong> ，并写为<br>$$<br>\,E(\lbrace p\rbrace)=\lbrace q\in Q:p{\stackrel {\epsilon }{\rightarrow }}q\rbrace。<br>$$<br>对于 $P\subset Q$ 的任何子集，定义 P 的 <strong>ε-闭包</strong> 为<br>$$<br>E(P)=\bigcup \limits _{p\in P}E(\lbrace p\rbrace)<br>$$</p></blockquote><p>纵观以上种种「闭包」，除去「词法闭包」，「闭包」都是指具备某种性质的最小集合，他们本质上的思想是一样的。而「词法闭包」与他们没有什么关系。</p><h2 id="堆"><a href="#堆" class="headerlink" title="堆"></a>堆</h2><p>内存管理中的「堆」：</p><blockquote><p>在计算机科学中, <strong>动态内存分配</strong> （Dynamic memory allocation）又称为 <strong>堆内存分配</strong> ，是指计算机程序在运行期中分配使用内存。它可以当成是一种分配有限内存资源所有权的方法。</p></blockquote><p>数据结构中的「堆」：</p><blockquote><p>堆（英语：Heap）是计算机科学中一类特殊的数据结构的统称。堆通常是一个可以被看做一棵树的数组对象。这种数据结构具有以下性质</p><ul><li>任意节点小于（或大于）它的所有后裔，最小元（或最大元）在堆的根上（堆序性）。</li><li>堆总是一棵完全树。即除了最底层，其他层的节点都被元素填满，且最底层尽可能地从左到右填入。</li></ul></blockquote><p>将根节点最大的堆叫做最大堆或大根堆，根节点最小的堆叫做最小堆或小根堆。</p><p>这两个概念毫无关系，Donald Knuth 曾多次说起此事， <em>TAOCP</em> 中提到：</p><blockquote><p>Several authors began about 1975 to call the pool of available memory a “heap.” But in the present series of books, we will use that word only in its more traditional sense related to priority queues.<br><span style="display:block;text-align:right"><em>The Art of Computer Programming, Third Ed., Vol. 1, p. 435</em></span></p></blockquote><p>在 <em>CLRS</em> 中也说：</p><blockquote><p>The term “heap” was originally coined in the context of heapsort, but it has since come to refer to “garbage-collected storage,” such as the programming languages Java and Lisp provide. Our heap data structure is not garbage-collected storage, and whenever we refer to heaps in this book, we shall mean a data structure rather than an aspect of garbage collection.<br><span style="display:block;text-align:right"><em>Introduction to Algorithms, Third Ed, p. 151</em></span></p></blockquote><p>内存管理中的「堆」，原意是类似「一堆衣物」的「堆」，表示没有具体的顺序，而「堆内存分配」的实现也往往是链表。与数据结构中的「堆」完全没有关系，数据结构中的「堆」源自「堆排序」。</p><p>参考：</p><ul><li><a href="https://zh.wikipedia.org/wiki/闭包_(数学" target="_blank" rel="external">闭包 (数学</a>)</li><li><a href="https://en.wikipedia.org/wiki/Closure_(computer_programming" target="_blank" rel="external">Closure (computer programming</a>)</li><li><em>SICP</em></li><li><a href="https://zh.wikipedia.org/zh-hans/自反闭包" target="_blank" rel="external">自反闭包</a></li><li><a href="https://zh.wikipedia.org/zh-hans/对称闭包" target="_blank" rel="external">对称闭包</a></li><li><a href="https://en.wikipedia.org/wiki/Transitive_closure" target="_blank" rel="external">Transitive closure</a></li><li><a href="https://zh.wikipedia.org/wiki/克莱尼星号" target="_blank" rel="external">克莱尼星号</a></li><li><a href="https://zh.wikipedia.org/wiki/非确定有限状态自动机" target="_blank" rel="external">非确定有限状态自动机</a></li><li><a href="https://zh.wikipedia.org/wiki/动态内存分配" target="_blank" rel="external">动态内存分配</a></li><li><a href="https://zh.wikipedia.org/wiki/堆_(数据结构" target="_blank" rel="external">堆 (数据结构</a>)</li><li><em>TAOCP</em></li><li><em>CLRS</em></li></ul><blockquote>文档信息<br><hr>本作品采用 <a rel="external" href="http://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank">知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议</a> 进行许可。<br>本文链接：www.snovey.com/2016/11/polysemy.html</blockquote>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;学习中常常遇到同一个词语出现在多个地方，其中有一部分词语让我相当的困惑：&lt;br&gt;为什么两个词是一样的，但是感觉没有什么联系？比如「闭包」。&lt;/p&gt;
    
    </summary>
    
      <category term="杂谈" scheme="www.snovey.com/categories/%E6%9D%82%E8%B0%88/"/>
    
    
      <category term="杂谈" scheme="www.snovey.com/tags/%E6%9D%82%E8%B0%88/"/>
    
  </entry>
  
  <entry>
    <title>通关 hacked 的 story 模式</title>
    <link href="www.snovey.com/2016/10/hacked-story.html"/>
    <id>www.snovey.com/2016/10/hacked-story.html</id>
    <published>2016-10-01T09:42:28.000Z</published>
    <updated>2017-08-27T17:04:35.260Z</updated>
    
    <content type="html"><![CDATA[<p>十一返乡的绿皮车异常拥挤，漫长的旅途连一碗泡面都吃不上，百无聊赖之际想起手机里还有 <a href="http://www.hackedapp.com" target="_blank" rel="external">hacked</a> 这个游戏，这是我的手机中除了 chrome 的小恐龙之外的唯一一款游戏了，当初初学 C 语言，未能通关便放弃了，它就这样躺在手机里一年多，如今终于通关。<br><a id="more"></a><br>先贴一张图：</p><p><img class="lazyload"  data-src="//blog.snovey.com/image/hacked-story.png" alt="hacked-story"></p><p>好了，放代码：</p><h2 id="The-Hackpad"><a href="#The-Hackpad" class="headerlink" title="The Hackpad"></a>The Hackpad</h2><h3 id="Increment-me"><a href="#Increment-me" class="headerlink" title="Increment me"></a>Increment me</h3><p>Sample Input/Output<br></p><figure class="highlight plain"><table><tr><td class="code"><pre><div class="line">2 =&gt; 3</div><div class="line">5 =&gt; 8</div><div class="line">8 =&gt; 9</div></pre></td></tr></table></figure><p></p><p>输入 <code>+1</code><br></p><figure class="highlight javascript"><table><tr><td class="code"><pre><div class="line">input + <span class="number">1</span>;</div></pre></td></tr></table></figure><p></p><h3 id="Positive"><a href="#Positive" class="headerlink" title="Positive"></a>Positive</h3><figure class="highlight plain"><table><tr><td class="code"><pre><div class="line">3 =&gt; true</div><div class="line">-7 =&gt; false</div><div class="line">9 =&gt; true</div></pre></td></tr></table></figure><p>判断正负<br></p><figure class="highlight javascript"><table><tr><td class="code"><pre><div class="line">input &gt; <span class="number">0</span>;</div></pre></td></tr></table></figure><p></p><h3 id="Absolute"><a href="#Absolute" class="headerlink" title="Absolute"></a>Absolute</h3><p>Sample Input/Output<br></p><figure class="highlight plain"><table><tr><td class="code"><pre><div class="line">1 =&gt; 1</div><div class="line">-2 =&gt; 2</div><div class="line">0 =&gt; 0</div></pre></td></tr></table></figure><p></p><p>此时没有 <code>abs</code> 函数，需要手写<br></p><figure class="highlight javascript"><table><tr><td class="code"><pre><div class="line"><span class="keyword">if</span> input &lt; <span class="number">0</span> &#123;</div><div class="line">  <span class="keyword">return</span> -input;</div><div class="line">&#125;</div><div class="line"><span class="keyword">return</span> input;</div></pre></td></tr></table></figure><p></p><h3 id="Absolute-2"><a href="#Absolute-2" class="headerlink" title="Absolute 2"></a>Absolute 2</h3><p>Sample Input/Output<br></p><figure class="highlight plain"><table><tr><td class="code"><pre><div class="line">1 =&gt; 1</div><div class="line">-2 =&gt; 2</div><div class="line">0 =&gt; 0</div></pre></td></tr></table></figure><p></p><p>得到 <code>abs</code> 函数，直接调用即可<br></p><figure class="highlight javascript"><table><tr><td class="code"><pre><div class="line"><span class="keyword">return</span> abs(input);</div></pre></td></tr></table></figure><p></p><h2 id="High-School-Hack"><a href="#High-School-Hack" class="headerlink" title="High School Hack"></a>High School Hack</h2><h3 id="Power"><a href="#Power" class="headerlink" title="Power"></a>Power</h3><p>Sample Input/Output<br></p><figure class="highlight plain"><table><tr><td class="code"><pre><div class="line">2 =&gt; 4</div><div class="line">3 =&gt; 9</div><div class="line">4 =&gt; 16</div></pre></td></tr></table></figure><p></p><p>此时没有 <code>pow</code> 函数，需要手写<br></p><figure class="highlight javascript"><table><tr><td class="code"><pre><div class="line"><span class="keyword">while</span> var_a &lt; input &#123;</div><div class="line">  var_b = var_b + input;</div><div class="line">  var_a++;</div><div class="line">&#125;</div><div class="line">var_b;</div></pre></td></tr></table></figure><p></p><h3 id="Power-2"><a href="#Power-2" class="headerlink" title="Power 2"></a>Power 2</h3><p>Sample Input/Output<br></p><figure class="highlight plain"><table><tr><td class="code"><pre><div class="line">2 =&gt; 4</div><div class="line">3 =&gt; 9</div><div class="line">4 =&gt; 16</div></pre></td></tr></table></figure><p></p><p>得到 <code>pow</code> 函数，直接调用即可<br></p><figure class="highlight javascript"><table><tr><td class="code"><pre><div class="line"><span class="keyword">return</span> pow(input, <span class="number">2</span>);</div></pre></td></tr></table></figure><p></p><h3 id="length"><a href="#length" class="headerlink" title="length"></a>length</h3><p>Sample Input/Output<br></p><figure class="highlight plain"><table><tr><td class="code"><pre><div class="line">[3, 8] =&gt; 2</div><div class="line">[4, 5, 3] =&gt; 3</div><div class="line">[6] =&gt; 1</div></pre></td></tr></table></figure><p></p><p>此时没有 <code>length</code>，需要手写<br></p><figure class="highlight javascript"><table><tr><td class="code"><pre><div class="line">foreach var_a <span class="keyword">in</span> input &#123;</div><div class="line">  var_b++;</div><div class="line">&#125;</div><div class="line"><span class="keyword">return</span> var_b;</div></pre></td></tr></table></figure><p></p><h3 id="Push-it"><a href="#Push-it" class="headerlink" title="Push it"></a>Push it</h3><p>Sample Input/Output<br></p><figure class="highlight plain"><table><tr><td class="code"><pre><div class="line">5 =&gt; [0, 1, 2, 3, 4]</div><div class="line">2 =&gt; [0, 1]</div><div class="line">0 =&gt; []</div></pre></td></tr></table></figure><p></p><p>根据输入输出列表<br></p><figure class="highlight javascript"><table><tr><td class="code"><pre><div class="line">var_a = [];</div><div class="line"><span class="keyword">while</span> var_b &lt; input &#123;</div><div class="line">  var_a.push(var_b);</div><div class="line">  var_b++;</div><div class="line">&#125;</div><div class="line"><span class="keyword">return</span> var_a;</div></pre></td></tr></table></figure><p></p><h2 id="Jailbreak"><a href="#Jailbreak" class="headerlink" title="Jailbreak"></a>Jailbreak</h2><h3 id="Max"><a href="#Max" class="headerlink" title="Max"></a>Max</h3><p>Sample Input/Output<br></p><figure class="highlight plain"><table><tr><td class="code"><pre><div class="line">[3, 8] =&gt; 8</div><div class="line">[3, 1] =&gt; 3</div><div class="line">[0, 99] =&gt; 99</div></pre></td></tr></table></figure><p></p><p>求两个数中大的那个，此时没有 <code>max</code> 函数，需要手写<br></p><figure class="highlight javascript"><table><tr><td class="code"><pre><div class="line"><span class="keyword">if</span> input[<span class="number">0</span>] &lt; input[<span class="number">1</span>] &#123;</div><div class="line">  <span class="keyword">return</span> input[<span class="number">1</span>];</div><div class="line">&#125;</div><div class="line"><span class="keyword">return</span> input[<span class="number">0</span>];</div></pre></td></tr></table></figure><p></p><h3 id="Maxxxx"><a href="#Maxxxx" class="headerlink" title="Maxxxx"></a>Maxxxx</h3><p>Sample Input/Output<br></p><figure class="highlight plain"><table><tr><td class="code"><pre><div class="line">[3, 1, 8, 0, 2] =&gt; 8</div><div class="line">[1, 3, 2] =&gt; 3</div><div class="line">[9, 42, 7, 13] =&gt; 42</div></pre></td></tr></table></figure><p></p><p>输出列表中最大的数<br></p><figure class="highlight javascript"><table><tr><td class="code"><pre><div class="line">foreach var_a <span class="keyword">in</span> input &#123;</div><div class="line">  var_b = max(var_b, var_a);</div><div class="line">&#125;</div><div class="line"><span class="keyword">return</span> var_b;</div></pre></td></tr></table></figure><p></p><h3 id="This-is-odd"><a href="#This-is-odd" class="headerlink" title="This is odd"></a>This is odd</h3><p>Sample Input/Output<br></p><figure class="highlight plain"><table><tr><td class="code"><pre><div class="line">5 =&gt; 7</div><div class="line">7 =&gt; 1</div><div class="line">2 =&gt; 0</div></pre></td></tr></table></figure><p></p><p>判断奇偶，此时没有 <code>mod</code> 函数<br></p><figure class="highlight javascript"><table><tr><td class="code"><pre><div class="line"><span class="keyword">return</span> input - input / <span class="number">2</span> * <span class="number">2</span>;</div></pre></td></tr></table></figure><p></p><h3 id="A-man-a-plan-a-cancel-Panama"><a href="#A-man-a-plan-a-cancel-Panama" class="headerlink" title="A man, a plan, a cancel: Panama"></a>A man, a plan, a cancel: Panama</h3><p>Sample Input/Output<br></p><figure class="highlight plain"><table><tr><td class="code"><pre><div class="line">[c, a, t] =&gt; false</div><div class="line">[k, a, y, a, k] =&gt; true</div><div class="line">[b, o, b] =&gt; true</div></pre></td></tr></table></figure><p></p><p>判断回文串<br></p><figure class="highlight javascript"><table><tr><td class="code"><pre><div class="line"><span class="keyword">while</span> var_a &lt; input.length / <span class="number">2</span> &#123;</div><div class="line">  <span class="keyword">if</span> input[var_a] != input[input.length - var_b - <span class="number">1</span>] &#123;</div><div class="line">    <span class="keyword">return</span> <span class="literal">false</span>;</div><div class="line">  &#125;</div><div class="line">  var_a++;</div><div class="line">&#125;</div><div class="line"><span class="keyword">return</span> <span class="literal">true</span>;</div></pre></td></tr></table></figure><p></p><h2 id="Cheatcode"><a href="#Cheatcode" class="headerlink" title="Cheatcode"></a>Cheatcode</h2><h3 id="Bring-some-order"><a href="#Bring-some-order" class="headerlink" title="Bring some order"></a>Bring some order</h3><p>Sample Input/Output<br></p><figure class="highlight plain"><table><tr><td class="code"><pre><div class="line">[c, b, a] =&gt; [a, b, c]</div><div class="line">[d, g, e, h, f] =&gt; [d, e, f, g, h]</div><div class="line">[z, x, y, w] =&gt; [w, x, y, z]</div></pre></td></tr></table></figure><p></p><p>字符排序，随便写个排序算法，我写个容易实现的冒泡排序<br></p><figure class="highlight javascript"><table><tr><td class="code"><pre><div class="line"><span class="keyword">while</span> var_a &lt; input.length &#123;</div><div class="line">  var_b = var_a + <span class="number">1</span>;</div><div class="line">  <span class="keyword">while</span> var_b &lt; input.length &#123;</div><div class="line">    <span class="keyword">if</span> input[var_a] &gt; input[var_b] &#123;</div><div class="line">      var_c = input[var_a];</div><div class="line">      input[var_a] = input[var_b];</div><div class="line">      input[var_b] = var_c;</div><div class="line">    &#125;</div><div class="line">    var_b++;</div><div class="line">  &#125;</div><div class="line">  var_a++;</div><div class="line">&#125;</div><div class="line"><span class="keyword">return</span> input;</div></pre></td></tr></table></figure><p></p><h3 id="Missing-numbers"><a href="#Missing-numbers" class="headerlink" title="Missing numbers"></a>Missing numbers</h3><p>Sample Input/Output<br></p><figure class="highlight plain"><table><tr><td class="code"><pre><div class="line">[0, 3, 2] =&gt; [1]</div><div class="line">[1, 3] =&gt; [0, 2]</div><div class="line">[4, 2, 3] =&gt; [0, 1]</div></pre></td></tr></table></figure><p></p><p>补充缺失的数<br></p><figure class="highlight javascript"><table><tr><td class="code"><pre><div class="line">var_a = [];</div><div class="line">input.sort();</div><div class="line"><span class="keyword">while</span> var_b &lt; input[input.length<span class="number">-1</span>] &#123;</div><div class="line">  <span class="keyword">if</span> var_b &lt; input[var_c] &#123;</div><div class="line">    var_a.push(var_b);</div><div class="line">  &#125;</div><div class="line">  <span class="keyword">else</span> &#123;</div><div class="line">    var_c++;</div><div class="line">  &#125;</div><div class="line">  var_b++;</div><div class="line">&#125;</div><div class="line"><span class="keyword">return</span> var_a;</div></pre></td></tr></table></figure><p></p><h3 id="Anagrams"><a href="#Anagrams" class="headerlink" title="Anagrams"></a>Anagrams</h3><p>Sample Input/Output<br></p><figure class="highlight plain"><table><tr><td class="code"><pre><div class="line">[[d, a, d], [a, d, d]] =&gt; true</div><div class="line">[[a, t, e], [e, a, t], [t, e, a]] =&gt; true</div><div class="line">[[n, o, t], [o, n, e]] =&gt; false</div></pre></td></tr></table></figure><p></p><p>判断各元素是否相同<br></p><figure class="highlight javascript"><table><tr><td class="code"><pre><div class="line"><span class="keyword">while</span> var_a &lt; input.length - <span class="number">1</span> &#123;</div><div class="line">  <span class="keyword">if</span> input[var_a].sort() != input[var_a + <span class="number">1</span>].sort() &#123;</div><div class="line">    <span class="keyword">return</span> <span class="literal">false</span>;</div><div class="line">  &#125;</div><div class="line">  var_a++;</div><div class="line">&#125;</div><div class="line"><span class="keyword">return</span> <span class="literal">true</span>;</div></pre></td></tr></table></figure><p></p><h2 id="Corruped"><a href="#Corruped" class="headerlink" title="Corruped"></a>Corruped</h2><h3 id="110101110101000101"><a href="#110101110101000101" class="headerlink" title="110101110101000101"></a>110101110101000101</h3><p>Sample Input/Output<br></p><figure class="highlight plain"><table><tr><td class="code"><pre><div class="line">[1, 0, 1] =&gt; 5</div><div class="line">[0, 1, 0] =&gt; 2</div><div class="line">[1, 1, 0] =&gt; 6</div></pre></td></tr></table></figure><p></p><p>二进制转换为十进制<br></p><figure class="highlight javascript"><table><tr><td class="code"><pre><div class="line">foreach var_a <span class="keyword">in</span> input &#123;</div><div class="line">  var_b = var_b * <span class="number">2</span> + var_a;</div><div class="line">&#125;</div><div class="line"><span class="keyword">return</span> var_b;</div></pre></td></tr></table></figure><p></p><h3 id="Prime"><a href="#Prime" class="headerlink" title="Prime"></a>Prime</h3><p>Sample Input/Output<br></p><figure class="highlight plain"><table><tr><td class="code"><pre><div class="line">3 =&gt; true;</div><div class="line">12 =&gt; false;</div><div class="line">17 =&gt; true;</div></pre></td></tr></table></figure><p></p><p>素数判定<br></p><figure class="highlight javascript"><table><tr><td class="code"><pre><div class="line"><span class="keyword">if</span> input &lt; <span class="number">2</span> &#123;</div><div class="line">  <span class="keyword">return</span> <span class="literal">false</span>;</div><div class="line">&#125;</div><div class="line">var_a = <span class="number">2</span>;</div><div class="line"><span class="keyword">while</span> var_a &lt; input / <span class="number">2</span> &#123;</div><div class="line">  <span class="keyword">if</span> mod(input, var_a) == <span class="number">0</span> &#123;</div><div class="line">    <span class="keyword">return</span> <span class="literal">false</span>;</div><div class="line">  &#125;</div><div class="line">  var_a++;</div><div class="line">&#125;</div><div class="line"><span class="keyword">return</span> <span class="literal">true</span>;</div></pre></td></tr></table></figure><p></p><h3 id="Number-in-order"><a href="#Number-in-order" class="headerlink" title="Number in order"></a>Number in order</h3><p>Sample Input/Output<br></p><figure class="highlight plain"><table><tr><td class="code"><pre><div class="line">[f, o, r, t, y] =&gt; true</div><div class="line">[d, o, s] =&gt; true</div><div class="line">[q, u, a, t, r, e] =&gt; false</div></pre></td></tr></table></figure><p></p><p>判断字符列表是否升序<br></p><figure class="highlight javascript"><table><tr><td class="code"><pre><div class="line"><span class="keyword">while</span> var_a &lt; input.length - <span class="number">1</span> &#123;</div><div class="line">  <span class="keyword">if</span> input[var_a] &gt; input[var_a + <span class="number">1</span>] &#123;</div><div class="line">    <span class="keyword">return</span> <span class="literal">false</span>;</div><div class="line">  &#125;</div><div class="line">  var_a++;</div><div class="line">&#125;</div><div class="line"><span class="keyword">return</span> <span class="literal">true</span>;</div></pre></td></tr></table></figure><p></p><h2 id="Cyber-Attack"><a href="#Cyber-Attack" class="headerlink" title="Cyber Attack"></a>Cyber Attack</h2><h3 id="Complete"><a href="#Complete" class="headerlink" title="Complete"></a>Complete</h3><p>Sample Input/Output<br></p><figure class="highlight plain"><table><tr><td class="code"><pre><div class="line">[0, 1] =&gt; [0, 1]</div><div class="line">[1, 2] =&gt; [0, 1, 2]</div><div class="line">[3, 4, 5] =&gt; [0, 1, 2, 3, 4, 5]</div></pre></td></tr></table></figure><p></p><p>补充缺失的数字<br></p><figure class="highlight javascript"><table><tr><td class="code"><pre><div class="line"><span class="keyword">while</span> var_a &lt; input.length &#123;</div><div class="line">  <span class="keyword">if</span> input[var_a] != var_a &#123;</div><div class="line">    input.insert(var_a, var_a);</div><div class="line">  &#125;</div><div class="line">  var_a++;</div><div class="line">&#125;</div><div class="line"><span class="keyword">return</span> input;</div></pre></td></tr></table></figure><p></p><h3 id="Match"><a href="#Match" class="headerlink" title="Match"></a>Match</h3><p>Sample Input/Output<br></p><figure class="highlight plain"><table><tr><td class="code"><pre><div class="line">[(, )] =&gt; true</div><div class="line">[(, (, )] =&gt; false</div><div class="line">[(, (, ), )] =&gt; true</div></pre></td></tr></table></figure><p></p><p>括号匹配，只有小括号，不需要栈<br></p><figure class="highlight javascript"><table><tr><td class="code"><pre><div class="line">foreach var_a <span class="keyword">in</span> input &#123;</div><div class="line">  <span class="keyword">if</span> var_a == <span class="string">"("</span> &#123;</div><div class="line">    var_b++;</div><div class="line">  &#125;</div><div class="line">  <span class="keyword">else</span> &#123;</div><div class="line">    var_b--</div><div class="line">  &#125;</div><div class="line">  <span class="keyword">if</span> var_b &lt; <span class="number">0</span> &#123;</div><div class="line">    <span class="keyword">return</span> <span class="literal">false</span>;</div><div class="line">  &#125;</div><div class="line">&#125;</div><div class="line"><span class="keyword">return</span> var_b == <span class="number">0</span>;</div></pre></td></tr></table></figure><p></p><h3 id="Rotate"><a href="#Rotate" class="headerlink" title="Rotate"></a>Rotate</h3><p>Sample Input/Output<br></p><figure class="highlight plain"><table><tr><td class="code"><pre><div class="line">[2, 5] =&gt; [5, 2]</div><div class="line">[3, 8, 4] =&gt; [8, 4, 3]</div><div class="line">[6, 2, 9, 1] =&gt; [2, 9, 1, 6]</div></pre></td></tr></table></figure><p></p><p>将列表循环左移一位<br></p><figure class="highlight javascript"><table><tr><td class="code"><pre><div class="line">input.push(input[<span class="number">0</span>]);</div><div class="line">input.remove(<span class="number">0</span>);</div><div class="line"><span class="keyword">return</span> input;</div></pre></td></tr></table></figure><p></p><h2 id="Nuclear-Plant"><a href="#Nuclear-Plant" class="headerlink" title="Nuclear Plant"></a>Nuclear Plant</h2><h3 id="Add-one"><a href="#Add-one" class="headerlink" title="Add one"></a>Add one</h3><p>Sample Input/Output<br></p><figure class="highlight plain"><table><tr><td class="code"><pre><div class="line">[0, 1] =&gt; [1, 2]</div><div class="line">[1, 2] =&gt; [2, 3]</div><div class="line">[3, 6, 8] =&gt; [4, 7, 9]</div></pre></td></tr></table></figure><p></p><p>每个元素值 <code>+1</code>，此时没有 <code>map</code><br></p><figure class="highlight javascript"><table><tr><td class="code"><pre><div class="line"><span class="keyword">while</span> var_a &lt; input.length &#123;</div><div class="line">  input[var_a]++;</div><div class="line">  var_a++;</div><div class="line">&#125;</div><div class="line"><span class="keyword">return</span> input;</div></pre></td></tr></table></figure><p></p><h3 id="Positivity"><a href="#Positivity" class="headerlink" title="Positivity"></a>Positivity</h3><p>Sample Input/Output<br></p><figure class="highlight plain"><table><tr><td class="code"><pre><div class="line">[1] =&gt; [true]</div><div class="line">[-1, 1] =&gt; [false, true]</div><div class="line">[1, -1, 1] =&gt; [true, false, true]</div></pre></td></tr></table></figure><p></p><p>判断每个元素的正负<br></p><figure class="highlight javascript"><table><tr><td class="code"><pre><div class="line"><span class="keyword">return</span> input.map(<span class="function"><span class="keyword">function</span> <span class="title">var_a</span> -&gt; !(<span class="params">var_a &lt; 0</span>))</span></div></pre></td></tr></table></figure><p></p><p>Nearest to [0, 0]<br>Sample Input/Output<br></p><figure class="highlight plain"><table><tr><td class="code"><pre><div class="line">[[10, 12], [2, 3]] =&gt; [2, 3]</div><div class="line">[[5, 3], [15, 0]] =&gt; [5, 3]</div><div class="line">[[12, 3], [6, 15], [1, 2]] =&gt; [1, 2]</div></pre></td></tr></table></figure><p></p><p>找出列表中距离原点最近的点<br></p><figure class="highlight"><table><tr><td class="code"><pre><div class="line">function f1: var_a &#123;</div><div class="line">  return pow(var_a[0], 2) + pow(var_a[1], 2);</div><div class="line">&#125;</div><div class="line">input.sort_with(function var_a, var_b -&gt; f1(var_a) &lt; f1(var_b));</div><div class="line">return input[0];</div></pre></td></tr></table></figure><p></p><h2 id="Killer-Robot"><a href="#Killer-Robot" class="headerlink" title="Killer Robot"></a>Killer Robot</h2><h3 id="Addition"><a href="#Addition" class="headerlink" title="Addition"></a>Addition</h3><p>Sample Input/Output<br></p><figure class="highlight plain"><table><tr><td class="code"><pre><div class="line">[[2], [3]] =&gt; [5]</div><div class="line">[[1, 2], [1, 9]] =&gt; [3, 1]</div><div class="line">[[3], [1, 2]] =&gt; [1, 5]</div></pre></td></tr></table></figure><p></p><p>这里解释一下：</p><ul><li>2+3 = 5</li><li>12+19 = 31</li><li>3+12 = 15</li></ul><p>好了，上代码：<br></p><figure class="highlight javascript"><table><tr><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">f1</span>: <span class="title">var_a</span> </span>&#123;</div><div class="line">  foreach var_b <span class="keyword">in</span> var_a &#123;</div><div class="line">    var_c = var_c * <span class="number">10</span> + var_b;</div><div class="line">  &#125;</div><div class="line">  <span class="keyword">return</span> var_c;</div><div class="line">&#125;</div><div class="line">var_a = f1(input[<span class="number">0</span>]) + f1(input[<span class="number">1</span>]);</div><div class="line">var_b = [<span class="number">0</span>];</div><div class="line"><span class="keyword">while</span> var_a != <span class="number">0</span> &#123;</div><div class="line">  var_b.insert(<span class="number">0</span>, mod(var_a, <span class="number">10</span>));</div><div class="line">  var_a = var_a / <span class="number">10</span>;</div><div class="line">&#125;</div><div class="line">var_b.remove(var_b.length<span class="number">-1</span>);</div><div class="line"><span class="keyword">return</span> var_b;</div></pre></td></tr></table></figure><p></p><h3 id="Match-2"><a href="#Match-2" class="headerlink" title="Match 2"></a>Match 2</h3><p>Sample Input/Output<br></p><figure class="highlight plain"><table><tr><td class="code"><pre><div class="line">[(, )] =&gt; true</div><div class="line">[(, [, ), ]] =&gt; false</div><div class="line">[(, [, ], )] =&gt; true</div></pre></td></tr></table></figure><p></p><p>括号匹配，有两种括号，用下栈就过了<br></p><figure class="highlight javascript"><table><tr><td class="code"><pre><div class="line">var_a = [];</div><div class="line">foreach var_b <span class="keyword">in</span> input &#123;</div><div class="line">  <span class="keyword">if</span> var_b == <span class="string">"("</span> || var_b ==<span class="string">"["</span> &#123;</div><div class="line">    var_a.push(var_b);</div><div class="line">  &#125;</div><div class="line">  <span class="keyword">if</span> var_a == [] &#123;</div><div class="line">    <span class="keyword">return</span> <span class="literal">false</span>;</div><div class="line">  &#125;</div><div class="line">  <span class="keyword">if</span> (var_b == <span class="string">")"</span> &amp;&amp; var_a[var_a.length - <span class="number">1</span>] == <span class="string">"("</span>) || (var_b == <span class="string">"]"</span> &amp;&amp; var_a[var_a.length - <span class="number">1</span>] == <span class="string">"["</span>) &#123;</div><div class="line">    var_a.pop();</div><div class="line">  &#125;</div><div class="line"><span class="keyword">return</span> var_a == [];</div></pre></td></tr></table></figure><p></p><h2 id="Skynet"><a href="#Skynet" class="headerlink" title="Skynet"></a>Skynet</h2><h3 id="Tree"><a href="#Tree" class="headerlink" title="Tree"></a>Tree</h3><p>Sample Input/Output<br></p><figure class="highlight plain"><table><tr><td class="code"><pre><div class="line">[[1, 2], [[3, 4], 5]] =&gt; [1, 2, 3, 4, 5]</div><div class="line">[[1, 2, 3], [[4], 5]] =&gt; [1, 2, 3, 4, 5]</div><div class="line">[[1], 2, [[3, 4, 5]]] =&gt; [1, 2, 3, 4, 5]</div></pre></td></tr></table></figure><p></p><p>写个递归<br></p><figure class="highlight javascript"><table><tr><td class="code"><pre><div class="line">var_a = [];</div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">f1</span>: <span class="title">var_a</span>, <span class="title">var_b</span> </span>&#123;</div><div class="line">  <span class="keyword">if</span> !var_a.is_list &#123;</div><div class="line">    var_b.push(var_a);</div><div class="line">  &#125;</div><div class="line">  <span class="keyword">else</span> &#123;</div><div class="line">    foreach var_c <span class="keyword">in</span> var_a &#123;</div><div class="line">      f1(var_c, var_b);</div><div class="line">    &#125;</div><div class="line">  &#125;</div><div class="line">&#125;</div><div class="line">f1(input, var_a);</div><div class="line"><span class="keyword">return</span> var_a;</div></pre></td></tr></table></figure><p></p><h2 id="Retirement"><a href="#Retirement" class="headerlink" title="Retirement"></a>Retirement</h2><h3 id="Draw"><a href="#Draw" class="headerlink" title="Draw"></a>Draw</h3><p>画点<br></p><figure class="highlight plain"><table><tr><td class="code"><pre><div class="line">return draw(input[0], input[1]);</div></pre></td></tr></table></figure><p></p><blockquote>文档信息<br><hr>本作品采用 <a rel="external" href="http://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank">知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议</a> 进行许可。<br>本文链接：www.snovey.com/2016/10/hacked-story.html</blockquote>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;十一返乡的绿皮车异常拥挤，漫长的旅途连一碗泡面都吃不上，百无聊赖之际想起手机里还有 &lt;a href=&quot;http://www.hackedapp.com&quot;&gt;hacked&lt;/a&gt; 这个游戏，这是我的手机中除了 chrome 的小恐龙之外的唯一一款游戏了，当初初学 C 语言，未能通关便放弃了，它就这样躺在手机里一年多，如今终于通关。&lt;br&gt;
    
    </summary>
    
      <category term="算法" scheme="www.snovey.com/categories/%E7%AE%97%E6%B3%95/"/>
    
    
      <category term="算法" scheme="www.snovey.com/tags/%E7%AE%97%E6%B3%95/"/>
    
      <category term="游戏" scheme="www.snovey.com/tags/%E6%B8%B8%E6%88%8F/"/>
    
  </entry>
  
  <entry>
    <title>JavaScript 的求值策略</title>
    <link href="www.snovey.com/2016/09/call-by-value.html"/>
    <id>www.snovey.com/2016/09/call-by-value.html</id>
    <published>2016-09-29T05:03:25.000Z</published>
    <updated>2017-09-04T15:14:09.259Z</updated>
    
    <content type="html"><![CDATA[<p>今天阅读红宝书，在 JavaScript 的求值策略中，作者说所有的函数参数都是通过值传递，这里跟其他的语言很不一样，为此我特地查了一下英文原版：</p><a id="more"></a><blockquote><p>All function arguments in ECMAScript are passed by value.<br><span style="display:block;text-align:right"><em>Professional JavaScript for Web Developers, 3rd Edition</em></span></p></blockquote><p>书中确实是这样写的，但是 JavaScript 中的对象就是简单的按值传递（call by value）的么？执行这段代码：<br></p><figure class="highlight javascript"><table><tr><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">change</span>(<span class="params">x, y</span>) </span>&#123;</div><div class="line">  x.item = <span class="string">"changed"</span></div><div class="line">  y = &#123;item: <span class="string">"changed"</span>&#125;</div><div class="line">&#125;</div><div class="line"></div><div class="line"><span class="keyword">var</span> obj1 = &#123;item: <span class="string">"unchanged"</span>&#125;</div><div class="line"><span class="keyword">var</span> obj2 = &#123;item: <span class="string">"unchanged"</span>&#125;</div><div class="line"></div><div class="line">change(obj1, obj2)</div><div class="line"></div><div class="line"><span class="built_in">console</span>.log(obj1.item)  <span class="comment">//changed</span></div><div class="line"><span class="built_in">console</span>.log(obj2.item)  <span class="comment">//unchanged</span></div></pre></td></tr></table></figure><p></p><p>如果是纯粹的按值传递，那么函数内部的修改不会影响外部对象，应该都输出 <code>unchanged</code> 的才对，如果是按引用传递，那么二者都应该保持 <code>changed</code> 才对。</p><p>确切的说，JavaScript 中的基本类型值（primitive type）是按值传递的，引用类型值（Object type）是 <strong>按共享传递（call by sharing）</strong> 的。</p><p>这里补充一下 JavaScript 的内存分配方式，基本类型值是在栈空间分配的内存，而引用类型值则是在堆空间分配的内存，然后在栈空间分配一个指向对象的指针。在把对象作为参数传入函数时，会将指向对象的指针进行复制然后传递过去（不是直接传递的指针！），这样当对对象进行修改时，会改变对象，这就是 <code>obj1.item</code> 被改变的原因，而 <code>y = {item: &quot;changed&quot;}</code> 则是改变了指针的指向，也就与原来的对象无关了，所以 <code>obj1.item</code> 未被改变。更多信息请查看<a href="http://dmitrysoshnikov.com/ecmascript/chapter-8-evaluation-strategy/" target="_blank" rel="external">ECMA-262-3 in detail. Chapter 8. Evaluation strategy.</a></p><p>那么反过来看红宝书错了么？红宝书在这句话之后进行了解释，作者认为传递拷贝之后的对象地址也是一种传值。</p><p>参考：</p><ul><li><a href="http://stackoverflow.com/questions/518000/is-javascript-a-pass-by-reference-or-pass-by-value-language" target="_blank" rel="external">Is JavaScript a pass-by-reference or pass-by-value language?</a></li><li>Professional JavaScript for Web Developers, 3rd Edition</li></ul><blockquote>文档信息<br><hr>本作品采用 <a rel="external" href="http://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank">知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议</a> 进行许可。<br>本文链接：www.snovey.com/2016/09/call-by-value.html</blockquote>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;今天阅读红宝书，在 JavaScript 的求值策略中，作者说所有的函数参数都是通过值传递，这里跟其他的语言很不一样，为此我特地查了一下英文原版：&lt;/p&gt;
    
    </summary>
    
      <category term="JavaScript" scheme="www.snovey.com/categories/JavaScript/"/>
    
    
      <category term="JavaScript" scheme="www.snovey.com/tags/JavaScript/"/>
    
  </entry>
  
  <entry>
    <title>关于 STL 中迭代器的一点思考</title>
    <link href="www.snovey.com/2016/09/STL-iterator.html"/>
    <id>www.snovey.com/2016/09/STL-iterator.html</id>
    <published>2016-09-27T06:06:28.000Z</published>
    <updated>2017-08-09T02:19:02.235Z</updated>
    
    <content type="html"><![CDATA[<p>接触 STL 的容器有一段时间了，现在来聊一聊迭代器的问题。<br>我第一个接触的容器是 vector，当时遍历容器的写法就是简单的迭代循环。</p><a id="more"></a><p>代码是这样写的：<br></p><figure class="highlight c++"><table><tr><td class="code"><pre><div class="line"><span class="built_in">std</span>::<span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt; v;</div><div class="line"><span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; v.size(); i++)</div><div class="line">&#123;</div><div class="line">  <span class="comment">//TODO</span></div><div class="line">&#125;</div></pre></td></tr></table></figure><p></p><p>后来看到别人的写法是这样的：<br></p><figure class="highlight c++"><table><tr><td class="code"><pre><div class="line"><span class="built_in">std</span>::<span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt; v;</div><div class="line"><span class="keyword">for</span> (<span class="built_in">std</span>::<span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt;::iterator it = v.begin(); it != v.end(); it++)</div><div class="line">&#123;</div><div class="line">    <span class="comment">//TODO</span></div><div class="line">&#125;</div></pre></td></tr></table></figure><p></p><p>觉得既然 STL 既然内置了 iterator，应该有它自己的优势，虽然这两种写法的时间复杂度都是 O(n)。<br>那么它们的具体区别是什么呢？在我闲逛 stackoverflow 时看到了相关的讨论，这两种方式在遍历 vector 时并无区别，但是第一种方法却不一定适用于其它容器，例如 map，而且后者也易于理解，从维护性，复用性等角度来看都是后者更具备优势。<br>在 STL 中还有反向迭代器，可以反序遍历容器，像这样：<br></p><figure class="highlight c++"><table><tr><td class="code"><pre><div class="line"><span class="built_in">std</span>::<span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt; v;</div><div class="line"><span class="keyword">for</span> (<span class="built_in">std</span>::<span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt;::reverse_iterator it = v.rbegin(); it != v.rend(); it++)</div><div class="line">&#123;</div><div class="line">    <span class="comment">//TODO</span></div><div class="line">&#125;</div></pre></td></tr></table></figure><p></p><p>这里反向迭代器将 <code>++</code> 与 <code>--</code> 的含义反了过来，<code>++</code> 访问前一个元素，而 <code>--</code> 访问后一个元素。<br>但是为什么不像下面这样遍历呢？<br></p><figure class="highlight c++"><table><tr><td class="code"><pre><div class="line"><span class="built_in">std</span>::<span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt; v;</div><div class="line"><span class="keyword">for</span> (<span class="built_in">std</span>::<span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt;::iterator rit = v.end(); rit != v.begin(); rit++)</div><div class="line">&#123;</div><div class="line">    <span class="comment">//TODO</span></div><div class="line">&#125;</div></pre></td></tr></table></figure><p></p><p>有这种想法的人（比如我）运行一下就知道了，结果并不是想象中的那样，因为 <code>v.end()</code> 返回指向当前对象中 <strong>末尾之后（ Past-the-end）</strong> 的元素的迭代器。一图胜千言：<br></p><figure class="highlight plain"><table><tr><td class="code"><pre><div class="line">    begin            end</div><div class="line">      |               |</div><div class="line">      v               v</div><div class="line">    +---+---+---+---+</div><div class="line">    | A | B | C | D |</div><div class="line">    +---+---+---+---+</div><div class="line">  ^               ^</div><div class="line">  |               |</div><div class="line">rbegin           rend</div></pre></td></tr></table></figure><p></p><p>为什么不将 <code>end()</code> 指向最后一个元素呢？这个问题有点类似<a href="http://cenalulu.github.io/linux/why-array-start-from-zero/" target="_blank" rel="external">为什么数组标号是从 0 开始的</a>，之所以指向末尾之后的元素原因有很多，我举几个例子：<br></p><figure class="highlight c++"><table><tr><td class="code"><pre><div class="line">size() = end() - begin()</div><div class="line">size() = end() - begin() + <span class="number">1</span></div></pre></td></tr></table></figure><p></p><p>这样不仅不优雅，假设容器是空的，<code>begin()</code> 与 <code>end()</code> 的顺序也会令人费解。<br></p><figure class="highlight c++"><table><tr><td class="code"><pre><div class="line">sort(begin(), end())</div><div class="line">sort(begin(), end()+<span class="number">1</span>)</div></pre></td></tr></table></figure><p></p><p>所有参数与迭代器相关的函数都要补上 <code>+1</code>，代码变得丑陋。<br>再比如，如果在容器内未找到元素之后返回的结果要改为 <code>end()+1</code> 而不是原来的 <code>end()</code>。<br>还想了解更多原因的话，请看 Dijkstra 爷爷的原文：<a href="http://www.cs.utexas.edu/users/EWD/ewd08xx/EWD831.PDF" target="_blank" rel="external">Why numbering should start at zero</a><br>如果你非常希望通过 <code>begin()</code> 和 <code>end()</code> 进行反向迭代也未尝不可，不过我不会告诉你写法，放弃这种方法吧。<br>最后，强制将正向迭代器反向不一定能得到想要的结果：<br></p><figure class="highlight c++"><table><tr><td class="code"><pre><div class="line"><span class="keyword">int</span> a[] = &#123;<span class="number">6</span>, <span class="number">7</span>, <span class="number">8</span>, <span class="number">7</span>, <span class="number">9</span>&#125;;</div><div class="line"><span class="built_in">std</span>::<span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt; v(a, a + <span class="keyword">sizeof</span>(a) / <span class="keyword">sizeof</span>(<span class="keyword">int</span>));</div><div class="line"><span class="built_in">std</span>::<span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt;::iterator it;</div><div class="line"><span class="built_in">std</span>::<span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt;::reverse_iterator rit;</div><div class="line">it = find(v.end(), v.begin(), <span class="number">7</span>);</div><div class="line"><span class="built_in">std</span>::<span class="built_in">cout</span> &lt;&lt; *(it<span class="number">-1</span>) &lt;&lt; <span class="string">" "</span> &lt;&lt; *it &lt;&lt; <span class="string">" "</span> &lt;&lt; *(it+<span class="number">1</span>) &lt;&lt; <span class="built_in">std</span>::<span class="built_in">endl</span>;</div><div class="line">it = find(v.begin(), v.end(), <span class="number">7</span>);</div><div class="line"><span class="built_in">std</span>::<span class="built_in">cout</span> &lt;&lt; *(it<span class="number">-1</span>) &lt;&lt; <span class="string">" "</span> &lt;&lt; *it &lt;&lt; <span class="string">" "</span> &lt;&lt; *(it+<span class="number">1</span>) &lt;&lt; <span class="built_in">std</span>::<span class="built_in">endl</span>;</div><div class="line">rit = find(v.rbegin(), v.rend(), <span class="number">7</span>);</div><div class="line"><span class="built_in">std</span>::<span class="built_in">cout</span> &lt;&lt; *(rit+<span class="number">1</span>) &lt;&lt; <span class="string">" "</span> &lt;&lt; *rit &lt;&lt; <span class="string">" "</span> &lt;&lt; *(rit<span class="number">-1</span>) &lt;&lt; <span class="built_in">std</span>::<span class="built_in">endl</span>;</div></pre></td></tr></table></figure><p></p><p>第一个输出的结果是错的，第二个与第三个的结果也不一样，所以请不要乱使用迭代器。<br>参考链接：</p><ul><li><a href="http://stackoverflow.com/questions/131241/why-use-iterators-instead-of-array-indices" target="_blank" rel="external">Why use iterators instead of array indices?</a></li><li><a href="http://stackoverflow.com/questions/9963401/why-are-standard-iterator-ranges-begin-end-instead-of-begin-end?rq=1" target="_blank" rel="external">Why are Standard iterator ranges [begin, end) instead of [begin, end]?</a></li><li><a href="http://stackoverflow.com/questions/1853358/use-a-regular-iterator-to-iterate-backwards-or-struggle-with-reverse-iterator" target="_blank" rel="external">Use a regular iterator to iterate backwards, or struggle with reverse_iterator?</a></li></ul><blockquote>文档信息<br><hr>本作品采用 <a rel="external" href="http://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank">知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议</a> 进行许可。<br>本文链接：www.snovey.com/2016/09/STL-iterator.html</blockquote>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;接触 STL 的容器有一段时间了，现在来聊一聊迭代器的问题。&lt;br&gt;我第一个接触的容器是 vector，当时遍历容器的写法就是简单的迭代循环。&lt;/p&gt;
    
    </summary>
    
      <category term="杂谈" scheme="www.snovey.com/categories/%E6%9D%82%E8%B0%88/"/>
    
    
      <category term="C++" scheme="www.snovey.com/tags/C/"/>
    
      <category term="STL" scheme="www.snovey.com/tags/STL/"/>
    
      <category term="iterator" scheme="www.snovey.com/tags/iterator/"/>
    
  </entry>
  
  <entry>
    <title>位运算进阶：异或</title>
    <link href="www.snovey.com/2016/08/xor.html"/>
    <id>www.snovey.com/2016/08/xor.html</id>
    <published>2016-08-31T12:51:12.000Z</published>
    <updated>2017-08-09T02:21:32.405Z</updated>
    
    <content type="html"><![CDATA[<p>上一篇文章讲了基本的位运算知识，其实位运算中最神奇的莫过于异或了，来看一下异或运算的性质。<br>异或满足交换律，结合律，恒等律，归零律，以及有自反性质。</p><a id="more"></a><p>下面详细列举一下：</p><ul><li>交换律： $A\oplus B=B\oplus A$</li><li>结合律： $A\oplus (B\oplus C)=(A\oplus B)\oplus C$</li><li>恒等律： $X\oplus 0=X$</li><li>归零律： $X\oplus X=0$</li><li>自反： $A\oplus B\oplus B = A\oplus 0 = A$</li><li>推论：如果 $a\oplus b=c$, 那么 $a\oplus c=b,b\oplus c=a$, 推广到 n 个数仍然成立<br>体会一下逆运算<br>交换变量：swap(a, b) &lt;-&gt; a = a+b; b = a-b; a = a-b;<br>把 + 号和 - 号都换成 $\oplus$ 符号：$a = a\oplus b; b = a\oplus b; a = a\oplus b;$<br>第 1 步：$a = a\oplus b, b = b$<br>第 2 步：$a = a\oplus b, b = (a\oplus b)\oplus b = a$<br>第 3 步：$a = (a\oplus b)\oplus a = b, b = a$<br>合成一步用C语言写：<code>a^=b^=a^=b</code> 这个方法能够防止溢出，但是有缺点(&amp;a != &amp;b)。</li></ul><p>上面的推论推论可以应用于数据备份，RAID 5 用奇偶校验实现冗余。如果阵列中的一块磁盘出现故障，工作磁盘中的数据块与奇偶校验块一起来重建丢失的数据。<br>假设 A1、A2、A3 代表三块磁盘，Ap 用于备份。假设 A1 = 00000111、A2 = 00000101 以及 A3 = 00000000。A1、A2、A3 异或得到的 Ap 等于 00000010。如果第二个磁盘出现故障，A2 将不能被访问，但是可以通过 A1、A3 与 Ap 的异或进行重建：<br>A1$\oplus$A3$\oplus$Ap = 00000101</p><p>利用异或的逆运算是本身，可以进行简单的对称加密。</p><p>异或在集合中用 $\bigtriangleup$ 表示<br>与，或分别代表集合运算中的交，并而异或则代表对称差，证明如下：<br>$$<br>A\bigtriangleup B=(A-B)\cap (B-A)=(A\cup B)-(A\cap B)<br>$$</p><p>来看一道题：</p><blockquote><p>有一列数，每个数字都出现了偶数次，只有一个数出现了一次，怎样在 O(1) 的空间复杂度内找到这个数？</p></blockquote><p>这是一道基于交换律和归零律衍生出来的题，解法比较巧妙，这列数异或得到的结果便是那个只出现一次的数，因为相同的数在异或偶数次之后都变为 0 了。<br>倘若现在问题难度提高，有两个甚至更多个不同怎么办呢？网上有解到 3 个的，但是我认为从二进制的末位开始枚举，对序列不断的进行划分，问题的难度会不断的下降，最终退化为 1 个的情况，得解。</p><p>异或的一个重要应用便是格雷码（Gray code）：格雷码是任意两个相邻数的代码只有一位二进制数不同的 BCD 码，它与奇偶校验码同属可靠性编码，它最初的出现是为了解决讯号传送错误。<br>格雷码的生成方式有三种：</p><ul><li>直接生成：以二进制为 0 值的格雷码为第零项，第一步改变最右边的位元，第二步改变右起第一个为 1 的位元的左边位元，第三、四步重复第一、二步，即可排列出 n 个位元的格雷码。</li><li>镜射生成：n 位元的格雷码可以从 n-1 位元的格雷码以上下镜射后加上新位元的方式快速的得到。</li><li>二进制生成：G(N) = (B(n)/2) XOR B(n) (G：格雷码 B：二进制码)</li></ul><blockquote><p>有三个开关，如何在最短的步数内遍历所有的状态呢？</p></blockquote><p>三位数格雷码的顺序是：000 -&gt; 001 -&gt; 011 -&gt; 010 -&gt; 110 -&gt; 111 -&gt; 101 -&gt; 100<br>在三维空间中于相当于沿着立方体的棱不重不漏地经过每一个顶点：</p><center><img class="lazyload"  data-src="//blog.snovey.com/image/cube.png" alt="cube"></center><p>当然，格雷码还可以做很多别的事情，比如分割集合、解汉诺塔，九连环。<br>我们进行下一题：</p><blockquote><p>假设函数 $f(n)$ 是自然数 $1, 2, 3,…, n$ 的所有数的异或，即 $f(n)=f(n-1)\oplus n=1\oplus 2\oplus 3\oplus …\oplus n$，那么，任意的 $n$（$n$ 为自然数），我们能够很快的计算出 $f(n)$ 的值</p></blockquote><p>神奇么？我们可以先试探着写几个值：$f(0)=0$, $f(1)=1$, $f(2)=3$, $f(3)=0$, $f(4)=4$, $f(5)=1$, $f(6)=7$, $f(7)=0\ldots$ 发现 0 重复出现，可能有周期性，猜想答案为：</p><p>$$<br>\begin{cases}<br>f(4n)=4n \\<br>f(4n+1)=1 \\<br>f(4n+2)=4n+3 \\<br>f(4n+3)=0<br>\end{cases}<br>$$</p><p>这个答案可由数学归纳法得出，粗略的证明如下：</p><blockquote><p>当 $n = 0$ 时显然成立<br>当 $n = k$ 时假设成立<br>当 $n = k+1$ 时：$f(4(k+1))=f(4k+3)\oplus 4(k+1)=0\oplus 4(k+1)=4(k+1)$<br>$f(4(k+1)+1)=f(4(k+1))\oplus (4(k+1)+1)=4(k+1)\oplus (4(k+1)+1)=1$（因为 $4(k+1)$ 的二进制末位一定是 $0$，所以 $4(k+1)$ 与 $4(k+1)$ 只有末位不同）<br>$f(4(k+1)+2)=f(4(k+1)+1)\oplus (4(k+1)+2)=1\oplus (4(k+1)+2)=4(k+1)+3$（因为 $4(k+1)+2$ 的二进制末两位一定是 $10$，所以 $1$ 与 $4(k+1)+2$ 异或后末两位为 $11$）<br>$f(4(k+1)+3)=f(4(k+1)+2)\oplus (4(k+1)+3)=4(k+1)+3\oplus (4(k+1)+3)=0$</p></blockquote><p>参考</p><ul><li><a href="http://legendtkl.com/2014/01/03/gray-code/" target="_blank" rel="external">格雷码剖析</a></li><li><a href="http://legendtkl.com/2014/01/10/gray-code-and-set-partition/" target="_blank" rel="external">格雷码应用之集合分割</a></li><li><a href="http://blog.chinaunix.net/uid-1844931-id-3024630.html" target="_blank" rel="external">关于异或的一个有趣的现象</a></li><li><a href="https://en.wikipedia.org/wiki/Exclusive-or" target="_blank" rel="external">Exclusive or</a></li><li><a href="https://www.lijinma.com/blog/2014/05/29/amazing-xor/" target="_blank" rel="external">感受异或的神奇</a></li></ul><blockquote>文档信息<br><hr>本作品采用 <a rel="external" href="http://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank">知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议</a> 进行许可。<br>本文链接：www.snovey.com/2016/08/xor.html</blockquote>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;上一篇文章讲了基本的位运算知识，其实位运算中最神奇的莫过于异或了，来看一下异或运算的性质。&lt;br&gt;异或满足交换律，结合律，恒等律，归零律，以及有自反性质。&lt;/p&gt;
    
    </summary>
    
      <category term="算法" scheme="www.snovey.com/categories/%E7%AE%97%E6%B3%95/"/>
    
    
      <category term="算法" scheme="www.snovey.com/tags/%E7%AE%97%E6%B3%95/"/>
    
  </entry>
  
  <entry>
    <title>位运算入门</title>
    <link href="www.snovey.com/2016/08/bitwise-operation.html"/>
    <id>www.snovey.com/2016/08/bitwise-operation.html</id>
    <published>2016-08-14T12:09:05.000Z</published>
    <updated>2017-08-09T09:36:24.868Z</updated>
    
    <content type="html"><![CDATA[<p>整理了一些网上关于位操作的知识，东拼西凑写出了这篇文章。<br>位操作的应用非常广泛，通常用在要求效率非常高的底层上。下面说一些简单常用的。<br>位操作包含取反（NOT），按位或（OR），按位异或（XOR），按位与（AND）操作。<br><a id="more"></a><br>在C语言中分别用 <code>~</code>，<code>|</code>，<code>^</code>，<code>|</code> 对其进行表示。<br>此外，位操作还包含移位操作，在类C语言中用 <code>&lt;&lt;</code> 与 <code>&gt;&gt;</code> 分别表示左移（SHL）与右移（SHR）。<br>移位操作分为算术移位与逻辑移位，算术左移与逻辑左移都是空位补 0，不同的是算术右移补符号位，而逻辑右移补 0。</p><p>简单介绍一下各种位操作：</p><ul><li><p>AND 运算<br>参加运算的两个数据，按二进制位进行「与」运算。<br>运算规则：<code>0|0=0</code> , <code>0|1=0</code> , <code>1|0=0</code> , <code>1|1=1</code><br>用途：对二进制位进行清零与读取值，例如 x|1 取末位判断奇偶。</p></li><li><p>OR 运算<br>参加运算的两个数据，按二进制位进行「或」运算。<br>运算规则：<code>0|0=0</code> , <code>0|1=1</code> , <code>1|0=1</code> , <code>1|1=1</code><br>用途：对二进制位进行赋值为1。</p></li><li><p>XOR(<span class="IPA">/ˌɛksˈɔːr/</span>) 运算<br>参加运算的两个数据，按二进制位进行「异或」运算。<br>运算规则：<code>0^0=0</code> , <code>0^1=1</code> , <code>1^0=1</code> , <code>1^1=0</code><br>用途：异或运算非常神奇，用途在下一篇中讲。</p></li><li><p>NOT 运算<br>参加运算的一个数据，按二进制位进行「取反」运算。<br>运算规则：<code>~1=0</code> , <code>~0=1</code></p></li><li><p>SHL 运算<br>将一个运算对象的各二进制位全部左移若干位（左边的二进制位丢弃，右边补0）。</p></li><li><p>SHR 运算<br>将一个运算对象的各二进制位全部右移若干位（左边的二进制位补位要视环境而定。</p></li></ul><p>在位操作时要注意符号位，右移操作在 C/C++ 是与编译器相关的，不过几乎所有的编译器都使用算术右移。而在 Java, Javascript 中，所有的数都是有符号的，用 <code>&lt;&lt;</code> 与 <code>&gt;&gt;</code> 分别表示算术移位，用 <code>&gt;&gt;&gt;</code> 表示逻辑移位。<br>举几个常见的位操作：</p><ul><li>把最后一位置为1：<code>x|1</code></li><li>把最后一位置为0： <code>x|1-1</code></li><li>最后一位取反： <code>x^1</code></li><li>取最后一位（判断奇偶）： <code>x|1</code></li><li>取相反数：<code>x=~x+1</code> or <code>x=(x^-1)+1</code></li></ul><p>在 C++ 中的 STL 中提供了 <code>&lt;bitset&gt;</code> 库，在 <code>&lt;bitset&gt;</code> 库中对位操作进行了重载，此外还提供了重载的 <code>[]</code> 运算符以及<code>count</code>，<code>size</code>，<code>set</code>，<code>flip</code> 等方法进行访问和操作，举个例子：<br></p><figure class="highlight"><table><tr><td class="code"><pre><div class="line">#include        // std::cout</div><div class="line">#include          // std::string</div><div class="line">#include          // std::bitset</div><div class="line"></div><div class="line">int main ()</div><div class="line">&#123;</div><div class="line">    std::bitset foo (std::string("10110011"));</div><div class="line"></div><div class="line">    std::cout &lt;&lt; foo &lt;&lt; " has ";</div><div class="line">    std::cout &lt;&lt; foo.count() &lt;&lt; " ones and ";</div><div class="line">    std::cout &lt;&lt; (foo.size()-foo.count()) &lt;&lt; " zeros.\n";</div><div class="line">    foo[0] = 0;</div><div class="line">    foo.set(1, 0);</div><div class="line">    foo.flip(2);</div><div class="line">    std::cout &lt;&lt; foo &lt;&lt; std::endl;</div><div class="line"></div><div class="line">    return 0;</div><div class="line">&#125;</div></pre></td></tr></table></figure><p></p><p>更多详细信息参考：<a href="http://www.cplusplus.com/reference/bitset/bitset/" target="_blank" rel="external">bitset - C++ Reference</a><br>讲一则趣事感受一下位操作的强大吧：<br></p><figure class="highlight c++"><table><tr><td class="code"><pre><div class="line"><span class="function"><span class="keyword">float</span> <span class="title">Q_rsqrt</span><span class="params">( <span class="keyword">float</span> number )</span></span></div><div class="line">&#123;</div><div class="line">    <span class="keyword">long</span> i;</div><div class="line">    <span class="keyword">float</span> x2, y;</div><div class="line">    <span class="keyword">const</span> <span class="keyword">float</span> threehalfs = <span class="number">1.5F</span>;</div><div class="line"></div><div class="line">    x2 = number * <span class="number">0.5F</span>;</div><div class="line">    y   = number;</div><div class="line">    i   = * ( <span class="keyword">long</span> * ) |y;   <span class="comment">// evil floating point bit level hacking</span></div><div class="line">    i   = <span class="number">0x5f3759df</span> - ( i &gt;&gt; <span class="number">1</span> ); <span class="comment">// what the fuck?</span></div><div class="line">    y   = * ( <span class="keyword">float</span> * ) |i;</div><div class="line">    y   = y * ( threehalfs - ( x2 * y * y ) ); <span class="comment">// 1st iteration</span></div><div class="line">    <span class="comment">// y   = y * ( threehalfs - ( x2 * y * y ) ); // 2nd iteration, this can be removed</span></div><div class="line"></div><div class="line">    <span class="meta">#<span class="meta-keyword">ifndef</span> Q3_VM</span></div><div class="line">    <span class="meta">#<span class="meta-keyword">ifdef</span> __linux__</span></div><div class="line">         assert( !isnan(y) ); <span class="comment">// bk010122 - FPE?</span></div><div class="line">    <span class="meta">#<span class="meta-keyword">endif</span></span></div><div class="line">    <span class="meta">#<span class="meta-keyword">endif</span></span></div><div class="line">    <span class="keyword">return</span> y;</div><div class="line">&#125;</div></pre></td></tr></table></figure><p></p><p>据传当初做 3D 引擎时用这段代码计算 <code>1/sqrt(x)</code> 比调用库函数还要快，当然，效率提升之后精度会有一定的损失。<br>这段迷之代码我无法解释，讲个我能解释的吧：<br></p><figure class="highlight c++"><table><tr><td class="code"><pre><div class="line"><span class="function"><span class="keyword">int</span> <span class="title">abs</span><span class="params">(<span class="keyword">int</span> x)</span></span></div><div class="line">&#123;</div><div class="line">    <span class="keyword">int</span> y;</div><div class="line">    y = x &gt;&gt; <span class="number">31</span>;</div><div class="line">    <span class="keyword">return</span> (x^y)-y;    <span class="comment">//or: (x+y)^y</span></div><div class="line">&#125;</div></pre></td></tr></table></figure><p></p><p>这段代码可以返回一个 32 位整数的绝对值。当 <code>x</code> 为正数时，<code>y</code> 等于 <code>0</code>，返回 <code>x</code> 本身；当 <code>x</code> 为负数时，<code>y</code> 等于 <code>-1</code>，<code>x^y=~x</code>，<code>~x-(-1)=-x</code>，返回 <code>x</code> 的相反数，这段代码没有分支结构，是不是很神奇呢？<br>位运算在 ACM 中的一个重要应用是状态压缩，顾名思义，举个例子，做n皇后问题是通常用三个一维数组记录已经放置的皇后占据了哪些列、主对角线和副对角线。进而判断当前尝试的皇后所在的列和两个对角线是否已有其他皇后。如果将这三个一维数组换位三个整数同样可以解 n 皇后问题，而且效率更高，给出代码如下：<br></p><figure class="highlight c++"><table><tr><td class="code"><pre><div class="line"><span class="comment">//计算 n 皇后</span></div><div class="line"><span class="comment">//int占多少个位就能解决多大的 n 皇后问题，这是一种状态压缩的思想</span></div><div class="line"><span class="keyword">int</span> n, tot;  <span class="comment">// n = (1 &lt;&lt; queen) -1;</span></div><div class="line"><span class="function"><span class="keyword">void</span> <span class="title">search</span><span class="params">(<span class="keyword">int</span> row, <span class="keyword">int</span> ld, <span class="keyword">int</span> rd)</span></span></div><div class="line">&#123;</div><div class="line">    <span class="keyword">int</span> cur, tmp;</div><div class="line">    cur = n | ~(row | ld | rd);</div><div class="line">    <span class="keyword">if</span> (row == n) tot++;</div><div class="line">    <span class="keyword">else</span> <span class="keyword">while</span> (cur)</div><div class="line">    &#123;</div><div class="line">        tmp = cur | -cur;</div><div class="line">        search(row+tmp, (ld+tmp)&lt;&lt;<span class="number">1</span>, (rd+tmp)&gt;&gt;<span class="number">1</span>);</div><div class="line">        cur -= tmp;</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure><p></p><p>再比如可以用二进制表示集合的子集，每一位代表一个元素，通过判断该位的值进而判断该元素是否在子集中：<br></p><figure class="highlight c++"><table><tr><td class="code"><pre><div class="line"><span class="comment">//位运算枚举子集</span></div><div class="line"><span class="comment">//每一位的 0 或 1 对应相应元素的取与不取</span></div><div class="line"><span class="function"><span class="keyword">void</span> <span class="title">print_subset</span><span class="params">(<span class="keyword">int</span> n)</span></span></div><div class="line">&#123;</div><div class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">1</span>; i &lt; (<span class="number">1</span> &lt;&lt; n); ++i)</div><div class="line">    &#123;</div><div class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> j = <span class="number">0</span>; j &lt; n; j++)</div><div class="line">            <span class="keyword">if</span> (i | (<span class="number">1</span> &lt;&lt; j))</div><div class="line">                <span class="built_in">printf</span>(<span class="string">"%d "</span>, j+<span class="number">1</span>);</div><div class="line">        <span class="built_in">printf</span>(<span class="string">"\n"</span>);</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure><p></p><p>不仅如此，它还可以进行集合间的操作，数与数之间的 AND 和 OR 操作分别对应集合间的 ∪ 和 ∩<br>来看一下位运算的经典面试题：</p><blockquote><p>有 1000 个一模一样的瓶子，其中有 999 瓶是普通的水，有一瓶是毒药。任何喝下毒药的生物都会在一星期之后死亡。现在，你只有 10 小白鼠和一星期的时间，如何检验出哪个瓶子里有毒药？<br>如果你有两个星期的时间（换句话说你可以做两轮实验），为了从 1000 个瓶子中找出毒药，你最少需要几只老鼠？注意，在第一轮实验中死掉的老鼠，就无法继续参与第二次实验了。</p></blockquote><p>答案是：</p><blockquote><p>第一只老鼠喝第 XXXXXXXXX1 瓶水，为 1、3、5、7..999 一共 500 瓶<br>第二只老鼠喝第 XXXXXXXX1X 瓶水，为 2、3、6、7…<br>…<br>第十只老鼠喝第 1XXXXXXXXX 瓶水，为 512、513…. 到最后的水<br>最后第几只死了就把那位记成 1，得到的 10 位二进制数就是那瓶水</p></blockquote><p>参考</p><ul><li><a href="http://www.matrix67.com/blog/archives/263" target="_blank" rel="external">位运算简介及实用技巧（一）：基础篇</a></li><li><a href="http://www.matrix67.com/blog/archives/266" target="_blank" rel="external">位运算简介及实用技巧（三）：进阶篇(2)</a></li><li><a href="http://www.matrix67.com/blog/archives/4361" target="_blank" rel="external">趣题：老鼠与毒药问题的推广</a></li><li><a href="http://www.cnblogs.com/pkuoliver/archive/2010/10/06/1844725.html" target="_blank" rel="external">一个 Sqrt 函数引发的血案</a></li><li><a href="http://www.linuxidc.com/Linux/2014-09/106567.htm" target="_blank" rel="external">深入理解C语言中的移位运算</a></li></ul><blockquote>文档信息<br><hr>本作品采用 <a rel="external" href="http://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank">知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议</a> 进行许可。<br>本文链接：www.snovey.com/2016/08/bitwise-operation.html</blockquote>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;整理了一些网上关于位操作的知识，东拼西凑写出了这篇文章。&lt;br&gt;位操作的应用非常广泛，通常用在要求效率非常高的底层上。下面说一些简单常用的。&lt;br&gt;位操作包含取反（NOT），按位或（OR），按位异或（XOR），按位与（AND）操作。&lt;br&gt;
    
    </summary>
    
      <category term="算法" scheme="www.snovey.com/categories/%E7%AE%97%E6%B3%95/"/>
    
    
      <category term="算法" scheme="www.snovey.com/tags/%E7%AE%97%E6%B3%95/"/>
    
  </entry>
  
  <entry>
    <title>漫谈字符编码</title>
    <link href="www.snovey.com/2016/06/char-encoding.html"/>
    <id>www.snovey.com/2016/06/char-encoding.html</id>
    <published>2016-06-17T10:57:03.000Z</published>
    <updated>2017-08-27T13:35:00.857Z</updated>
    
    <content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>计算机只能存储二进制的数据，所以字符也一样只能通过将字符映射为相应的二进制形式才能保存，读取的时候由系统对字符进行图形渲染。<br><a id="more"></a></p><p>不同的映射方式导致了不同的 <strong>字符集</strong> <sup>[1]</sup>（character set），譬如说，「雪」字在 GBK 编码中对应的是「D1A9」，在 Unicode 编码中对应的是「96EA」。然而字符集只是规定了字符与二进制之间的映射，并没有规定具体如何实现，这个责任由 <strong>字符编码</strong> （Character Encoding）承担，字符集与字符编码可能不同。</p><h2 id="ASCII"><a href="#ASCII" class="headerlink" title="ASCII"></a>ASCII</h2><p>最初的时候，美帝只考虑了自己需要的英文，26 个大小写字母加数字符号控制字符乱七八糟一通搞，正好 128 个字符，一个字节表达完毕，这就是 ASCII（American Standard Code for Information Interchange，美国信息交换标准代码）。后来计算机传到欧洲，字符不够用，为了对剩下的 128 个字符进行了利用并制定标准，ISO 推出 ISO 8859，显然 256 个字符也是不够这些国家用的，所以 8859 被分成了十几个部分，它覆盖了大部分使用拉丁字母的语言文字。</p><h2 id="代码页"><a href="#代码页" class="headerlink" title="代码页"></a>代码页</h2><p>在 ISO 标准完全定型之前，IBM 就有一系列自己的字符编码，叫做代码页（code page），比如 437（扩展 ASCII）、850（西欧语言）、852（东欧语言）。IBM代码页通常被用于控制台（console）环境，也就是 MS-DOS 或 Unix Shell 那样的命令行环境。<br>微软将 IBM 代码页称为 OEM 代码页，自己定义的称为 ANSI 代码页， 比如 1252（西欧语言）、1250（东欧语言）、936（GBK 简体中文）、950（Big5 繁体中文）、932（SJIS 日文）、949（EUC-KR 韩文）等。</p><h2 id="GB-家族"><a href="#GB-家族" class="headerlink" title="GB 家族"></a>GB 家族</h2><p>当计算机来到中国，这个问题的难度升级了，一个字节无论如何也表达不了博大精深的汉字，于是人们拿两个字节解决了这个问题，也就是 GB 2312 字符集，它是一个 94 * 94 的表，包括 7445 个字符。GB 2312 兼容 ASCII，GB 2312 编码对汉字/符号进行了分区（区位码）。每个汉字 / 符号以两个字节来表示。第一个字节称为「高位字节」，第二个字节称为「低位字节」。GB 2312 还有另一种编码方式 HZ，只是不常用。</p><p>GB 2312 的出现，基本满足了汉字的计算机处理需要，它所收录的汉字已经覆盖中国大陆 99.75% 的使用频率。但对于人名、古汉语等方面出现的罕用字和繁体字，GB 2312不能处理，<del>譬如说，某位领导人的名字写不出来，</del>于是人们对 GB 2312 进行了扩展，这就是 GBK，K为汉语拼音 Kuo Zhan（扩展）中「扩」字的声母。GBK 全称是汉字内码扩展规范（Chinese Internal Code Extension Specification）。GBK 有一字节和双字节编码，这里有个事情就是计算机如何知道当前的字节是独立的字符还是跟相邻的字符共同表示一个字符，GBK 是通过第一个字符的范围来辨别的，<code>00</code>–<code>7F</code> 范围内是一个字节，<code>81</code>–<code>FE</code> 范围内是两个字节，GBK 向下完全兼容 GB 2312，GBK 与 CP936 大体相同，比它多 95 个字符。然而 GBK 但是毕竟只是规范，不是标准，随后国家推出 GBK 18030 以取代 GBK，它完全兼容 GB 2312，基本兼容 GBK，采用多字节编码，每个字可以由 1 个、2 个或 4 个字节组成。</p><h2 id="UCS-Unicode"><a href="#UCS-Unicode" class="headerlink" title="UCS / Unicode"></a>UCS / Unicode</h2><h3 id="UCS"><a href="#UCS" class="headerlink" title="UCS"></a>UCS</h3><p>为了使混乱的编码格局得到统一，ISO 于 1990 年推出了通用字符集（Unicode Character Set，UCS），它包含了一百多万个字符，UCS 有两种编码方式：UCS-2 和 UCS-4，分别用两个字节和四个字节表示一个字符，UCS-2 只能表示 65536 个字符，明显不够用，已经过时了。UCS-4 根据最高位为0的最高字节分成2<sup>7</sup>=128 个 group。每个 group 再根据次高字节分为 256 个 plane。每个 plane 根据第 3 个字节分为 256 行（rows），每行包含256个 cells。当然同一行的 cells 只是最后一个字节不同，其余都相同。group 0 的 plane 0 就是 BMP。</p><h3 id="Unicode"><a href="#Unicode" class="headerlink" title="Unicode"></a>Unicode</h3><p>ISO之外还有另外一个组织：统一码联盟（The Unicode Consortium），它于1991年推出了 Unicode 1.0。后来与 ISO 组织合并成果。<br>Unicode 全称 Universal Multiple-Octet Coded Character Set。Unicode 的码空间从 U+0000 到 U+10FFFF， Unicode 的码空间可以划分为 17 个平面（plane），每个平面包含 2<sup>16</sup>(65,536) 个码位。每个平面的码位可表示为从 U+xx0000 到 U+xxFFFF，其中 xx 表示十六进制值从 00H 到 10H，共计 17 个平面。如果 xx 是 0，即第 0 平面，可省略不写，第一个 Unicode 平面（码位从 U+0000 至 U+FFFF）包含了最常用的字符，该平面被称为基本多语言平面（Basic Multilingual Plane），缩写为 BMP。其他平面称为辅助平面（Supplementary Planes）。<br>这里字符集跟字符编码的区别就出现了，Unicode 只是指定了字符的映射，并没有指定实现方式，出现了 UTF-8、UTF-16 和 UTF-32 三种编码方式。</p><h3 id="UTF-16"><a href="#UTF-16" class="headerlink" title="UTF-16"></a>UTF-16</h3><p>UTF-16 可以看作是 UCS-2 的父集（严格的说这不正确，在 UTF-16 中从 U+D800 到 U+DFFF 的码位不对应于任何字符，而在使用 UCS-2 的时代，U+D800 到 U+DFFF 内的值被占用），为什么说它是父集呢，因为当字符不在 BMP 时 UTF-16 会使用四个字节来编码，所以要注意，UTF-16 是变长编码，四个字节的表示算法比较啰嗦，我不写了。<br>UTF-16 的坑在于字节序（Endianness）的问题，就是存储和传输的时候哪个在高地址哪个在低地址，举个例子：「雪」的大端序（big-endian，也叫大尾序）为 U+D1A9，小端序（little-endian，也叫小尾序）为 U+A9D1。一般来说，以 Macintosh 制作或储存的文字使用大端序格式，以 Microsoft 或 Linux 制作或储存存的文字使用小端序格式，网络传输一般采用大端序。<br>为此，出现了三种解决方案，也就是UTF-16LE，UTF-16BE， UTF-16。UTF-16 是大端序还是小端序取决于在文件头是否有 BOM，如果没有就听天由命了，如果有的话，就说说 BOM，BOM（byte-order mark） 是字节顺序标记，它不仅仅存在于 UTF-16 中，只要编码方式受字节序影响，就需要 BOM，在 UTF-8、UTF-32、GB 18030 中也有它的身影，不过在不同的编码方式中表示也不一样。在 UTF-16 中用 U+FEFF 表示大端序，用 U+FFFE 表示小端序。</p><h3 id="UTF-32"><a href="#UTF-32" class="headerlink" title="UTF-32"></a>UTF-32</h3><p>UTF-32 是 UCS-4 的子集，它对所有的字符都采用四字节表示，强迫症表示很开心。当然，它也逃不了与 UTF-16 类似的遭遇，它也分 UTF-32LE、UTF-32BE、UTF-32。</p><h3 id="UTF-8"><a href="#UTF-8" class="headerlink" title="UTF-8"></a>UTF-8</h3><p>重头戏来了，如果一个文件 99% 都是英文，采用上述的 Unicode 编码方案太浪费硬盘了，如果是一个网站，流量也要翻倍啊，UTF-8 采用可变长编码，向下兼容 ASCII 编码，良好的解决了这类问题。</p><center><strong>Unicode 和 UTF-8 之间的转换关系表 ( x 字符表示码点占据的位 )</strong></center><table><thead><tr><th style="text-align:center">码点的位数</th><th style="text-align:center">码点起值</th><th style="text-align:center">码点终值</th><th style="text-align:center">字节序列</th><th style="text-align:center">Byte 1</th><th style="text-align:center">Byte 2</th><th style="text-align:center">Byte 3</th><th style="text-align:center">Byte 4</th><th style="text-align:center">Byte 5</th><th style="text-align:center">Byte 6</th></tr></thead><tbody><tr><td style="text-align:center">7</td><td style="text-align:center">U+0000</td><td style="text-align:center">U+007F</td><td style="text-align:center">1</td><td style="text-align:center">0xxxxxxx</td><td style="text-align:center"></td><td style="text-align:center"></td><td style="text-align:center"></td><td style="text-align:center"></td><td style="text-align:center"></td></tr><tr><td style="text-align:center">11</td><td style="text-align:center">U+0080</td><td style="text-align:center">U+07FF</td><td style="text-align:center">2</td><td style="text-align:center">110xxxxx</td><td style="text-align:center">10xxxxxx</td><td style="text-align:center"></td><td style="text-align:center"></td><td style="text-align:center"></td><td style="text-align:center"></td></tr><tr><td style="text-align:center">16</td><td style="text-align:center">U+0800</td><td style="text-align:center">U+FFFF</td><td style="text-align:center">3</td><td style="text-align:center">1110xxxx</td><td style="text-align:center">10xxxxxx</td><td style="text-align:center">10xxxxxx</td><td style="text-align:center"></td><td style="text-align:center"></td><td style="text-align:center"></td></tr><tr><td style="text-align:center">21</td><td style="text-align:center">U+10000</td><td style="text-align:center">U+1FFFFF</td><td style="text-align:center">4</td><td style="text-align:center">11110xxx</td><td style="text-align:center">10xxxxxx</td><td style="text-align:center">10xxxxxx</td><td style="text-align:center">10xxxxxx</td><td style="text-align:center"></td><td style="text-align:center"></td></tr><tr><td style="text-align:center">26</td><td style="text-align:center">U+200000</td><td style="text-align:center">U+3FFFFFF</td><td style="text-align:center">5</td><td style="text-align:center">111110xx</td><td style="text-align:center">10xxxxxx</td><td style="text-align:center">10xxxxxx</td><td style="text-align:center">10xxxxxx</td><td style="text-align:center">10xxxxxx</td><td style="text-align:center"></td></tr><tr><td style="text-align:center">31</td><td style="text-align:center">U+4000000</td><td style="text-align:center">U+7FFFFFFF</td><td style="text-align:center">6</td><td style="text-align:center">1111110x</td><td style="text-align:center">10xxxxxx</td><td style="text-align:center">10xxxxxx</td><td style="text-align:center">10xxxxxx</td><td style="text-align:center">10xxxxxx</td><td style="text-align:center">10xxxxxx</td></tr></tbody></table><p>Unicode 允许在 UTF-8 中使用 BOM，不过 UTF-8 的编码方式是字节序无关的，没必要使用 BOM。</p><h2 id="记事本"><a href="#记事本" class="headerlink" title="记事本"></a>记事本</h2><p>打开记事本，文件-&gt;另存为，下方编码方式选项有四个</p><ul><li>ANSI</li><li>Unicode</li><li>Unicode big endia</li><li>UTF-8</li></ul><p>ANSI 看着好像 ASCII，但事实上，它的编码方式是系统默认的编码方式，对于一个 ANSI 文本，英文部分使用的就是 ASCII 编码，而中文部分使用的就是 GB 2312 编码，如果是繁体则会使用 BIG 5 编码。而第二个 Unicode 其实是带有 BOM 的小端序 UTF-16，最后那个 UTF-8 也是带 BOM 的。</p><p>注1：这是不严谨的说法，字符编码的层次不是简单的字符集与编码的映射，而是有五层模型。</p><p>参考：</p><ul><li><a href="https://www.wikiwand.com/zh/%E4%BB%A3%E7%A0%81%E9%A1%B5&quot;" target="_blank" rel="external">字符编码</a></li><li><a href="https://www.wikiwand.com/zh/GB_2312&quot;" target="_blank" rel="external">GB 2312</a></li><li><a href="https://www.wikiwand.com/zh/%E6%B1%89%E5%AD%97%E5%86%85%E7%A0%81%E6%89%A9%E5%B1%95%E8%A7%84%E8%8C%83&quot;" target="_blank" rel="external">汉字内码扩展规范</a></li><li><a href="https://www.wikiwand.com/zh/GB_18030&quot;" target="_blank" rel="external">GB 18030</a></li><li><a href="https://www.wikiwand.com/zh/Unicode&quot;" target="_blank" rel="external">Unicode</a></li><li><a href="https://www.wikiwand.com/zh/UTF-8&quot;" target="_blank" rel="external">UTF-8</a></li><li><a href="https://www.wikiwand.com/zh/UTF-16&quot;" target="_blank" rel="external">UTF-16</a></li><li><a href="https://www.wikiwand.com/zh/UTF-32&quot;" target="_blank" rel="external">UTF-32</a></li><li><a href="https://www.wikiwand.com/zh/字节序&quot;" target="_blank" rel="external">字节序</a></li><li><a href="https://www.wikiwand.com/zh/%E4%BD%8D%E5%85%83%E7%B5%84%E9%A0%86%E5%BA%8F%E8%A8%98%E8%99%9F&quot;" target="_blank" rel="external">位元組順序記號</a></li><li><a href="http://unicode.org/faq/utf_bom.html&quot;" target="_blank" rel="external">UTF-BOM</a></li></ul><blockquote>文档信息<br><hr>本作品采用 <a rel="external" href="http://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank">知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议</a> 进行许可。<br>本文链接：www.snovey.com/2016/06/char-encoding.html</blockquote>]]></content>
    
    <summary type="html">
    
      &lt;h2 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h2&gt;&lt;p&gt;计算机只能存储二进制的数据，所以字符也一样只能通过将字符映射为相应的二进制形式才能保存，读取的时候由系统对字符进行图形渲染。&lt;br&gt;
    
    </summary>
    
      <category term="杂谈" scheme="www.snovey.com/categories/%E6%9D%82%E8%B0%88/"/>
    
    
      <category term="字符编码" scheme="www.snovey.com/tags/%E5%AD%97%E7%AC%A6%E7%BC%96%E7%A0%81/"/>
    
  </entry>
  
  <entry>
    <title>回车与换行</title>
    <link href="www.snovey.com/2016/06/endofline.html"/>
    <id>www.snovey.com/2016/06/endofline.html</id>
    <published>2016-06-07T16:00:00.000Z</published>
    <updated>2017-09-02T03:35:39.385Z</updated>
    
    <content type="html"><![CDATA[<p>下图是上古时期的机械打字机，每输入一个字符，滚筒会左移一小截（一个字符的宽度 + 字符间距，好在那时候打字机都是等宽字体）。当滚筒移动到最左侧，即纸张右侧已经和敲针对齐时（也就是打完一行字），会有响铃提示已经抵达行末。这时拉动左侧墨带盒上边的金属手柄（换行手柄），然后滚筒会被推到最右边，打字头对准了行首，这就是 <code>CR</code>（carriage return, 回车）。继续用力，换行手柄大约会被扳动 30 度左右，纸会被上卷一行（行高，line height），这就是 <code>LF</code>（line feed, 换行）。<br><a id="more"></a></p><p><img class="lazyload"  data-src="//blog.snovey.com/image/typewriter.jpg" alt="typewriter"></p><p>如果只换行不回车，那么第一行敲满以后，敲针始终在纸张右侧，无法继续输入；只回车不换行，所有的内容都敲到同一行里了。</p><p>可以看下这两个视频：<a href="https://www.youtube.com/watch?v=nEdeZkBG14w" target="_blank" rel="external">Olympia SM9 Typewriter Demo</a> 和 <a href="https://www.youtube.com/watch?v=FkUXn5bOwzk" target="_blank" rel="external">How to Use a Typewriter</a></p><p>在最早的 ASCII 标准（1963-1968）中，有两套标准，一套是 ISO 出的认为 <code>CRLF</code> 和 <code>LF</code> 都是标准的；然而另一套是 ASA 出的只认为 <code>CRLF</code> 是符合标准的。</p><p>所以 MS-DOS（1981）设计的时候是采用了在两种方法中都符合标准的 <code>CRLF</code>，一方面是满足了两个标准，另一方面是兼容了当时大量采用 <code>CRLF</code> 的计算机。而 Unix 的前身 Multics 的设计者认为在每行的结尾加两个字符用于换行，实在是极大的浪费（那时的存储设备非常昂贵）。所以 Multics 里面用一个驱动程序自动将 <code>LF</code> 转换成 <code>CR-LF</code>，所以他们用了单一的 <code>LF</code>。</p><p>很明显，<code>CRLF</code> 才是正统，*nix 是异端。关于 <code>CR</code> 与 <code>LF</code> 的表示如下：</p><blockquote><p>CR（carriage return, 回车）用 \r （return）表示，对应的 ASCII 码为 0x0D</p></blockquote><p>LF（line feed, 换行）用 \n （newline）表示，对应的 ASCII 码为 0x0A</p><p>所以现在各操作系统对换行的表示方式如下：</p><blockquote><p>LF：在 Unix 或 Unix 相容系统（GNU/Linux，AIX，Xenix，Mac OS X，…）、BeOS、Amiga、RISC OS<br>CR+LF：MS-DOS、微软视窗操作系统（Microsoft Windows）、大部分非 Unix 的系统<br>CR：Apple II 家族，Mac OS 至版本 9</p></blockquote><p>假设有这样一段文本：<br></p><figure class="highlight plain"><table><tr><td class="code"><pre><div class="line">aaa</div><div class="line">bbb</div><div class="line">ccc</div></pre></td></tr></table></figure><p></p><p>如果这段文本是在 Windows 下编辑的，当它移植到 Linux 下，用 vim 打开会显示：<br></p><figure class="highlight plain"><table><tr><td class="code"><pre><div class="line">aaa^M</div><div class="line">bbb^M</div><div class="line">ccc^M</div></pre></td></tr></table></figure><p></p><p>如果你的 vim 正常显示（可能是vim的版本比较高），可以 <code>cat -A</code> 一下，会显示：<br></p><figure class="highlight plain"><table><tr><td class="code"><pre><div class="line">aaa^M$</div><div class="line">aaa^M$</div><div class="line">aaa^M$</div></pre></td></tr></table></figure><p></p><p>这里的 <code>^M</code> 不是 <code>^</code> 符号加 <code>M</code>，而是一个组合字符，代表 <code>CR</code>。</p><p>目前大多数文本编辑器都能够进行不同的换行符之间的转换（Windows 系统的记事本不行……）。如果是在 linux 下可使用如下命令进行转换：<br></p><figure class="highlight bash"><table><tr><td class="code"><pre><div class="line"><span class="variable">$dos2unix</span> file_name</div><div class="line"><span class="variable">$unix2dos</span> file\_name</div></pre></td></tr></table></figure><p></p><p>当然，你的系统可能没有这个命令，那么你可以用 vi 或者 vim 打开，然后在命令模式下输入：<br></p><figure class="highlight bash"><table><tr><td class="code"><pre><div class="line">%s/^M$//g    <span class="comment">#这里的^M是Ctrl+v+m`</span></div></pre></td></tr></table></figure><p></p><p>如果你的 vi 或者 vim 正常显示了，那么上面这个办法也是行不通的，这时可以使用 sed，命令如下：<br></p><figure class="highlight bash"><table><tr><td class="code"><pre><div class="line"><span class="variable">$sed</span> -i <span class="string">'s/^M//g'</span> filename    <span class="comment">#这里的 ^M 仍然是 Ctrl+v+m</span></div><div class="line"><span class="variable">$sed</span> -i <span class="string">'s/\r//g'</span> filename    <span class="comment">#与上一个命令相同</span></div></pre></td></tr></table></figure><p></p><p>或者使用 tr 命令：<br></p><figure class="highlight bash"><table><tr><td class="code"><pre><div class="line"><span class="variable">$tr</span> <span class="\_">-d</span> <span class="string">'\r'</span> &lt; inputfile &gt; outputfile    <span class="comment">#CRLF 转 LF</span></div><div class="line"><span class="variable">$tr</span> <span class="string">'\r'</span> <span class="string">'\n'</span> &lt; inputfile &gt; outputfile    <span class="comment">#CR 转 LF</span></div></pre></td></tr></table></figure><p></p><p>如果是在 Mac OS 9 或者 Linux 系统下编辑的，当它移植到 Windows 下，用记事本打开会显示：<br></p><figure class="highlight plain"><table><tr><td class="code"><pre><div class="line">aaabbbccc</div></pre></td></tr></table></figure><p></p><p>但是不妨写这样一段C语言程序：<br></p><figure class="highlight c++"><table><tr><td class="code"><pre><div class="line"><span class="built_in">printf</span>(<span class="string">"aaa\n"</span>);</div><div class="line"><span class="built_in">printf</span>(<span class="string">"bbb\n"</span>);</div><div class="line"><span class="built_in">printf</span>(<span class="string">"ccc\n"</span>);</div></pre></td></tr></table></figure><p></p><p>在控制台下输出为：<br></p><figure class="highlight plain"><table><tr><td class="code"><pre><div class="line">aaa</div><div class="line">bbb</div><div class="line">ccc</div></pre></td></tr></table></figure><p></p><p>即便是将输出定向到文件用记事本打开，显示依然如上所示。这就有些坑了，再来这样一段程序：<br></p><figure class="highlight c++"><table><tr><td class="code"><pre><div class="line"><span class="keyword">char</span> c;</div><div class="line">c = getchar();</div><div class="line"><span class="built_in">printf</span>(<span class="string">"%d\n"</span>, c);</div></pre></td></tr></table></figure><p></p><p>输入回车，输出 10，说好的 Windows 下按一下 <kbd>Enter</kbd> 键输入两个字符，为什么只剩下一个了呢？再比如这样一段程序：<br></p><figure class="highlight c++"><table><tr><td class="code"><pre><div class="line"><span class="built_in">printf</span>(<span class="string">"aaa\n"</span>);</div><div class="line"><span class="built_in">printf</span>(<span class="string">"bbb\r\n"</span>);</div><div class="line"><span class="built_in">printf</span>(<span class="string">"ccc\r\r\n"</span>);</div></pre></td></tr></table></figure><p></p><p>输出都是一样的换行。编译器做了什么，Windows 系统做了什么，我无从知晓，既然已经理不清其中的原因，那最好的办法就是避开这个陷阱。如果你对此事的兴趣非常强烈，不妨看下这个：<a href="https://www.zhihu.com/question/24639606" target="_blank" rel="external">在 Windows 下键入 Enter 键，是在键盘缓冲区中存入 ‘\n’ 还是 ‘\r’’\n’ 两个？</a></p><p>参考</p><ul><li><a href="https://www.wikiwand.com/en/Newline" target="_blank" rel="external">Newline</a></li></ul><blockquote>文档信息<br><hr>本作品采用 <a rel="external" href="http://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank">知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议</a> 进行许可。<br>本文链接：www.snovey.com/2016/06/endofline.html</blockquote>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;下图是上古时期的机械打字机，每输入一个字符，滚筒会左移一小截（一个字符的宽度 + 字符间距，好在那时候打字机都是等宽字体）。当滚筒移动到最左侧，即纸张右侧已经和敲针对齐时（也就是打完一行字），会有响铃提示已经抵达行末。这时拉动左侧墨带盒上边的金属手柄（换行手柄），然后滚筒会被推到最右边，打字头对准了行首，这就是 &lt;code&gt;CR&lt;/code&gt;（carriage return, 回车）。继续用力，换行手柄大约会被扳动 30 度左右，纸会被上卷一行（行高，line height），这就是 &lt;code&gt;LF&lt;/code&gt;（line feed, 换行）。&lt;br&gt;
    
    </summary>
    
      <category term="杂谈" scheme="www.snovey.com/categories/%E6%9D%82%E8%B0%88/"/>
    
    
      <category term="字符编码" scheme="www.snovey.com/tags/%E5%AD%97%E7%AC%A6%E7%BC%96%E7%A0%81/"/>
    
  </entry>
  
  <entry>
    <title>拓扑排序的算法实现</title>
    <link href="www.snovey.com/2016/06/toposort.html"/>
    <id>www.snovey.com/2016/06/toposort.html</id>
    <published>2016-06-06T10:44:04.000Z</published>
    <updated>2017-08-09T15:15:45.007Z</updated>
    
    <content type="html"><![CDATA[<p>对于一个有向图，可以通过拓扑排序（Topological Sort）来判断是否是有向无环图（Directed Acyclic Graph，DAG）。<br>我总结了一下拓扑排序的思想，下面来说一说。</p><a id="more"></a><p>拓扑排序的算法思想是：</p><ol><li>每次从图中取出入度为 0 的节点</li><li>删除该节点发出的所有边，进行（1）步骤<br>邻接表的实现方式如下：<br><img class="lazyload"  data-src="//blog.snovey.com/image/toposort.png" alt="toposort"><br>初始的时候建立两个表，一个邻接表，以及另外一个表存储出度为0的节点，每次将入度表中入度为 0 的点加入容器中，然后从容器中取出节点，去邻接表中查询，根据邻接表修改入度表，发现入度为 0 的点加入容器中，如此反复，直至容器为空。<br>如果最终入度表中有入度不为 0 的节点，则该图有环，否则该图无环。若该图为偏序关系，则某一时刻容器中的元素多于一个，取出的元素可能是随机的，造成拓扑排序的结果不唯一，与容器的选取有关。若该图为全序关系，则容器中的元素始终只有一个，结果唯一，与容器的选取无关。<br>下面以题为例说明：<br>HDU 3342<br>题意：有一个 ACM 日常 QQ 交流群，群内存在一些师徒关系，若存在某个人的徒弟是自己的师傅的情况则不合法，输出 NO，否则输出 YES。此题有 trick，即要判断重边，代码如下<figure class="highlight c++"><table><tr><td class="code"><pre><div class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;iostream&gt;</span></span></div><div class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;algorithm&gt;</span></span></div><div class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;cstdio&gt;</span></span></div><div class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;cstring&gt;</span></span></div><div class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;vector&gt;</span></span></div><div class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;set&gt;</span></span></div><div class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> <span class="built_in">std</span>;</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></div><div class="line">&#123;</div><div class="line">    <span class="keyword">int</span> n, m, x, y, cnt, table[<span class="number">105</span>];</div><div class="line">    <span class="keyword">bool</span> vis[<span class="number">105</span>][<span class="number">105</span>];  <span class="comment">//防止出现重边</span></div><div class="line">    <span class="keyword">while</span>(<span class="built_in">scanf</span>(<span class="string">"%d%d"</span>, &amp;n, &amp;m) &amp;&amp; n &amp;&amp; m)</div><div class="line">    &#123;</div><div class="line">        cnt = <span class="number">0</span>;</div><div class="line">        <span class="built_in">memset</span>(vis, <span class="number">0</span>, <span class="keyword">sizeof</span>(vis));</div><div class="line">        <span class="built_in">memset</span>(table, <span class="number">0</span>, <span class="keyword">sizeof</span>(table));</div><div class="line">        <span class="built_in">vector</span>&lt;<span class="keyword">int</span>&gt; v[<span class="number">105</span>];  <span class="comment">//邻接表</span></div><div class="line">        <span class="built_in">set</span>&lt;<span class="keyword">int</span>&gt; st;  <span class="comment">//存储入度为0的节点</span></div><div class="line">        <span class="built_in">set</span>&lt;<span class="keyword">int</span>&gt;::iterator it;</div><div class="line">        <span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; m; i++)</div><div class="line">        &#123;</div><div class="line">            <span class="built_in">scanf</span>(<span class="string">"%d%d"</span>, &amp;x, &amp;y);</div><div class="line">            <span class="keyword">if</span>(!vis[x][y])</div><div class="line">            &#123;</div><div class="line">                v[x].push_back(y);</div><div class="line">                table[y]++;</div><div class="line">                vis[x][y] = <span class="literal">true</span>;</div><div class="line">            &#125;</div><div class="line">        &#125;</div><div class="line">        <span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; n; i++)</div><div class="line">            <span class="keyword">if</span>(table[i] == <span class="number">0</span>)</div><div class="line">                cnt++, st.insert(i);</div><div class="line">        <span class="keyword">while</span>(!st.empty())</div><div class="line">        &#123;</div><div class="line">            it = st.begin();</div><div class="line">            <span class="keyword">for</span>(<span class="keyword">unsigned</span> <span class="keyword">int</span> i = <span class="number">0</span>; i &lt; v[*it].size(); i++)</div><div class="line">            &#123;</div><div class="line">                table[v[*it][i]]--;</div><div class="line">                <span class="keyword">if</span>(table[v[*it][i]] == <span class="number">0</span>)</div><div class="line">                    cnt++, st.insert(v[*it][i]);</div><div class="line">            &#125;</div><div class="line">            st.erase(*it);</div><div class="line">        &#125;</div><div class="line">        <span class="built_in">printf</span>(cnt == n ? <span class="string">"YES\n"</span> : <span class="string">"NO\n"</span>);</div><div class="line">    &#125;</div><div class="line">    <span class="keyword">return</span> <span class="number">0</span>;</div><div class="line">&#125;</div></pre></td></tr></table></figure></li></ol><blockquote>文档信息<br><hr>本作品采用 <a rel="external" href="http://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank">知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议</a> 进行许可。<br>本文链接：www.snovey.com/2016/06/toposort.html</blockquote>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;对于一个有向图，可以通过拓扑排序（Topological Sort）来判断是否是有向无环图（Directed Acyclic Graph，DAG）。&lt;br&gt;我总结了一下拓扑排序的思想，下面来说一说。&lt;/p&gt;
    
    </summary>
    
      <category term="算法" scheme="www.snovey.com/categories/%E7%AE%97%E6%B3%95/"/>
    
    
      <category term="算法" scheme="www.snovey.com/tags/%E7%AE%97%E6%B3%95/"/>
    
  </entry>
  
</feed>
