<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
	<channel>
		<title>Posts on Creedowl</title>
		<link>https://blog.creedowl.com/posts/</link>
		<description>Recent content in Posts on Creedowl</description>
		<generator>Hugo -- gohugo.io</generator>
		<language>zh-hans</language>
		<copyright>This work is licensed under a Creative Commons Attribution-NonCommercial 4.0 International License.</copyright>
		<lastBuildDate>Wed, 03 Mar 2021 15:10:56 +0800</lastBuildDate>
		<atom:link href="https://blog.creedowl.com/posts/index.xml" rel="self" type="application/rss+xml" />
		
		<item>
			<title>Ocaml Heap</title>
			<link>https://blog.creedowl.com/posts/ocaml-heap/</link>
			<pubDate>Wed, 03 Mar 2021 15:10:56 +0800</pubDate>
			
			<guid>https://blog.creedowl.com/posts/ocaml-heap/</guid>
			<description>简介 本次课设我实现的数据结构是堆（heap)，堆是一种非常重要的数据结构，分为大顶堆和小顶堆，可以快速地获取其中优先级最高的元素。若是满足以</description>
			<content type="html"><![CDATA[<h1 id="简介">简介</h1>
<p>本次课设我实现的数据结构是<strong>堆（heap)</strong>，堆是一种非常重要的数据结构，分为大顶堆和小顶堆，可以快速地获取其中优先级最高的元素。若是满足以下特性，即可称为堆：“给定堆中任意节点P和C，若P是C的母节点，那么P的值会小于等于（或大于等于）C的值”。若母节点的值恒小于等于子节点的值，此堆称为最小堆（min heap）；反之，若母节点的值恒大于等于子节点的值，此堆称为最大堆（max heap）。所以堆又称作优先队列。</p>
<h1 id="原理">原理</h1>
<h2 id="完全二叉树">完全二叉树</h2>
<p>堆的实现方式通常是完全二叉树，采用数组进行模拟。完全二叉树的特点是较为对称，查找时时间复杂度较为稳定，可以通过数组进行模拟，而不需要实际构建一棵二叉树。</p>
<p>以数组 <code>[1, 2, 3, 4, 5, 6, 7]</code> 为例，可以构建出如下一棵完全二叉树。</p>
<p><img src="https://blog-1251984664.cos.ap-shanghai.myqcloud.com/blogUntitled.png" alt="p1"></p>
<p>对于每个节点都满足值大于父节点，所以这是一个小顶堆。对比数组和二叉树，可以发现：</p>
<ol>
<li>对于节点 <code>i</code> ，它的两个子节点在数组中的索引为 <code>2*i</code> 和 <code>2*i+1</code></li>
<li>两个子节点的大小对于堆的性质并没有影响，这点和二叉搜索树不一样</li>
</ol>
<p>其中的核心操作是 <code>shiftUp</code> 和 <code>shiftDown</code></p>
<p>分析其时间复杂度，有：</p>
<ul>
<li>获取堆顶元素：$O(1)$</li>
<li>插入元素：$O(logn)$</li>
<li>删除元素：$O(logn)$</li>
<li>合并堆：$O(n)$</li>
</ul>
<p>不过采用完全二叉树并不能方便地进行合并操作，需要转换成对一个堆的插入操作</p>
<hr>
<p>但在函数式语言中要如何实现一个堆呢？对于纯函数式语言，是不允许有副作用的。虽然 <code>Ocaml</code> 支持命令式操作，也支持传统的数组，但依然采用数组模拟的话和普通的命令式语言就没有太多区别，而且会产生副作用，这是不推荐的。那么如果用 <code>List</code> 来模拟数组操作呢？</p>
<p><code>Ocaml</code> 中的 <code>List</code> 是通过链表实现的，这就意味着 <code>List</code> 只能进行顺序访问，无法随机访问，也就无法像上面那样构建出一个虚拟的树形结构，而要全部转换成遍历操作。虽然可以通过计算和记录确定节点的位置，但会导致访问的时间复杂性大大提高。还有别的办法吗？</p>
<p>既然用数组模拟完全二叉树的方法在函数式语言中有很大的限制，那我们手动模拟一棵二叉树试试。我们可以定义出以下的数据结构：</p>
<div class="highlight"><pre class="chroma"><code class="language-ocaml" data-lang="ocaml"><span class="k">type</span> <span class="k">&#39;</span><span class="n">a</span> <span class="n">heap</span> <span class="o">=</span> 
	<span class="o">|</span> <span class="nc">Leaf</span>
	<span class="o">|</span> <span class="nc">Node</span> <span class="k">of</span> <span class="k">&#39;</span><span class="n">a</span> <span class="o">*</span> <span class="k">&#39;</span><span class="n">a</span> <span class="n">heap</span> <span class="o">*</span> <span class="k">&#39;</span><span class="n">a</span> <span class="n">heap</span>
</code></pre></div><p>该结构参考了书上的内容，一个节点由自身的值和左右子节点构成，空节点为叶子节点。</p>
<p>对于插入操作，传统的数组模拟方式将元素插入数组尾部，再调用 <code>shiftUp</code> 操作将元素移动到正确的位置，如图所示：</p>
<p><img src="https://blog-1251984664.cos.ap-shanghai.myqcloud.com/blog/1614757139001-Untitled%201.png" alt="p2"></p>
<p>但在树状结构中，我们很难找到最后一个元素的位置，或者说时间复杂度太高，不具备可行性，也就无法采用 <code>shiftUp</code> 的思想。而我们的树结构只保存了根节点的位置，也就意味着只能自顶向下想想办法。</p>
<p><img src="https://blog-1251984664.cos.ap-shanghai.myqcloud.com/blog/1614757187865-Untitled%202.png" alt="p3"></p>
<p>首先比较 1 和 4 ，1 比 4 小，应该继续向下，但应该走左边还是右边呢？堆能保持高效性的原因之一就是结构比较对称，即左右子树的深度大致相等。必须要找到一个规则来解决这个问题。</p>
<p>堆的另一个重要操作是删除，即删除最顶端节点。这时我们发现如果删掉了根节点，我们会得到两个二叉树：</p>
<p><img src="https://blog-1251984664.cos.ap-shanghai.myqcloud.com/blog/1614757208893-Untitled%203.png" alt="p4"></p>
<p>在数组模拟的方式中我们将数组的最后一个元素（也就是完全二叉树的最后一个节点）移动到原本根节点的为止，再采用 <code>shiftDown</code> 操作将它移动到正确的位置。但同上面的问题一样，无法快速定位到最后一个节点，因此问题变成了如何合并两个堆。这时再看插入操作，发现也可以当成合并堆解决，即将目标堆和由待插入元素构成的只有一个节点的堆进行合并。可见堆的核心操作都转换成了堆合并，这就需要找到一个可以高效地进行堆合并的方法。</p>
<h2 id="左偏树">左偏树</h2>
<p>搜索后发现，虽然大部分算法教程和书中堆都是采用数组模拟完全二叉树，但也有其它数据结构可以实现堆，就是左偏树。查看维基百科中的定义：</p>
<blockquote>
<p>左偏树是一种可并堆的实现。左偏树是一棵二叉树，它的节点除了和二叉树的节点一样具有左右子树指针（left, right）外，还有两个属性： 键值和距离（英文文献中称为s-value）。键值用于比较节点的大小。距离的定义如下：
当且仅当节点 i 的左子树或右子树为空时，节点被称作外节点（实际上保存在二叉树中的节点都是内节点，外节点是逻辑上存在而无需保存。把一颗二叉树补上全部的外节点，则称为extended binary tree）。节点i的距离是节点 i 到它的后代中的最近的外节点所经过的边数。特别的，如果节点 i 本身是外节点，则它的距离为0;而空节点的距离规定为 -1。</p>
</blockquote>
<p>光看定义比较复杂，但其实很简单，只需要在上面定义的树中加入一个距离（rank）即可，一个节点距离的值为到最右叶子节点的长度，如图所示：</p>
<p><img src="https://blog-1251984664.cos.ap-shanghai.myqcloud.com/blog/1614757222212-Untitled%204.png" alt="p5"></p>
<p>rank 是用来比较左右子树的长度的，在左偏树中，<strong>所有节点的左子树 rank 值一定大于等于右子树</strong>。因此每次进行合并操作时我们选择右子树，重复进行直到叶子节点或正确的位置，最后更新路径上的 rank 值，如果出现右子树的 rank 值大于左子树，则交换左右子树以保证左偏树的定义成立。</p>
<p>作为左偏树的核心操作，合并（merge）操作的流程如下：</p>
<ol>
<li>合并 <code>t1</code> ，<code>t2</code> 两个树 <code>merge t1 t2</code></li>
<li>比较两个树根节点的值，如果 <code>v1 &lt; v2</code> 则交换两个树（即 <code>merge t2 t1</code> ），这样做的目的是保证左边树的值小于右边，便于操作。实际比较操作是通过函子中的 <code>compare</code> 函数进行，可以根据该函数设置大顶堆或小顶堆</li>
<li>由于左边树（ <code>t1</code> ）根节点的值小于右边树（ <code>t2</code> ），<code>v1</code> 保持不变作为新的根节点</li>
<li>由于 <code>t1</code> 的右子树 <code>r</code> 一定是最短的，开始合并 <code>r</code> 和 <code>t2</code> （即 <code>merge r t2</code> ）</li>
<li>当任一子树是叶子节点时，只需要直接返回另一个子树，并开始向上更新每个节点的 <code>rank</code> 值</li>
<li>更新完毕后还要比较左右子树的 <code>rank</code> 值以确定是否需要交换左右子树</li>
</ol>
<p>看一个合并子树的例子：</p>
<p><img src="https://blog-1251984664.cos.ap-shanghai.myqcloud.com/blog/1614757235831-Untitled%205.png" alt="p6"></p>
<p>分析其时间复杂度，有：</p>
<ul>
<li>获取堆顶元素：$O(1)$</li>
<li>插入元素：$O(logn)$</li>
<li>删除元素：$O(logn)$</li>
<li>合并堆：$O(logn)$</li>
</ul>
<h1 id="代码">代码</h1>
<h2 id="完全二叉树-1">完全二叉树</h2>
<p>这部分代码是我一开始研究学习时写的测试代码，比较混乱，也采用了很多命令式语言的思维，比如有大量的副作用，对对象的操作都是在一个对象本身等。这里保留在这来和下面纯函数式的写法做一个对比</p>
<p><code>binary_heap.ml</code></p>
<div class="highlight"><pre class="chroma"><code class="language-ocaml" data-lang="ocaml"><span class="c">(* Input signature of the functor *)</span>
<span class="k">module</span> <span class="k">type</span> <span class="nc">OrderedType</span> <span class="o">=</span> <span class="k">sig</span>
  <span class="c">(* the type of the heap elements *)</span>
  <span class="k">type</span> <span class="n">t</span>

  <span class="c">(* compare function used to determine the order of elements *)</span>
  <span class="k">val</span> <span class="n">compare</span> <span class="o">:</span> <span class="n">t</span> <span class="o">-&gt;</span> <span class="n">t</span> <span class="o">-&gt;</span> <span class="kt">int</span>
<span class="k">end</span>

<span class="k">module</span> <span class="nc">Make</span> <span class="o">(</span><span class="nc">Ord</span> <span class="o">:</span> <span class="nc">OrderedType</span><span class="o">)</span> <span class="o">=</span> <span class="k">struct</span>
  <span class="k">type</span> <span class="n">elt</span> <span class="o">=</span> <span class="nn">Ord</span><span class="p">.</span><span class="n">t</span>

  <span class="k">type</span> <span class="n">tree</span> <span class="o">=</span> <span class="o">{</span> <span class="k">mutable</span> <span class="n">elements</span> <span class="o">:</span> <span class="n">elt</span> <span class="kt">array</span><span class="o">;</span> <span class="k">mutable</span> <span class="n">size</span> <span class="o">:</span> <span class="kt">int</span> <span class="o">}</span>

  <span class="k">let</span> <span class="n">create</span> <span class="n">n</span> <span class="n">default</span> <span class="o">=</span> <span class="o">{</span> <span class="n">size</span> <span class="o">=</span> <span class="n">0</span><span class="o">;</span> <span class="n">elements</span> <span class="o">=</span> <span class="nn">Array</span><span class="p">.</span><span class="n">make</span> <span class="n">n</span> <span class="n">default</span> <span class="o">}</span>

  <span class="k">let</span> <span class="n">insert</span> <span class="n">v</span> <span class="n">t</span> <span class="o">=</span>
    <span class="k">let</span> <span class="n">d</span> <span class="o">=</span> <span class="n">t</span><span class="o">.</span><span class="n">elements</span> <span class="k">in</span>
    <span class="k">let</span> <span class="k">rec</span> <span class="n">shiftUp</span> <span class="n">i</span> <span class="o">=</span>
      <span class="k">let</span> <span class="n">fi</span> <span class="o">=</span> <span class="o">(</span><span class="n">i</span> <span class="o">-</span> <span class="n">1</span><span class="o">)</span> <span class="o">/</span> <span class="n">2</span> <span class="k">in</span>
      <span class="k">if</span> <span class="n">i</span> <span class="o">&gt;</span> <span class="n">0</span> <span class="o">&amp;&amp;</span> <span class="nn">Ord</span><span class="p">.</span><span class="n">compare</span> <span class="n">d</span><span class="o">.(</span><span class="n">fi</span><span class="o">)</span> <span class="n">v</span> <span class="o">&gt;</span> <span class="n">0</span> <span class="k">then</span> <span class="o">(</span>
        <span class="n">d</span><span class="o">.(</span><span class="n">i</span><span class="o">)</span> <span class="o">&lt;-</span> <span class="n">d</span><span class="o">.(</span><span class="n">fi</span><span class="o">);</span>
        <span class="n">shiftUp</span> <span class="n">fi</span> <span class="o">)</span>
      <span class="k">else</span> <span class="n">d</span><span class="o">.(</span><span class="n">i</span><span class="o">)</span> <span class="o">&lt;-</span> <span class="n">v</span>
    <span class="k">in</span>
    <span class="n">shiftUp</span> <span class="n">t</span><span class="o">.</span><span class="n">size</span><span class="o">;</span>
    <span class="n">t</span><span class="o">.</span><span class="n">size</span> <span class="o">&lt;-</span> <span class="n">t</span><span class="o">.</span><span class="n">size</span> <span class="o">+</span> <span class="n">1</span>

  <span class="k">let</span> <span class="n">deleteTop</span> <span class="n">t</span> <span class="o">=</span>
    <span class="k">if</span> <span class="n">t</span><span class="o">.</span><span class="n">size</span> <span class="o">&lt;=</span> <span class="n">0</span> <span class="k">then</span> <span class="n">failwith</span> <span class="s2">&#34;empty&#34;</span>
    <span class="k">else</span>
      <span class="k">let</span> <span class="n">n</span> <span class="o">=</span> <span class="n">t</span><span class="o">.</span><span class="n">size</span> <span class="o">-</span> <span class="n">1</span> <span class="k">in</span>
      <span class="n">t</span><span class="o">.</span><span class="n">size</span> <span class="o">&lt;-</span> <span class="n">n</span><span class="o">;</span>
      <span class="k">let</span> <span class="n">d</span> <span class="o">=</span> <span class="n">t</span><span class="o">.</span><span class="n">elements</span> <span class="k">in</span>
      <span class="k">let</span> <span class="n">x</span> <span class="o">=</span> <span class="n">d</span><span class="o">.(</span><span class="n">n</span><span class="o">)</span> <span class="k">in</span>
      <span class="k">let</span> <span class="k">rec</span> <span class="n">shiftDown</span> <span class="n">i</span> <span class="o">=</span>
        <span class="k">let</span> <span class="n">ci</span> <span class="o">=</span> <span class="o">(</span><span class="n">2</span> <span class="o">*</span> <span class="n">i</span><span class="o">)</span> <span class="o">+</span> <span class="n">1</span> <span class="k">in</span>
        <span class="k">if</span> <span class="n">ci</span> <span class="o">&lt;</span> <span class="n">n</span> <span class="k">then</span>
          <span class="k">let</span> <span class="n">j</span> <span class="o">=</span>
            <span class="k">let</span> <span class="n">k</span> <span class="o">=</span> <span class="n">ci</span> <span class="o">+</span> <span class="n">1</span> <span class="k">in</span>
            <span class="k">if</span> <span class="n">k</span> <span class="o">&lt;</span> <span class="n">n</span> <span class="o">&amp;&amp;</span> <span class="nn">Ord</span><span class="p">.</span><span class="n">compare</span> <span class="n">d</span><span class="o">.(</span><span class="n">k</span><span class="o">)</span> <span class="n">d</span><span class="o">.(</span><span class="n">ci</span><span class="o">)</span> <span class="o">&lt;</span> <span class="n">0</span> <span class="k">then</span> <span class="n">k</span> <span class="k">else</span> <span class="n">ci</span>
          <span class="k">in</span>
          <span class="k">if</span> <span class="nn">Ord</span><span class="p">.</span><span class="n">compare</span> <span class="n">d</span><span class="o">.(</span><span class="n">j</span><span class="o">)</span> <span class="n">x</span> <span class="o">&lt;</span> <span class="n">0</span> <span class="k">then</span> <span class="o">(</span>
            <span class="n">d</span><span class="o">.(</span><span class="n">i</span><span class="o">)</span> <span class="o">&lt;-</span> <span class="n">d</span><span class="o">.(</span><span class="n">j</span><span class="o">);</span>
            <span class="n">shiftDown</span> <span class="n">j</span> <span class="o">)</span>
          <span class="k">else</span> <span class="n">d</span><span class="o">.(</span><span class="n">i</span><span class="o">)</span> <span class="o">&lt;-</span> <span class="n">x</span>
        <span class="k">else</span> <span class="n">d</span><span class="o">.(</span><span class="n">i</span><span class="o">)</span> <span class="o">&lt;-</span> <span class="n">x</span>
      <span class="k">in</span>
      <span class="n">shiftDown</span> <span class="n">0</span>

  <span class="k">let</span> <span class="n">getTop</span> <span class="n">t</span> <span class="o">=</span> <span class="k">if</span> <span class="n">t</span><span class="o">.</span><span class="n">size</span> <span class="o">&lt;=</span> <span class="n">0</span> <span class="k">then</span> <span class="n">failwith</span> <span class="s2">&#34;empty&#34;</span> <span class="k">else</span> <span class="n">t</span><span class="o">.</span><span class="n">elements</span><span class="o">.(</span><span class="n">0</span><span class="o">)</span>

  <span class="k">let</span> <span class="k">rec</span> <span class="n">toList</span> <span class="n">t</span> <span class="o">=</span>
    <span class="k">if</span> <span class="n">t</span><span class="o">.</span><span class="n">size</span> <span class="o">&lt;=</span> <span class="n">0</span> <span class="k">then</span> <span class="bp">[]</span>
    <span class="k">else</span>
      <span class="k">let</span> <span class="n">top</span> <span class="o">=</span> <span class="n">getTop</span> <span class="n">t</span> <span class="k">in</span>
      <span class="n">deleteTop</span> <span class="n">t</span><span class="o">;</span>
      <span class="n">top</span> <span class="o">::</span> <span class="n">toList</span> <span class="n">t</span>

  <span class="k">let</span> <span class="k">rec</span> <span class="n">fromList</span> <span class="n">n</span> <span class="n">default</span> <span class="o">=</span> <span class="k">function</span>
    <span class="o">|</span> <span class="bp">[]</span> <span class="o">-&gt;</span> <span class="n">create</span> <span class="n">n</span> <span class="n">default</span>
    <span class="o">|</span> <span class="n">hd</span> <span class="o">::</span> <span class="n">tl</span> <span class="o">-&gt;</span>
        <span class="k">let</span> <span class="n">t</span> <span class="o">=</span> <span class="n">fromList</span> <span class="n">n</span> <span class="n">default</span> <span class="n">tl</span> <span class="k">in</span>
        <span class="n">insert</span> <span class="n">hd</span> <span class="n">t</span><span class="o">;</span>
        <span class="n">t</span>
<span class="k">end</span>
</code></pre></div><p><code>binary_heap.mli</code></p>
<div class="highlight"><pre class="chroma"><code class="language-ocaml" data-lang="ocaml"><span class="c">(* Input signature of the functor *)</span>
<span class="k">module</span> <span class="k">type</span> <span class="nc">OrderedType</span> <span class="o">=</span> <span class="k">sig</span>
  <span class="c">(* the type of the heap elements *)</span>
  <span class="k">type</span> <span class="n">t</span>

  <span class="c">(* compare function used to determine the order of elements *)</span>
  <span class="k">val</span> <span class="n">compare</span> <span class="o">:</span> <span class="n">t</span> <span class="o">-&gt;</span> <span class="n">t</span> <span class="o">-&gt;</span> <span class="kt">int</span>
<span class="k">end</span>

<span class="k">module</span> <span class="nc">Make</span> <span class="o">:</span> <span class="k">functor</span> <span class="o">(</span><span class="nc">Ord</span> <span class="o">:</span> <span class="nc">OrderedType</span><span class="o">)</span> <span class="o">-&gt;</span> <span class="k">sig</span>
  <span class="k">type</span> <span class="n">elt</span> <span class="o">=</span> <span class="nn">Ord</span><span class="p">.</span><span class="n">t</span>

  <span class="k">type</span> <span class="n">tree</span>

  <span class="c">(* create an empty heap, with only a leaf *)</span>
  <span class="k">val</span> <span class="n">create</span> <span class="o">:</span> <span class="kt">int</span> <span class="o">-&gt;</span> <span class="n">elt</span> <span class="o">-&gt;</span> <span class="n">tree</span>

  <span class="c">(* insert an element into a heap, just like merge two heap *)</span>
  <span class="k">val</span> <span class="n">insert</span> <span class="o">:</span> <span class="n">elt</span> <span class="o">-&gt;</span> <span class="n">tree</span> <span class="o">-&gt;</span> <span class="kt">unit</span>

  <span class="c">(* delete the top element of a heap, just merge two children trees *)</span>
  <span class="k">val</span> <span class="n">deleteTop</span> <span class="o">:</span> <span class="n">tree</span> <span class="o">-&gt;</span> <span class="kt">unit</span>

  <span class="c">(* get the top element of a heap *)</span>
  <span class="k">val</span> <span class="n">getTop</span> <span class="o">:</span> <span class="n">tree</span> <span class="o">-&gt;</span> <span class="n">elt</span>

  <span class="c">(* generate a list from a heap *)</span>
  <span class="k">val</span> <span class="n">toList</span> <span class="o">:</span> <span class="n">tree</span> <span class="o">-&gt;</span> <span class="n">elt</span> <span class="kt">list</span>

  <span class="c">(* create a heap from list *)</span>
  <span class="k">val</span> <span class="n">fromList</span> <span class="o">:</span> <span class="kt">int</span> <span class="o">-&gt;</span> <span class="n">elt</span> <span class="o">-&gt;</span> <span class="n">elt</span> <span class="kt">list</span> <span class="o">-&gt;</span> <span class="n">tree</span>
<span class="k">end</span>
</code></pre></div><h2 id="左偏堆">左偏堆</h2>
<p>这里的左偏堆采用了纯函数式的写法，包括采用联合体构建一棵二叉树，所有操作都无副作用，返回一个新的对象等。和上面命令式的写法相比，也更加简练，通过递归和迭代就实现了复杂的操作逻辑</p>
<p><code>heap.ml</code></p>
<div class="highlight"><pre class="chroma"><code class="language-ocaml" data-lang="ocaml"><span class="c">(* Input signature of the functor *)</span>
<span class="k">module</span> <span class="k">type</span> <span class="nc">OrderedType</span> <span class="o">=</span> <span class="k">sig</span>
  <span class="c">(* the type of the heap elements *)</span>
  <span class="k">type</span> <span class="n">t</span>

  <span class="c">(* compare function used to determine the order of elements *)</span>
  <span class="k">val</span> <span class="n">compare</span> <span class="o">:</span> <span class="n">t</span> <span class="o">-&gt;</span> <span class="n">t</span> <span class="o">-&gt;</span> <span class="kt">int</span>
<span class="k">end</span>

<span class="k">module</span> <span class="nc">Make</span> <span class="o">(</span><span class="nc">Ord</span> <span class="o">:</span> <span class="nc">OrderedType</span><span class="o">)</span> <span class="o">=</span> <span class="k">struct</span>
  <span class="k">type</span> <span class="n">elt</span> <span class="o">=</span> <span class="nn">Ord</span><span class="p">.</span><span class="n">t</span>

  <span class="c">(* heaps are represented by leftist tree *)</span>
  <span class="k">type</span> <span class="n">tree</span> <span class="o">=</span>
    <span class="c">(* leaf node *)</span>
    <span class="o">|</span> <span class="nc">Leaf</span>
    <span class="c">(* left child, value of node, right child, rank of node *)</span>
    <span class="o">|</span> <span class="nc">Node</span> <span class="k">of</span> <span class="n">tree</span> <span class="o">*</span> <span class="n">elt</span> <span class="o">*</span> <span class="n">tree</span> <span class="o">*</span> <span class="kt">int</span>

  <span class="c">(* create an empty heap, with only a leaf *)</span>
  <span class="k">let</span> <span class="n">create</span> <span class="bp">()</span> <span class="o">=</span> <span class="nc">Leaf</span>

  <span class="c">(* create a node contains only v *)</span>
  <span class="k">let</span> <span class="n">singleton</span> <span class="n">v</span> <span class="o">=</span> <span class="nc">Node</span> <span class="o">(</span><span class="nc">Leaf</span><span class="o">,</span> <span class="n">v</span><span class="o">,</span> <span class="nc">Leaf</span><span class="o">,</span> <span class="n">1</span><span class="o">)</span>

  <span class="c">(* get the rank of a node *)</span>
  <span class="k">let</span> <span class="n">getRank</span> <span class="o">=</span> <span class="k">function</span> <span class="nc">Leaf</span> <span class="o">-&gt;</span> <span class="n">0</span> <span class="o">|</span> <span class="nc">Node</span> <span class="o">(_,</span> <span class="o">_,</span> <span class="o">_,</span> <span class="n">r</span><span class="o">)</span> <span class="o">-&gt;</span> <span class="n">r</span>

  <span class="c">(* merge to heap, the most important function in leftist heap *)</span>
  <span class="k">let</span> <span class="k">rec</span> <span class="n">merge</span> <span class="n">t1</span> <span class="n">t2</span> <span class="o">=</span>
    <span class="k">match</span> <span class="o">(</span><span class="n">t1</span><span class="o">,</span> <span class="n">t2</span><span class="o">)</span> <span class="k">with</span>
    <span class="o">|</span> <span class="nc">Leaf</span><span class="o">,</span> <span class="n">t</span> <span class="o">|</span> <span class="n">t</span><span class="o">,</span> <span class="nc">Leaf</span> <span class="o">-&gt;</span> <span class="n">t</span>
    <span class="o">|</span> <span class="nc">Node</span> <span class="o">(</span><span class="n">l</span><span class="o">,</span> <span class="n">v1</span><span class="o">,</span> <span class="n">r</span><span class="o">,</span> <span class="o">_),</span> <span class="nc">Node</span> <span class="o">(_,</span> <span class="n">v2</span><span class="o">,</span> <span class="o">_,</span> <span class="o">_)</span> <span class="o">-&gt;</span>
        <span class="c">(* switch two heap if the tree on the left have a bigger key *)</span>
        <span class="k">if</span> <span class="nn">Ord</span><span class="p">.</span><span class="n">compare</span> <span class="n">v1</span> <span class="n">v2</span> <span class="o">&gt;</span> <span class="n">0</span> <span class="k">then</span> <span class="n">merge</span> <span class="n">t2</span> <span class="n">t1</span>
          <span class="c">(* the order is determined by the compare function *)</span>
        <span class="k">else</span>
          <span class="c">(* merge with the right tree *)</span>
          <span class="k">let</span> <span class="n">merged</span> <span class="o">=</span> <span class="n">merge</span> <span class="n">r</span> <span class="n">t2</span> <span class="k">in</span>
          <span class="k">let</span> <span class="n">leftRank</span> <span class="o">=</span> <span class="n">getRank</span> <span class="n">l</span> <span class="ow">and</span> <span class="n">rightRank</span> <span class="o">=</span> <span class="n">getRank</span> <span class="n">merged</span> <span class="k">in</span>
          <span class="c">(* compare the rank of both tree *)</span>
          <span class="k">if</span> <span class="n">leftRank</span> <span class="o">&gt;=</span> <span class="n">rightRank</span> <span class="k">then</span> <span class="nc">Node</span> <span class="o">(</span><span class="n">l</span><span class="o">,</span> <span class="n">v1</span><span class="o">,</span> <span class="n">merged</span><span class="o">,</span> <span class="n">rightRank</span> <span class="o">+</span> <span class="n">1</span><span class="o">)</span>
            <span class="c">(* switch left and right since left tree is shorter *)</span>
          <span class="k">else</span> <span class="nc">Node</span> <span class="o">(</span><span class="n">merged</span><span class="o">,</span> <span class="n">v1</span><span class="o">,</span> <span class="n">l</span><span class="o">,</span> <span class="n">leftRank</span> <span class="o">+</span> <span class="n">1</span><span class="o">)</span>

  <span class="c">(* insert an element into a heap, just like merge two heap *)</span>
  <span class="k">let</span> <span class="n">insert</span> <span class="n">v</span> <span class="n">t</span> <span class="o">=</span>
    <span class="k">match</span> <span class="n">t</span> <span class="k">with</span>
    <span class="o">|</span> <span class="nc">Leaf</span> <span class="o">-&gt;</span> <span class="n">singleton</span> <span class="n">v</span>
    <span class="o">|</span> <span class="nc">Node</span> <span class="o">(_,</span> <span class="o">_,</span> <span class="o">_,</span> <span class="o">_)</span> <span class="o">-&gt;</span> <span class="n">merge</span> <span class="n">t</span> <span class="o">(</span><span class="n">singleton</span> <span class="n">v</span><span class="o">)</span>

  <span class="c">(* get the top element of a heap *)</span>
  <span class="k">let</span> <span class="n">getTop</span> <span class="o">=</span> <span class="k">function</span> <span class="nc">Leaf</span> <span class="o">-&gt;</span> <span class="n">failwith</span> <span class="s2">&#34;empty&#34;</span> <span class="o">|</span> <span class="nc">Node</span> <span class="o">(_,</span> <span class="n">v</span><span class="o">,</span> <span class="o">_,</span> <span class="o">_)</span> <span class="o">-&gt;</span> <span class="n">v</span>

  <span class="c">(* delete the top element of a heap, just merge two children trees *)</span>
  <span class="k">let</span> <span class="n">deleteTop</span> <span class="o">=</span> <span class="k">function</span>
    <span class="o">|</span> <span class="nc">Leaf</span> <span class="o">-&gt;</span> <span class="n">failwith</span> <span class="s2">&#34;empty&#34;</span>
    <span class="o">|</span> <span class="nc">Node</span> <span class="o">(</span><span class="n">l</span><span class="o">,</span> <span class="o">_,</span> <span class="n">r</span><span class="o">,</span> <span class="o">_)</span> <span class="o">-&gt;</span> <span class="n">merge</span> <span class="n">l</span> <span class="n">r</span>

  <span class="c">(* generate a list from a heap *)</span>
  <span class="k">let</span> <span class="k">rec</span> <span class="n">toList</span> <span class="n">t</span> <span class="o">=</span>
    <span class="k">match</span> <span class="n">t</span> <span class="k">with</span> <span class="nc">Leaf</span> <span class="o">-&gt;</span> <span class="bp">[]</span> <span class="o">|</span> <span class="o">_</span> <span class="o">-&gt;</span> <span class="n">getTop</span> <span class="n">t</span> <span class="o">::</span> <span class="n">toList</span> <span class="o">(</span><span class="n">deleteTop</span> <span class="n">t</span><span class="o">)</span>

  <span class="k">let</span> <span class="k">rec</span> <span class="n">fromList</span> <span class="o">=</span> <span class="k">function</span>
    <span class="o">|</span> <span class="bp">[]</span> <span class="o">-&gt;</span> <span class="n">create</span> <span class="bp">()</span>
    <span class="o">|</span> <span class="n">hd</span> <span class="o">::</span> <span class="n">tl</span> <span class="o">-&gt;</span> <span class="n">insert</span> <span class="n">hd</span> <span class="o">(</span><span class="n">fromList</span> <span class="n">tl</span><span class="o">)</span>
<span class="k">end</span>
</code></pre></div><p><code>heap.mli</code></p>
<div class="highlight"><pre class="chroma"><code class="language-ocaml" data-lang="ocaml"><span class="c">(* Input signature of the functor *)</span>
<span class="k">module</span> <span class="k">type</span> <span class="nc">OrderedType</span> <span class="o">=</span> <span class="k">sig</span>
  <span class="c">(* the type of the heap elements *)</span>
  <span class="k">type</span> <span class="n">t</span>

  <span class="c">(* compare function used to determine the order of elements *)</span>
  <span class="k">val</span> <span class="n">compare</span> <span class="o">:</span> <span class="n">t</span> <span class="o">-&gt;</span> <span class="n">t</span> <span class="o">-&gt;</span> <span class="kt">int</span>
<span class="k">end</span>

<span class="k">module</span> <span class="nc">Make</span> <span class="o">:</span> <span class="k">functor</span> <span class="o">(</span><span class="nc">Ord</span> <span class="o">:</span> <span class="nc">OrderedType</span><span class="o">)</span> <span class="o">-&gt;</span> <span class="k">sig</span>
  <span class="k">type</span> <span class="n">elt</span> <span class="o">=</span> <span class="nn">Ord</span><span class="p">.</span><span class="n">t</span>

  <span class="k">type</span> <span class="n">tree</span>

  <span class="c">(* create an empty heap, with only a leaf *)</span>
  <span class="k">val</span> <span class="n">create</span> <span class="o">:</span> <span class="kt">unit</span> <span class="o">-&gt;</span> <span class="n">tree</span>

  <span class="c">(* merge to heap, the most important function in leftist heap *)</span>
  <span class="k">val</span> <span class="n">merge</span> <span class="o">:</span> <span class="n">tree</span> <span class="o">-&gt;</span> <span class="n">tree</span> <span class="o">-&gt;</span> <span class="n">tree</span>

  <span class="c">(* insert an element into a heap, just like merge two heap *)</span>
  <span class="k">val</span> <span class="n">insert</span> <span class="o">:</span> <span class="n">elt</span> <span class="o">-&gt;</span> <span class="n">tree</span> <span class="o">-&gt;</span> <span class="n">tree</span>

  <span class="c">(* get the top element of a heap *)</span>
  <span class="k">val</span> <span class="n">getTop</span> <span class="o">:</span> <span class="n">tree</span> <span class="o">-&gt;</span> <span class="n">elt</span>

  <span class="c">(* delete the top element of a heap, just merge two children trees *)</span>
  <span class="k">val</span> <span class="n">deleteTop</span> <span class="o">:</span> <span class="n">tree</span> <span class="o">-&gt;</span> <span class="n">tree</span>

  <span class="c">(* generate a list from a heap *)</span>
  <span class="k">val</span> <span class="n">toList</span> <span class="o">:</span> <span class="n">tree</span> <span class="o">-&gt;</span> <span class="n">elt</span> <span class="kt">list</span>

  <span class="c">(* create a heap from list *)</span>
  <span class="k">val</span> <span class="n">fromList</span> <span class="o">:</span> <span class="n">elt</span> <span class="kt">list</span> <span class="o">-&gt;</span> <span class="n">tree</span>
<span class="k">end</span>
</code></pre></div><p><code>test.ml</code></p>
<p>测试文件，对所写的堆向外提供的函数进行测试。创建了一个带 <code>int</code> 类型的函子，采用自定义的排序函数实现大顶堆。还创建了一个 <code>String</code> 类型的函子</p>
<div class="highlight"><pre class="chroma"><code class="language-ocaml" data-lang="ocaml"><span class="c">(* int 型函子，带比较函数 *)</span>
<span class="k">module</span> <span class="nc">E</span> <span class="o">=</span> <span class="k">struct</span>
  <span class="k">type</span> <span class="n">t</span> <span class="o">=</span> <span class="kt">int</span>

  <span class="k">let</span> <span class="n">compare</span> <span class="n">a</span> <span class="n">b</span> <span class="o">=</span> <span class="o">-</span><span class="n">compare</span> <span class="n">a</span> <span class="n">b</span>
<span class="k">end</span>

<span class="c">(* int 型大顶堆，采用左偏树 *)</span>
<span class="k">module</span> <span class="nc">H_int</span> <span class="o">=</span> <span class="nn">Heap</span><span class="p">.</span><span class="nc">Make</span> <span class="o">(</span><span class="nc">E</span><span class="o">)</span>

<span class="k">let</span> <span class="n">t</span> <span class="o">=</span> <span class="nn">H_int</span><span class="p">.</span><span class="n">create</span> <span class="bp">()</span>

<span class="c">(* 测试基本的插入、删除等操作 *)</span>
<span class="k">let</span> <span class="bp">()</span> <span class="o">=</span>
  <span class="k">let</span> <span class="n">t</span> <span class="o">=</span>
    <span class="nn">H_int</span><span class="p">.</span><span class="n">insert</span> <span class="n">3</span> <span class="n">t</span> <span class="o">|&gt;</span> <span class="nn">H_int</span><span class="p">.</span><span class="n">insert</span> <span class="n">5</span> <span class="o">|&gt;</span> <span class="nn">H_int</span><span class="p">.</span><span class="n">insert</span> <span class="n">1</span> <span class="o">|&gt;</span> <span class="nn">H_int</span><span class="p">.</span><span class="n">insert</span> <span class="n">4</span>
    <span class="o">|&gt;</span> <span class="nn">H_int</span><span class="p">.</span><span class="n">insert</span> <span class="n">2</span>
  <span class="k">in</span>
  <span class="k">assert</span> <span class="o">(</span><span class="nn">H_int</span><span class="p">.</span><span class="n">getTop</span> <span class="n">t</span> <span class="o">=</span> <span class="n">5</span><span class="o">);</span>
  <span class="k">assert</span> <span class="o">(</span><span class="nn">H_int</span><span class="p">.</span><span class="n">deleteTop</span> <span class="n">t</span> <span class="o">|&gt;</span> <span class="nn">H_int</span><span class="p">.</span><span class="n">deleteTop</span> <span class="o">|&gt;</span> <span class="nn">H_int</span><span class="p">.</span><span class="n">getTop</span> <span class="o">=</span> <span class="n">3</span><span class="o">);</span>
  <span class="nn">List</span><span class="p">.</span><span class="n">iter</span> <span class="o">(</span><span class="nn">Printf</span><span class="p">.</span><span class="n">printf</span> <span class="s2">&#34;%d &#34;</span><span class="o">)</span> <span class="o">(</span><span class="nn">H_int</span><span class="p">.</span><span class="n">toList</span> <span class="n">t</span><span class="o">);</span>
  <span class="n">print_newline</span> <span class="bp">()</span>

<span class="c">(* 测试从 list 构建和生成 list *)</span>
<span class="k">let</span> <span class="bp">()</span> <span class="o">=</span>
  <span class="k">let</span> <span class="n">l</span> <span class="o">=</span> <span class="nn">H_int</span><span class="p">.</span><span class="n">toList</span> <span class="o">(</span><span class="nn">H_int</span><span class="p">.</span><span class="n">fromList</span> <span class="o">[</span> <span class="n">4</span><span class="o">;</span> <span class="n">2</span><span class="o">;</span> <span class="n">1</span><span class="o">;</span> <span class="n">5</span><span class="o">;</span> <span class="n">6</span><span class="o">;</span> <span class="n">7</span><span class="o">;</span> <span class="n">2</span><span class="o">;</span> <span class="n">0</span> <span class="o">])</span> <span class="k">in</span>
  <span class="k">assert</span> <span class="o">(</span><span class="n">l</span> <span class="o">=</span> <span class="o">[</span> <span class="n">7</span><span class="o">;</span> <span class="n">6</span><span class="o">;</span> <span class="n">5</span><span class="o">;</span> <span class="n">4</span><span class="o">;</span> <span class="n">2</span><span class="o">;</span> <span class="n">2</span><span class="o">;</span> <span class="n">1</span><span class="o">;</span> <span class="n">0</span> <span class="o">]);</span>
  <span class="nn">List</span><span class="p">.</span><span class="n">iter</span> <span class="o">(</span><span class="nn">Printf</span><span class="p">.</span><span class="n">printf</span> <span class="s2">&#34;%d &#34;</span><span class="o">)</span> <span class="n">l</span><span class="o">;</span>
  <span class="n">print_newline</span> <span class="bp">()</span>

<span class="c">(* 测试合并两个堆 *)</span>
<span class="k">let</span> <span class="bp">()</span> <span class="o">=</span>
  <span class="k">let</span> <span class="n">t</span> <span class="o">=</span>
    <span class="nn">H_int</span><span class="p">.</span><span class="n">merge</span>
      <span class="o">(</span><span class="nn">H_int</span><span class="p">.</span><span class="n">fromList</span> <span class="o">[</span> <span class="n">1</span><span class="o">;</span> <span class="n">3</span><span class="o">;</span> <span class="n">5</span><span class="o">;</span> <span class="n">7</span><span class="o">;</span> <span class="n">9</span> <span class="o">])</span>
      <span class="o">(</span><span class="nn">H_int</span><span class="p">.</span><span class="n">fromList</span> <span class="o">[</span> <span class="n">2</span><span class="o">;</span> <span class="n">4</span><span class="o">;</span> <span class="n">6</span><span class="o">;</span> <span class="n">8</span> <span class="o">])</span>
  <span class="k">in</span>
  <span class="k">let</span> <span class="n">l</span> <span class="o">=</span> <span class="nn">H_int</span><span class="p">.</span><span class="n">toList</span> <span class="n">t</span> <span class="k">in</span>
  <span class="k">assert</span> <span class="o">(</span><span class="n">l</span> <span class="o">=</span> <span class="o">[</span> <span class="n">9</span><span class="o">;</span> <span class="n">8</span><span class="o">;</span> <span class="n">7</span><span class="o">;</span> <span class="n">6</span><span class="o">;</span> <span class="n">5</span><span class="o">;</span> <span class="n">4</span><span class="o">;</span> <span class="n">3</span><span class="o">;</span> <span class="n">2</span><span class="o">;</span> <span class="n">1</span> <span class="o">]);</span>
  <span class="nn">List</span><span class="p">.</span><span class="n">iter</span> <span class="o">(</span><span class="nn">Printf</span><span class="p">.</span><span class="n">printf</span> <span class="s2">&#34;%d &#34;</span><span class="o">)</span> <span class="n">l</span><span class="o">;</span>
  <span class="n">print_newline</span> <span class="bp">()</span>

<span class="c">(* string 型小顶堆 *)</span>
<span class="k">module</span> <span class="nc">H_string</span> <span class="o">=</span> <span class="nn">Heap</span><span class="p">.</span><span class="nc">Make</span> <span class="o">(</span><span class="nc">String</span><span class="o">)</span>

<span class="k">let</span> <span class="n">t</span> <span class="o">=</span> <span class="nn">H_string</span><span class="p">.</span><span class="n">create</span> <span class="bp">()</span>

<span class="c">(* 测试基本的插入、删除等操作 *)</span>
<span class="k">let</span> <span class="bp">()</span> <span class="o">=</span>
  <span class="k">let</span> <span class="n">t</span> <span class="o">=</span>
    <span class="nn">H_string</span><span class="p">.</span><span class="n">insert</span> <span class="s2">&#34;asdf&#34;</span> <span class="n">t</span> <span class="o">|&gt;</span> <span class="nn">H_string</span><span class="p">.</span><span class="n">insert</span> <span class="s2">&#34;qwer&#34;</span> <span class="o">|&gt;</span> <span class="nn">H_string</span><span class="p">.</span><span class="n">insert</span> <span class="s2">&#34;zz&#34;</span>
  <span class="k">in</span>
  <span class="k">assert</span> <span class="o">(</span><span class="nn">H_string</span><span class="p">.</span><span class="n">getTop</span> <span class="n">t</span> <span class="o">=</span> <span class="s2">&#34;asdf&#34;</span><span class="o">);</span>
  <span class="k">assert</span> <span class="o">(</span>
    <span class="nn">H_string</span><span class="p">.</span><span class="n">toList</span>
      <span class="o">(</span><span class="nn">H_string</span><span class="p">.</span><span class="n">deleteTop</span> <span class="n">t</span> <span class="o">|&gt;</span> <span class="nn">H_string</span><span class="p">.</span><span class="n">deleteTop</span> <span class="o">|&gt;</span> <span class="nn">H_string</span><span class="p">.</span><span class="n">insert</span> <span class="s2">&#34;bb&#34;</span><span class="o">)</span>
    <span class="o">=</span> <span class="o">[</span> <span class="s2">&#34;bb&#34;</span><span class="o">;</span> <span class="s2">&#34;zz&#34;</span> <span class="o">]</span> <span class="o">)</span>

<span class="c">(* 测试 string 类型上的操作 *)</span>
<span class="k">let</span> <span class="bp">()</span> <span class="o">=</span>
  <span class="k">let</span> <span class="n">l</span> <span class="o">=</span>
    <span class="nn">H_string</span><span class="p">.</span><span class="n">toList</span>
      <span class="o">(</span><span class="nn">H_string</span><span class="p">.</span><span class="n">merge</span>
         <span class="o">(</span><span class="nn">H_string</span><span class="p">.</span><span class="n">fromList</span> <span class="o">[</span> <span class="s2">&#34;Z&#34;</span><span class="o">;</span> <span class="s2">&#34;Y&#34;</span><span class="o">;</span> <span class="s2">&#34;X&#34;</span> <span class="o">])</span>
         <span class="o">(</span><span class="nn">H_string</span><span class="p">.</span><span class="n">fromList</span> <span class="o">[</span> <span class="s2">&#34;y&#34;</span><span class="o">;</span> <span class="s2">&#34;z&#34;</span><span class="o">;</span> <span class="s2">&#34;x&#34;</span> <span class="o">]))</span>
  <span class="k">in</span>
  <span class="k">assert</span> <span class="o">(</span><span class="n">l</span> <span class="o">=</span> <span class="o">[</span> <span class="s2">&#34;X&#34;</span><span class="o">;</span> <span class="s2">&#34;Y&#34;</span><span class="o">;</span> <span class="s2">&#34;Z&#34;</span><span class="o">;</span> <span class="s2">&#34;x&#34;</span><span class="o">;</span> <span class="s2">&#34;y&#34;</span><span class="o">;</span> <span class="s2">&#34;z&#34;</span> <span class="o">]);</span>
  <span class="nn">List</span><span class="p">.</span><span class="n">iter</span> <span class="o">(</span><span class="nn">Printf</span><span class="p">.</span><span class="n">printf</span> <span class="s2">&#34;%s &#34;</span><span class="o">)</span> <span class="n">l</span><span class="o">;</span>
  <span class="n">print_newline</span> <span class="bp">()</span>

<span class="c">(* int 型大顶堆，采用数组模拟完全二叉树 *)</span>
<span class="k">module</span> <span class="nc">B_H_int</span> <span class="o">=</span> <span class="nn">Binary_heap</span><span class="p">.</span><span class="nc">Make</span> <span class="o">(</span><span class="nc">E</span><span class="o">)</span>

<span class="k">let</span> <span class="n">t</span> <span class="o">=</span> <span class="nn">B_H_int</span><span class="p">.</span><span class="n">create</span> <span class="n">20</span> <span class="n">0</span>

<span class="c">(* 测试基本的插入、删除等操作 *)</span>
<span class="k">let</span> <span class="bp">()</span> <span class="o">=</span>
  <span class="nn">B_H_int</span><span class="p">.</span><span class="n">insert</span> <span class="n">1</span> <span class="n">t</span><span class="o">;</span>
  <span class="nn">B_H_int</span><span class="p">.</span><span class="n">insert</span> <span class="n">3</span> <span class="n">t</span><span class="o">;</span>
  <span class="nn">B_H_int</span><span class="p">.</span><span class="n">insert</span> <span class="n">2</span> <span class="n">t</span><span class="o">;</span>
  <span class="nn">B_H_int</span><span class="p">.</span><span class="n">insert</span> <span class="n">4</span> <span class="n">t</span><span class="o">;</span>
  <span class="k">assert</span> <span class="o">(</span><span class="nn">B_H_int</span><span class="p">.</span><span class="n">getTop</span> <span class="n">t</span> <span class="o">=</span> <span class="n">4</span><span class="o">);</span>
  <span class="nn">B_H_int</span><span class="p">.</span><span class="n">deleteTop</span> <span class="n">t</span><span class="o">;</span>
  <span class="k">assert</span> <span class="o">(</span><span class="nn">B_H_int</span><span class="p">.</span><span class="n">getTop</span> <span class="n">t</span> <span class="o">=</span> <span class="n">3</span><span class="o">);</span>
  <span class="nn">B_H_int</span><span class="p">.</span><span class="n">deleteTop</span> <span class="n">t</span><span class="o">;</span>
  <span class="k">assert</span> <span class="o">(</span><span class="nn">B_H_int</span><span class="p">.</span><span class="n">getTop</span> <span class="n">t</span> <span class="o">=</span> <span class="n">2</span><span class="o">);</span>
  <span class="nn">B_H_int</span><span class="p">.</span><span class="n">insert</span> <span class="n">6</span> <span class="n">t</span><span class="o">;</span>
  <span class="k">assert</span> <span class="o">(</span><span class="nn">B_H_int</span><span class="p">.</span><span class="n">getTop</span> <span class="n">t</span> <span class="o">=</span> <span class="n">6</span><span class="o">);</span>
  <span class="nn">B_H_int</span><span class="p">.</span><span class="n">deleteTop</span> <span class="n">t</span><span class="o">;</span>
  <span class="nn">B_H_int</span><span class="p">.</span><span class="n">deleteTop</span> <span class="n">t</span><span class="o">;</span>
  <span class="k">assert</span> <span class="o">(</span><span class="nn">B_H_int</span><span class="p">.</span><span class="n">getTop</span> <span class="n">t</span> <span class="o">=</span> <span class="n">1</span><span class="o">)</span>

<span class="k">let</span> <span class="n">t</span> <span class="o">=</span> <span class="nn">B_H_int</span><span class="p">.</span><span class="n">create</span> <span class="n">20</span> <span class="n">0</span>

<span class="c">(* 测试生成 list *)</span>
<span class="k">let</span> <span class="bp">()</span> <span class="o">=</span>
  <span class="nn">B_H_int</span><span class="p">.</span><span class="n">insert</span> <span class="n">1</span> <span class="n">t</span><span class="o">;</span>
  <span class="nn">B_H_int</span><span class="p">.</span><span class="n">insert</span> <span class="n">3</span> <span class="n">t</span><span class="o">;</span>
  <span class="nn">B_H_int</span><span class="p">.</span><span class="n">insert</span> <span class="n">2</span> <span class="n">t</span><span class="o">;</span>
  <span class="nn">B_H_int</span><span class="p">.</span><span class="n">insert</span> <span class="n">4</span> <span class="n">t</span><span class="o">;</span>
  <span class="nn">List</span><span class="p">.</span><span class="n">iter</span> <span class="o">(</span><span class="nn">Printf</span><span class="p">.</span><span class="n">printf</span> <span class="s2">&#34;%d &#34;</span><span class="o">)</span> <span class="o">(</span><span class="nn">B_H_int</span><span class="p">.</span><span class="n">toList</span> <span class="n">t</span><span class="o">);</span>
  <span class="n">print_newline</span> <span class="bp">()</span>

<span class="c">(* 测试从 list 构建 *)</span>
<span class="k">let</span> <span class="bp">()</span> <span class="o">=</span>
  <span class="k">let</span> <span class="n">t</span> <span class="o">=</span> <span class="nn">B_H_int</span><span class="p">.</span><span class="n">fromList</span> <span class="n">20</span> <span class="n">0</span> <span class="o">[</span> <span class="n">1</span><span class="o">;</span> <span class="n">3</span><span class="o">;</span> <span class="n">5</span><span class="o">;</span> <span class="n">7</span><span class="o">;</span> <span class="n">9</span><span class="o">;</span> <span class="n">2</span><span class="o">;</span> <span class="n">4</span><span class="o">;</span> <span class="n">6</span><span class="o">;</span> <span class="n">8</span><span class="o">;</span> <span class="n">0</span> <span class="o">]</span> <span class="k">in</span>
  <span class="k">assert</span> <span class="o">(</span><span class="nn">B_H_int</span><span class="p">.</span><span class="n">toList</span> <span class="n">t</span> <span class="o">=</span> <span class="o">[</span> <span class="n">9</span><span class="o">;</span> <span class="n">8</span><span class="o">;</span> <span class="n">7</span><span class="o">;</span> <span class="n">6</span><span class="o">;</span> <span class="n">5</span><span class="o">;</span> <span class="n">4</span><span class="o">;</span> <span class="n">3</span><span class="o">;</span> <span class="n">2</span><span class="o">;</span> <span class="n">1</span><span class="o">;</span> <span class="n">0</span> <span class="o">])</span>

<span class="c">(* 测试两种堆实现的结果一致 *)</span>
<span class="k">let</span> <span class="bp">()</span> <span class="o">=</span>
  <span class="k">let</span> <span class="n">t1</span> <span class="o">=</span> <span class="nn">H_int</span><span class="p">.</span><span class="n">fromList</span> <span class="o">[</span> <span class="n">1</span><span class="o">;</span> <span class="n">3</span><span class="o">;</span> <span class="n">5</span><span class="o">;</span> <span class="n">7</span><span class="o">;</span> <span class="n">9</span><span class="o">;</span> <span class="n">2</span><span class="o">;</span> <span class="n">4</span><span class="o">;</span> <span class="n">6</span><span class="o">;</span> <span class="n">8</span><span class="o">;</span> <span class="n">0</span> <span class="o">]</span>
  <span class="ow">and</span> <span class="n">t2</span> <span class="o">=</span> <span class="nn">B_H_int</span><span class="p">.</span><span class="n">fromList</span> <span class="n">20</span> <span class="n">0</span> <span class="o">[</span> <span class="n">1</span><span class="o">;</span> <span class="n">3</span><span class="o">;</span> <span class="n">5</span><span class="o">;</span> <span class="n">7</span><span class="o">;</span> <span class="n">9</span><span class="o">;</span> <span class="n">2</span><span class="o">;</span> <span class="n">4</span><span class="o">;</span> <span class="n">6</span><span class="o">;</span> <span class="n">8</span><span class="o">;</span> <span class="n">0</span> <span class="o">]</span> <span class="k">in</span>
  <span class="k">assert</span> <span class="o">(</span><span class="nn">H_int</span><span class="p">.</span><span class="n">toList</span> <span class="n">t1</span> <span class="o">=</span> <span class="nn">B_H_int</span><span class="p">.</span><span class="n">toList</span> <span class="n">t2</span><span class="o">)</span>
</code></pre></div><p>项目采用 <code>make</code> 进行构建</p>
<div class="highlight"><pre class="chroma"><code class="language-makefile" data-lang="makefile"><span class="nf">.PHONY</span><span class="o">:</span>	<span class="n">all</span> <span class="n">clean</span> <span class="n">byte</span> <span class="n">native</span> <span class="n">profile</span> <span class="n">debug</span> <span class="n">test</span> <span class="n">run</span>

<span class="nv">OCB_FLAGS</span> <span class="o">=</span> -tag bin_annot
<span class="nv">OCB</span> <span class="o">=</span> 		ocamlbuild <span class="k">$(</span>OCB_FLAGS<span class="k">)</span>

<span class="nf">all</span><span class="o">:</span> <span class="n">native</span> <span class="n">byte</span>

<span class="nf">clean</span><span class="o">:</span>
	<span class="k">$(</span>OCB<span class="k">)</span> -clean

<span class="nf">native</span><span class="o">:</span>
	<span class="k">$(</span>OCB<span class="k">)</span> main.native

<span class="nf">byte</span><span class="o">:</span>
	<span class="k">$(</span>OCB<span class="k">)</span> main.byte

<span class="nf">profile</span><span class="o">:</span>
	<span class="k">$(</span>OCB<span class="k">)</span> -tag profile main.native

<span class="nf">debug</span><span class="o">:</span>
	<span class="k">$(</span>OCB<span class="k">)</span> -tag debug main.byte

<span class="nf">run</span><span class="o">:</span> <span class="n">native</span>
	./main.native
</code></pre></div><h1 id="心得体会">心得体会</h1>
<p>第一次用纯函数式语言写一个算法，和之前上课时在 <code>toplevel</code> 中测试还是有较大的区别，包括如何实现多文件编译，如何配置编译，如何编写模块等都困扰了我很久。最后依靠文档、手册、github上的一些项目等学会了如何构建一个 <code>Ocaml</code> 项目。</p>
<p>此外，<code>Ocaml</code> 标准库也给了我很大的帮助，里面的标准库如 <code>Set</code> 等都写得很清晰，有很详细的注释，我写的堆的模块结构、函子等都是参考 <code>Set</code> 库的处理方式。</p>
<p>对于函数式语言，特别是纯函数式的思想我也有了更深刻的理解。一开始我还是以命令式的思维来编写代码，认为不使用 <code>for</code> 、<code>while</code> 语句就是函数式，代码中还是存在很多 <code>mutable</code> 、<code>ref</code> 等内容，即存在副作用，也就导致了函数操作还是对一个“对象”进行操作，而不是每次返回一个新的结果。后来参考各种项目、教程，才发现了纯函数式“无副作用”的核心思想及处理方式。</p>
<p>我认为，纯函数式语言更贴近数学模型，可以用非常简练的语句写出算法，其核心是递归和迭代，例如我的堆通过短短几十行语句就实现了，通过一些精巧的递归就实现了命令式语言要写大量循环命令才能实现的功能。但这些简洁也带来了一些问题，比如代码抽象程度较高，理解起来较为困难；大量递归对栈上空间造成很大压力，性能降低了的同时也难以进行大量的计算，需要花费很大的力气去消除尾递归（但通常很难实现）；不允许副作用造成时间、空间的浪费；思维在两种思想中的转换较为困难等。但总的来说，函数式语言还是有很好的前景，从近年来越来越多的语言都开始支持函数式的写法（如 <code>lambda</code> 表达式等）可以看出。如 <code>C++</code> 、<code>Python</code> 、<code>Java</code> 等都有函数式的写法，我在平时编写代码的过程中也会用到函数式语句来简化代码。函数式语言和命令式语言在未来可能会越走越近，取长补短，最后融为一体。</p>
]]></content>
		</item>
		
		<item>
			<title>GPG使用笔记</title>
			<link>https://blog.creedowl.com/posts/gpg/</link>
			<pubDate>Thu, 02 Apr 2020 14:47:31 +0800</pubDate>
			
			<guid>https://blog.creedowl.com/posts/gpg/</guid>
			<description>Why GPG? GPG 是一个密码学软件，用于加密、签名通信内容及管理非对称密码学的密钥，具体历史、介绍可以在wiki中看到，官网 而我使用 GPG 的缘由是，在 Github 上提</description>
			<content type="html"><![CDATA[<h2 id="why-gpg">Why GPG?</h2>
<p><a href="https://zh.wikipedia.org/wiki/GnuPG">GPG</a> 是一个密码学软件，用于加密、签名通信内容及管理非对称密码学的密钥，具体历史、介绍可以在wiki中看到，<a href="https://gnupg.org/">官网</a></p>
<p>而我使用 GPG 的缘由是，在 Github 上提交时，如果使用 GPG 对提交进行签名，会有一个<code>Verified</code>标识<del>看起来很nb</del></p>
<p><img src="https://blog-1251984664.cos.ap-shanghai.myqcloud.com/UTOOLS1585795152275.png/compress" alt="UTOOLS1585795152275.png"></p>
<p>当然使用 GPG 不仅是为了好看，它能确保你提供的信息是由你自己发布的，无法被别人篡改、冒充，前提是保护好自己的 GPG 私钥</p>
<h2 id="安装">安装</h2>
<p>这边以 MacOS 为示例进行操作</p>
<p><strong>强烈建议 MacOS 用户使用 <a href="https://gpgtools.org/">GPG Suite</a> 进行操作，可以避免一些奇奇怪怪的问题</strong></p>
<div class="highlight"><pre class="chroma"><code class="language-shell" data-lang="shell">brew cask install gpg-suite
</code></pre></div><p><del>仅安装 GPG 本体</del></p>
<div class="highlight"><pre class="chroma"><code class="language-shell" data-lang="shell"><span class="c1"># deprecated</span>
brew install gpg
</code></pre></div><h2 id="生成密钥">生成密钥</h2>
<p>GPG 采用非对称加密，一切的操作都需要有密钥。网上一些教程中的操作与我使用的 GPG 版本 <code>gpg (GnuPG/MacGPG2) 2.2.17</code> 有点出入，以下操作都在我所使用的版本下进行</p>
<p>有两个命令用于生成密钥，为<code>gpg --gen-key</code>和<code>gpg --full-gen-key</code>，区别是前者采用默认的配置不可更改，后者能自定义配置。我们采用后一种，首先选择密钥类型，默认的<code>1</code>就可以了</p>
<div class="highlight"><pre class="chroma"><code class="language-shell" data-lang="shell">$ gpg --full-gen-key
gpg <span class="o">(</span>GnuPG/MacGPG2<span class="o">)</span> 2.2.17<span class="p">;</span> Copyright <span class="o">(</span>C<span class="o">)</span> <span class="m">2019</span> Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

请选择您要使用的密钥类型：
   <span class="o">(</span>1<span class="o">)</span> RSA 和 RSA （默认）
   <span class="o">(</span>2<span class="o">)</span> DSA 和 Elgamal
   <span class="o">(</span>3<span class="o">)</span> DSA（仅用于签名）
   <span class="o">(</span>4<span class="o">)</span> RSA（仅用于签名）
您的选择是？ <span class="m">1</span>
</code></pre></div><p>接着选择密钥长度，默认<code>2048</code>即可</p>
<div class="highlight"><pre class="chroma"><code class="language-text" data-lang="text">RSA 密钥的长度应在 1024 位与 4096 位之间。
您想要使用的密钥长度？(2048) 2048
</code></pre></div><p>然后设定密钥的有效期限，我们没有特殊需求，设置永不过期即可</p>
<div class="highlight"><pre class="chroma"><code class="language-text" data-lang="text">请设定这个密钥的有效期限。
         0 = 密钥永不过期
      &lt;n&gt;  = 密钥在 n 天后过期
      &lt;n&gt;w = 密钥在 n 周后过期
      &lt;n&gt;m = 密钥在 n 月后过期
      &lt;n&gt;y = 密钥在 n 年后过期
密钥的有效期限是？(0) 0
</code></pre></div><p>确认上述设置后，依次输入姓名、邮箱和注释，这里我使用随机信息用于演示</p>
<pre><code>GnuPG 需要构建用户标识以辨认您的密钥。

真实姓名： naive
电子邮件地址： naive@test.com
注释： too young too simple
您选定了此用户标识：
    “naive (too young too simple) &lt;naive@test.com&gt;”

更改姓名（N）、注释（C）、电子邮件地址（E）或确定（O）/退出（Q）？
</code></pre>
<p>确定后，需要设定密钥的密码，如果你采用了 <code>GPG Suite</code> ，会出现一个窗口，在此输入密码</p>
<p><img src="https://blog-1251984664.cos.ap-shanghai.myqcloud.com/UTOOLS1585796741624.png/compress" alt="UTOOLS1585796741624.png"></p>
<p>如果没使用<code>GPG Suite</code>，则会在你当前终端弹出一个全屏终端窗口，同样是输入密码。</p>
<blockquote>
<p>注意，在 mac 终端环境下，如果语言是中文的话，这个终端窗口可能会乱码，但不影响使用，也可以临时设置语言为英文解决这个问题<code>LANGUAGE=en gpg --full-generate-key</code>。但千万不要用<code>ctrl-c</code>结束这个进程，这会破坏当前终端环境，后面的输入会变得很奇怪，而且后台进程不会结束，会占满你的CPU</p>
</blockquote>
<div class="highlight"><pre class="chroma"><code class="language-text" data-lang="text">我们需要生成大量的随机字节。在质数生成期间做些其他操作（敲打键盘
、移动鼠标、读写硬盘之类的）将会是一个不错的主意；这会让随机数
发生器有更好的机会获得足够的熵。
gpg: 密钥 4C26D2E451FFDAF1 被标记为绝对信任
gpg: 目录‘/Users/creedowl/.gnupg/openpgp-revocs.d’已创建
gpg: 吊销证书已被存储为‘/Users/creedowl/.gnupg/openpgp-revocs.d/0B3C52F1EF07B14EA7C45C9A4C26D2E451FFDAF1.rev’
公钥和私钥已经生成并被签名。

pub   rsa2048 2020-04-02 [SC]
      0B3C52F1EF07B14EA7C45C9A4C26D2E451FFDAF1
uid                      naive (too young too simple) &lt;naive@test.com&gt;
sub   rsa2048 2020-04-02 [E]
</code></pre></div><p>这样，密钥就生成完成，其中<code>4C26D2E451FFDAF1</code>就是你的密钥名，后续操作都需要通过这个密钥名选择密钥，但也可以通过之前设置的姓名或邮箱代替操作</p>
<h2 id="gpg-常用操作">GPG 常用操作</h2>
<p>GPG 最基础的操作就是对文件等信息进行加密、签名，首先创建一个测试文件<code>test.txt</code></p>
<div class="highlight"><pre class="chroma"><code class="language-shell" data-lang="shell"><span class="nb">echo</span> <span class="nb">test</span> &gt; test.txt
</code></pre></div><h3 id="加密">加密</h3>
<div class="highlight"><pre class="chroma"><code class="language-shell" data-lang="shell">gpg --recipient creedowl --output test.txt.gpg --encrypt test.txt
</code></pre></div><p>其中<code>--recipient</code>指定接收者，简写为<code>-r</code>，设定接受者时需要你本地有接收者的<strong>公钥</strong>，可以用密钥名、姓名或者邮箱进行指定。<code>--output</code>指定输出文件名，可省略。<code>--encrypt</code>指定待加密的文件，简写为<code>-e</code>。这样在你当前目录下会生成一个<code>test.txt.gpg</code>即加密后的文件，你就可以把这个文件发送给接受者，不必担心文件内容被第三方读取</p>
<p><strong>注意</strong>这里生成加密文件为二进制格式，有时候（如通过邮件发送）不便于操作，可以加上<code>--armor</code>参数（简写为<code>-a</code>）生成文本格式的密文，如</p>
<div class="highlight"><pre class="chroma"><code class="language-shell" data-lang="shell">gpg -r creedowl -ae test.txt
</code></pre></div><p>生成的加密文件为<code>test.txt.asc</code></p>
<div class="highlight"><pre class="chroma"><code class="language-text" data-lang="text">-----BEGIN PGP MESSAGE-----

hQEMA9KmvDhAMTGrAQf+KITqx4HTpWsevIEfRFlw5g+jt8l8ol3fuww0gBoMEPBG
KaqxCmjIpHZdGmaXwBSKadmeieZGLsm1CYuf4gbv4FpdWgveVWnLvB/HmwuvnmJt
YhTtapNgkQ++mwPbH4WKeOFQ58U5dZ7C+R8rrucRRYQBRTH6AJQY82aV1KXTqyF1
D8c/2ANW7aCWr8W/ogCbEqH47VE3foP+aSD+JMPtdnaYWIPzyr5x5lw6ASwxD4hB
ikOUcJTxp+GyFLTd3n1zCvSP9ZQzvolur7soqZVP8t4hxlALGL+KYN4YgubcPt5y
bucMYWkbLlZM+AuAYzSCLaSmkgkiYUFEkwKJWdLr+NJGAY5Mk+bnEP2k/kKxl9vo
/LVsloiYbg9M184LxUNfZRBnKcI9vKw/C6uU2Ee5wKUMsy3Epnp60Fe5u5fj5yn7
0JjMBmY+gQ==
=8K/i
-----END PGP MESSAGE-----
</code></pre></div><h3 id="解密">解密</h3>
<p>有了密文就要对其进行解密得到原文。注意要进行解密，你必须有接受者的<strong>私钥</strong>。公钥是公开的，任何人都可以查看、使用，而<strong>私钥</strong>是私有的，一定要保管好，如果泄露了这个 GPG 密钥就等同于失效了，需要吊销。这就是非对称加密</p>
<div class="highlight"><pre class="chroma"><code class="language-shell" data-lang="shell">	gpg --decrypt test.txt.asc --output out.txt
</code></pre></div><p>其中<code>--decrypt</code>声明该命令进行解密，简写为<code>-d</code>，<code>--output</code>指定输出文件，默认输出到终端。注意解密时需要输入你密钥的密码，所以也一定要保管好密码</p>
<h3 id="签名">签名</h3>
<p>有的时候不需要对文件进行签名，只需要确保文件是由本人发出的，这个时候就可以用到签名</p>
<div class="highlight"><pre class="chroma"><code class="language-shell" data-lang="shell">gpg --sign test.txt
</code></pre></div><p>会生成二进制的签名文件<code>test.txt.gpg</code>，如果想生成文本类型的签名，使用</p>
<div class="highlight"><pre class="chroma"><code class="language-shell" data-lang="shell">gpg --clearsign test.txt
</code></pre></div><p>会生成文本类型的签名文件<code>test.txt.asc</code></p>
<p>注意以上两种方式原文和签名是在同一个文件中，查看<code>test.txt.asc</code></p>
<div class="highlight"><pre class="chroma"><code class="language-text" data-lang="text">-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

test
-----BEGIN PGP SIGNATURE-----

iQFDBAEBCAAtFiEECzxS8e8HsU6nxFyaTCbS5FH/2vEFAl6FX+cPHG5haXZlQHRl
c3QuY29tAAoJEEwm0uRR/9rxGNsH/AmjJiT19q5caq9Pe8tMRnR2yvZIGWbMD3Ch
UTkyGMkSPMIo6XRpYPlANe2/dddBT2l7EAPy2lAxaa8LcjsxtRhJG4GxMaCcujik
M6MX3IOfa1m9aDP1IY3Sh2U07j3t+8dawahR8EUF2D+rtvx24ybnwpWpKbDVsGyp
Mt06BI3pWC3opMRYPCl21IBhPhaPPnvXF7X8DMP+2VqZye2juX2oG1KMkZWQuiBb
/D7DYlx6/ainu0cJw6UL8YXHag7tkUeKtTPQbBpCsszOuxJZ74gduIUzDcokayCB
DM9qB/GpUyVOOTwfLMBta6JP5BO8ppPaPdPX46kKDqLKG7eh4bQ=
=Q4/W
-----END PGP SIGNATURE-----
</code></pre></div><p>因为是签名，不会对内容进行加密，所以直接显示原文。如果想把原始文件分离出来，使用</p>
<div class="highlight"><pre class="chroma"><code class="language-shell" data-lang="shell">gpg --output out.txt -d test.txt.gpg
</code></pre></div><p>分离的同时还顺便校验了签名。注意这里<code>test.txt.gpg</code>是上面生成的二进制<strong>签名</strong>文件，也可以用<code>test.txt.asc</code></p>
<p>如果每次都要分离，就很麻烦，而且没有安装 GPG 的用户就无法使用源文件了。所以更好的方式是分离源文件和签名文件</p>
<div class="highlight"><pre class="chroma"><code class="language-shell" data-lang="shell">gpg --detach-sign --armor test.txt
</code></pre></div><p>这样会生成一个单独的、文本类型的签名文件<code>test.txt.sig</code></p>
<blockquote>
<p>注意：如果有多个私钥，可以用<code>-u</code>参数选择</p>
</blockquote>
<h3 id="签名校验">签名校验</h3>
<p>收到文件和签名文件后，需要对签名进行校验</p>
<div class="highlight"><pre class="chroma"><code class="language-shell" data-lang="shell">gpg --verify test.txt.sig test.txt
</code></pre></div><p>如果签名校验成功，会显示</p>
<div class="highlight"><pre class="chroma"><code class="language-shell" data-lang="shell">gpg: 签名建立于 四  4/ <span class="m">2</span> 11:49:35 <span class="m">2020</span> CST
gpg:               使用 RSA 密钥 34B368EFC6FA8941B4A096CFFA4C884FBE8164D7
gpg: 完好的签名，来自于 “creedowl &lt;creedowl@gmail.com&gt;” <span class="o">[</span>绝对<span class="o">]</span>
</code></pre></div><h3 id="导入导出">导入导出</h3>
<p>当你需要与他人交换数据时，需要导出自己的公钥，同时导入他人的公钥；而如果你更换电脑，就需要导出自己的私钥。这就涉及到了密钥的导入导出</p>
<div class="highlight"><pre class="chroma"><code class="language-shell" data-lang="shell">gpg -a --output public_key.txt --export naive
</code></pre></div><p>上述命令用于导出公钥，相关参数在上文都有介绍。接着你就可以把<code>public_key.txt</code>发布给他人</p>
<div class="highlight"><pre class="chroma"><code class="language-shell" data-lang="shell">gpg -a --output private_key.txt --export-secret-keys naive
</code></pre></div><p>上述命令用于导出私钥，一定要保管好，建议在脱机情况下进行操作</p>
<div class="highlight"><pre class="chroma"><code class="language-shell" data-lang="shell">gpg --improt <span class="o">[</span>密钥文件<span class="o">]</span>
</code></pre></div><p>上述命令用于导入密钥，不区分公钥私钥</p>
<h3 id="查看所有密钥">查看所有密钥</h3>
<div class="highlight"><pre class="chroma"><code class="language-shell" data-lang="shell">gpg -k
</code></pre></div><h3 id="生成吊销证书">生成吊销证书</h3>
<p>当你的私钥泄露或者更换时，需要吊销失效的密钥，这时就需要使用吊销证书</p>
<div class="highlight"><pre class="chroma"><code class="language-shell" data-lang="shell">gpg --gen-revoke naive
</code></pre></div><p>接着选择吊销原因，就能生成吊销证书</p>
<div class="highlight"><pre class="chroma"><code class="language-text" data-lang="text">sec  rsa2048/4C26D2E451FFDAF1 2020-04-02 naive (too young too simple) &lt;naive@test.com&gt;

要为这个密钥创建一个吊销证书吗？(y/N) y
请选择吊销的原因：
  0 = 未指定原因
  1 = 密钥已泄漏
  2 = 密钥被替换
  3 = 密钥不再使用
  Q = 取消
（也许您会想要在这里选择 1）
您的决定是什么？ 1
请输入描述（可选）；以空白行结束：
&gt;
吊销原因：密钥已泄漏
（未给定描述）
这样可以吗？ (y/N) y
已强行使用 ASCII 字符封装过的输出。
-----BEGIN PGP PUBLIC KEY BLOCK-----
Comment: This is a revocation certificate

****
-----END PGP PUBLIC KEY BLOCK-----
已创建吊销证书。

请把这个文件转移到一个您可以藏起来的介质上；如果坏人获取到了这
份证书的话，那么他就能使用它并让您的密钥无法继续使用。把此证书
打印出来再存放到安全的地方也是很好的方法，以免您的保存媒体变得
不可读。但是千万小心：您机器上的打印系统可能会在打印过程中储存
这些数据，并使得其他人看到！
</code></pre></div><p>只需要保存中间的吊销证书，当密钥泄露时将其发送到公钥服务器，就可以吊销旧密钥</p>
<h3 id="编辑密钥">编辑密钥</h3>
<p>有时候需要对密钥进行编辑，如修改信任度，生成子密钥等，使用以下命令</p>
<div class="highlight"><pre class="chroma"><code class="language-shell" data-lang="shell">gpg --edit-key naive
</code></pre></div><p>接着就会进入交互模式等待命令，输入<code>help</code>可以查看所有操作</p>
<div class="highlight"><pre class="chroma"><code class="language-text" data-lang="text">gpg (GnuPG/MacGPG2) 2.2.17; Copyright (C) 2019 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

私钥可用。

sec  rsa2048/4C26D2E451FFDAF1
     创建于：2020-04-02  有效至：永不       可用于：SC
     信任度：绝对        有效性：绝对
ssb  rsa2048/A86AB46BDC677F8F
     创建于：2020-04-02  有效至：永不       可用于：E
[ 绝对 ] (1). naive (too young too simple) &lt;naive@test.com&gt;

gpg&gt;
</code></pre></div><h2 id="使用-gpg-签名-git-commit">使用 GPG 签名 Git Commit</h2>
<p>这就是我使用 GPG 的原因，下面开始配置，首先配置 git</p>
<div class="highlight"><pre class="chroma"><code class="language-shell" data-lang="shell"><span class="c1"># 设置全局默认密钥</span>
git config --global user.signingkey &lt;密钥ID&gt;

<span class="c1"># 设置强制使用GPG签名</span>
git config --global commit.gpgsign <span class="nb">true</span>
</code></pre></div><p>之后在提交时，只需要带上<code>-S</code>参数，就能对提交进行签名</p>
<div class="highlight"><pre class="chroma"><code class="language-shell" data-lang="shell">git commit -S -m <span class="s2">&#34;xxx&#34;</span>
</code></pre></div><p>查看 git 日志时，带上<code>--show-signature</code>参数就能看到签名信息</p>
<div class="highlight"><pre class="chroma"><code class="language-shell" data-lang="shell">git log --show-signature
</code></pre></div><p>最后一步，需要把你的公钥提交到 github，否则它怎么知道这个签名是你的呢？将之前生成的文本类型的公钥<code>public_key.txt</code>提交到 <a href="https://github.com/settings/keys">github</a>，之后你的提交在 github 上就有像开头那样的绿色小勾了</p>
<h2 id="faq">FAQ</h2>
<h3 id="解决git提交时出现error-gpg-failed-to-sign-the-data">解决git提交时出现<code>error: gpg failed to sign the data</code></h3>
<p>以下是原始解决方案，虽然能解决问题，但体验不好，在 IDE 中提交时难以输入密码，建议 MacOS 用户采用上面说的 <code>GPG Suite</code>进行操作，它可以接管密码输入操作，弹出一个独立窗口用于输入，还能将密码添加到 iCloud 钥匙串中，避免重复输入密码。如果你已经在使用<code>GPG Suite</code>，但仍然遇到了这个问题，可以尝试重装<code>GPG Suite</code></p>
<ol>
<li>
<p>导出密钥（私钥）<strong>重要</strong></p>
</li>
<li>
<p>卸载 GPG</p>
<div class="highlight"><pre class="chroma"><code class="language-shell" data-lang="shell">brew uninstall gnupg gnutls
   
brew cask uninstall gpg-suite
</code></pre></div></li>
<li>
<p>删除 gpg 文件夹，<strong>请确认已经导出密钥</strong></p>
<div class="highlight"><pre class="chroma"><code class="language-shell" data-lang="shell">rm -rf ~/.gnupg
</code></pre></div></li>
<li>
<p>重新安装<code>GPG Suite</code>，<strong>不需要手动安装 GPG</strong></p>
<div class="highlight"><pre class="chroma"><code class="language-shell" data-lang="shell">brew cask install gpg-suite
</code></pre></div></li>
<li>
<p>导入密钥</p>
</li>
</ol>
<p>这时 <code>GPG Suite</code>应该已经可以正常工作了</p>
<hr>
<p><em>原始方案</em></p>
<p>首先测试<code>gpg</code>工作是否正常</p>
<div class="highlight"><pre class="chroma"><code class="language-shell" data-lang="shell"><span class="nb">echo</span> <span class="s2">&#34;test&#34;</span> <span class="p">|</span> gpg --clearsign
</code></pre></div><p>如果出现下面的错误</p>
<div class="highlight"><pre class="chroma"><code class="language-text" data-lang="text">-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

test
gpg: 签名时失败： Inappropriate ioctl for device
gpg: [stdin]: clear-sign failed: Inappropriate ioctl for device
</code></pre></div><p>说明<code>gpg</code>无法打开输入密码的窗口。在生成<code>gpg key</code>时，如果指定了密码，在签名时就需要输入。而这时<code>gpg</code>不知道从哪里读取输入，所以可以用以下的命令指定<code>tty</code></p>
<div class="highlight"><pre class="chroma"><code class="language-shell" data-lang="shell"><span class="nb">export</span> <span class="nv">GPG_TTY</span><span class="o">=</span><span class="k">$(</span>tty<span class="k">)</span> 
</code></pre></div><p>这样就可以正常弹出输入密码的窗口。</p>
<hr>
<p>但我不喜欢这个全屏窗口，主要是在mac上<code>gpg</code>语言为中文时，这个窗口会乱码，所以用另一个方法解决。编辑<code>~/.gnupg/gpg.conf</code>，添加以下内容</p>
<div class="highlight"><pre class="chroma"><code class="language-text" data-lang="text">use-agent 
pinentry-mode loopback
</code></pre></div><p>再编辑<code>~/.gnupg/gpg-agent.conf</code>，添加以下内容</p>
<div class="highlight"><pre class="chroma"><code class="language-text" data-lang="text">allow-loopback-pinentry
</code></pre></div><p>最后输入<code>echo RELOADAGENT | gpg-connect-agent</code>重启<code>agent</code>就生效了，这样可以直接在终端输入密码</p>
<blockquote>
<p><a href="https://d.sb/2016/11/gpg-inappropriate-ioctl-for-device-errors">source</a></p>
</blockquote>
]]></content>
		</item>
		
		<item>
			<title>重新开始</title>
			<link>https://blog.creedowl.com/posts/renew/</link>
			<pubDate>Thu, 19 Mar 2020 21:28:13 +0800</pubDate>
			
			<guid>https://blog.creedowl.com/posts/renew/</guid>
			<description>突然发现距上一篇blog发布已经过去了快一年的时间，原先的blog也疏于打理，只有寥寥几篇文章，图床基本失效。主要因为我比较懒，认真整理一篇</description>
			<content type="html"><![CDATA[<p>突然发现距上一篇blog发布已经过去了快一年的时间，原先的blog也疏于打理，只有寥寥几篇文章，图床基本失效。主要因为我比较懒，认真整理一篇blog也是费时费力的事情。但自己整理一些文章是很有意义的事情，能记录下自己的学习历程，总结知识，再次遇到相同的问题也可以回头参考。</p>
<p>blog一直没更新有个诱因是微博图床开启了防盗链，导致blog中许多图片都无法访问了<del>老王背锅</del>，又懒得修复。同时看上了<a href="https://gohugo.io/">hugo</a>这个框架，正好也在学习<code>golang</code>，就想从<code>hexo</code>更换到<code>hugo</code>。但这两个框架有个很大的区别<del>我觉得</del>，就是<code>hexo</code>是一堆前端工程师搞的，而<code>hugo</code>是一堆后端工程师搞的，这就导致了<code>hugo</code>没有啥好看的主题，也就一直鸽下去了。</p>
<p>去年年中的时候就打算和@queensferry写一个Hugo主题<a href="https://github.com/queensferryme/hugo-theme-verse">verse</a>，立项的时候很多想法，但做起来就不想动了。摸完页面框架后就鸽了，一方面实在不会设计，另一方面调各种样式是真的蛋疼。鸽了半年又捡起来<del>虽然我没写啥</del>，不过渐渐也有点样子了。但今天发现了一个很棒的主题<a href="https://github.com/Track3/hermit">hermit</a>，几乎就是我想要的，里面很多设计就是我当初构想的内容，马上捡起了blog。</p>
<p>这一年来也计划整理个人笔记软件，在尝试了数个软件后，有印象笔记，为知笔记，有道云笔记，onenote及一堆开源笔记应用，都不太满足我的需求。最后发现了notion这个东西贼香（主要对学生免费了），最后就选择了它。不过这也导致我手上一堆笔记分散在各处，打算后面整理清楚<del>水</del>几篇blog出来。</p>
]]></content>
		</item>
		
		<item>
			<title>Vue.js 从入坑到入土</title>
			<link>https://blog.creedowl.com/posts/vue-demo-instruction/</link>
			<pubDate>Tue, 23 Apr 2019 21:25:25 +0800</pubDate>
			
			<guid>https://blog.creedowl.com/posts/vue-demo-instruction/</guid>
			<description>Vue.js 从入坑到入土 近年来前端领域发展飞速，从最早的直接写html, js, css 到使用jQuery快速开发，再到近年来使用Vuejs，Reactjs 实现工</description>
			<content type="html"><![CDATA[<h1 id="vuejs-从入坑到入土">Vue.js 从入坑到入土</h1>
<p>近年来前端领域发展飞速，从最早的直接写html, js, css 到使用jQuery快速开发，再到近年来使用Vuejs，Reactjs 实现工程化开发，也就短短十几年。虽然前端项目结构越来越复杂，但也越来越规范，清晰，更加工程化。当前较为流行的前端框架有Vuejs、Reactjs、Angularjs 等，而Vuejs由于其灵(zhong)活(wen)轻(wen)便(dang)，较为流行。虽然Vuejs有完善的中文文档，但文档主要在介绍其用法，缺少如何实现一个完整项目的指导，本文就介绍如何用Vuejs创建一个完整的前端项目，并与后端进行交互。</p>
<p><strong>本文基于<a href="mailto:Vuejs@2.x">Vuejs@2.x</a>及<a href="mailto:VueCLI@3.x">VueCLI@3.x</a></strong></p>
<ol>
<li>所需技术栈：html, javascript, css基础知识，其中js建议了解<strong>ES2015</strong>标准，特别要掌握<strong>promise</strong>的使用</li>
<li>使用的工具：<del>宇宙第一编辑器</del> VSCode —— 现在对vue项目的支持已经很好了，可以自动安装vue项目需要的插件</li>
</ol>
<h1 id="0x0-安装">0x0 安装</h1>
<blockquote>
<p>推荐使用<a href="https://yarnpkg.com/lang/zh-hans">yarn</a>代替npm作为包管理器</p>
</blockquote>
<h2 id="安装vuecli">安装VueCLI</h2>
<blockquote>
<p>VueCLI是Vuejs官方推出的项目脚手架工具，可以方便的创建、管理Vue项目，还提供优雅的web管理界面</p>
</blockquote>
<p>全局安装<a href="https://cli.vuejs.org/zh/">VueCLI</a></p>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash">yarn global add @vue/cli
</code></pre></div><h2 id="安装vue-devtools">安装Vue-devtools</h2>
<blockquote>
<p><a href="https://github.com/vuejs/vue-devtools">Vue-devtools</a> 是vue官方的浏览器开发插件，可以方便的在浏览器中调试vue应用</p>
</blockquote>
<p>Vue-devtools 提供多平台的浏览器插件可以直接安装使用</p>
<p><a href="https://github.com/vuejs/vue-devtools#installation">installation</a></p>
<h1 id="0x1-新建项目">0x1 新建项目</h1>
<p>使用 <code>vue create vue_demo</code> 创建新项目，接下来会出现一个交互式命令行帮你配置项目。</p>
<p><strong>注意此处不要使用默认的配置！具体配置参考下图</strong></p>
<blockquote>
<p>坑x1 默认的配置十分简单，新手经常不知道怎么写多页面，弄不明白项目怎么组织的，所以需要自定义配置，添加vue-router插件实现多页面跳转</p>
</blockquote>
<p><a href="https://asciinema.org/a/240155"><img src="https://asciinema.org/a/240155.svg" alt="asciicast"></a></p>
<h1 id="0x2-项目框架">0x2 项目框架</h1>
<p>此时项目已经创建成功，如果你按照提示启动项目就能看到经典的vue启动页</p>
<p><img src="http://ww1.sinaimg.cn/large/0071ouepgy1g1yg2r5ysxj30up0mx3zv.jpg" alt="image"></p>
<p>当前的项目结构如下</p>
<div class="highlight"><pre class="chroma"><code class="language-text" data-lang="text">.                     项目根目录
├── README.md 
├── babel.config.js
├── package.json      项目主配置文件
├── public            公共资源文件夹
│   ├── favicon.ico
│   └── index.html    入口html
├── src               源码根目录
│   ├── App.vue       入口文件
│   ├── assets        资源文件夹
│   │   └── logo.png
│   ├── components    组件文件夹
│   │   └── HelloWorld.vue 
│   ├── main.js       入口配置文件
│   ├── router.js     路由文件
│   ├── store.js      状态管理
│   └── views         页面文件夹
│       ├── About.vue
│       └── Home.vue
└── yarn.lock         依赖文件
</code></pre></div><p>在下面的章节具体介绍各个文件、文件夹的用处和如何组织项目。</p>
<h1 id="0x3-项目结构">0x3 项目结构</h1>
<p>用vue开发的项目一般是<a href="https://zh.wikipedia.org/wiki/%E5%8D%95%E9%A1%B5%E5%BA%94%E7%94%A8">SPA(单页应用)</a>，通过动态重写当前页面来与用户交互，而不会重新加载整个新页面。所以要组织多页面就要通过vue-router来组织页面，而vuex用来保存状态。</p>
<p>作为单页应用，当然需要一个入口html文件，也就是 <code>public/index.html</code> :</p>
<div class="highlight"><pre class="chroma"><code class="language-html" data-lang="html">public/index.html

<span class="cp">&lt;!DOCTYPE html&gt;</span>
<span class="p">&lt;</span><span class="nt">html</span> <span class="na">lang</span><span class="o">=</span><span class="s">&#34;en&#34;</span><span class="p">&gt;</span>
  <span class="p">&lt;</span><span class="nt">head</span><span class="p">&gt;</span>
    <span class="p">&lt;</span><span class="nt">meta</span> <span class="na">charset</span><span class="o">=</span><span class="s">&#34;utf-8&#34;</span><span class="p">&gt;</span>
    <span class="p">&lt;</span><span class="nt">meta</span> <span class="na">http-equiv</span><span class="o">=</span><span class="s">&#34;X-UA-Compatible&#34;</span> <span class="na">content</span><span class="o">=</span><span class="s">&#34;IE=edge&#34;</span><span class="p">&gt;</span>
    <span class="p">&lt;</span><span class="nt">meta</span> <span class="na">name</span><span class="o">=</span><span class="s">&#34;viewport&#34;</span> <span class="na">content</span><span class="o">=</span><span class="s">&#34;width=device-width,initial-scale=1.0&#34;</span><span class="p">&gt;</span>
    <span class="p">&lt;</span><span class="nt">link</span> <span class="na">rel</span><span class="o">=</span><span class="s">&#34;icon&#34;</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;&lt;%= BASE_URL %&gt;favicon.ico&#34;</span><span class="p">&gt;</span>
    <span class="p">&lt;</span><span class="nt">title</span><span class="p">&gt;</span>vue_demo<span class="p">&lt;/</span><span class="nt">title</span><span class="p">&gt;</span>
  <span class="p">&lt;/</span><span class="nt">head</span><span class="p">&gt;</span>
  <span class="p">&lt;</span><span class="nt">body</span><span class="p">&gt;</span>
    <span class="p">&lt;</span><span class="nt">noscript</span><span class="p">&gt;</span>
      <span class="p">&lt;</span><span class="nt">strong</span><span class="p">&gt;</span>We&#39;re sorry but vue_demo doesn&#39;t work properly without JavaScript enabled. Please enable it to continue.<span class="p">&lt;/</span><span class="nt">strong</span><span class="p">&gt;</span>
    <span class="p">&lt;/</span><span class="nt">noscript</span><span class="p">&gt;</span>
    <span class="p">&lt;</span><span class="nt">div</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;app&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
    <span class="c">&lt;!-- built files will be auto injected --&gt;</span>
  <span class="p">&lt;/</span><span class="nt">body</span><span class="p">&gt;</span>
<span class="p">&lt;/</span><span class="nt">html</span><span class="p">&gt;</span>
</code></pre></div><p>这是默认生成的 <code>index.html</code> ，而编译好的文件（网页内容）会被自动注入到 `` 中，也就意味着要使用vue开发的应用，浏览器必须支持js。</p>
<p>而#app所绑定的vue实例，则位于 <code>src/main.js</code></p>
<hr>
<div class="highlight"><pre class="chroma"><code class="language-js" data-lang="js"><span class="nx">src</span><span class="o">/</span><span class="nx">main</span><span class="p">.</span><span class="nx">js</span>

<span class="kr">import</span> <span class="nx">Vue</span> <span class="nx">from</span> <span class="s1">&#39;vue&#39;</span>
<span class="kr">import</span> <span class="nx">App</span> <span class="nx">from</span> <span class="s1">&#39;./App.vue&#39;</span>
<span class="kr">import</span> <span class="nx">router</span> <span class="nx">from</span> <span class="s1">&#39;./router&#39;</span>
<span class="kr">import</span> <span class="nx">store</span> <span class="nx">from</span> <span class="s1">&#39;./store&#39;</span>
<span class="nx">Vue</span><span class="p">.</span><span class="nx">config</span><span class="p">.</span><span class="nx">productionTip</span> <span class="o">=</span> <span class="kc">false</span>
<span class="k">new</span> <span class="nx">Vue</span><span class="p">({</span>
  <span class="nx">router</span><span class="p">,</span> <span class="c1">// 使用vue-router插件
</span><span class="c1"></span>  <span class="nx">store</span><span class="p">,</span> <span class="c1">// 使用vuex插件
</span><span class="c1"></span>  <span class="nx">render</span><span class="o">:</span> <span class="nx">h</span> <span class="p">=&gt;</span> <span class="nx">h</span><span class="p">(</span><span class="nx">App</span><span class="p">)</span> <span class="c1">// 渲染页面
</span><span class="c1"></span><span class="p">}).</span><span class="nx">$mount</span><span class="p">(</span><span class="s1">&#39;#app&#39;</span><span class="p">)</span> <span class="c1">// 绑定到 index.html -&gt; div#app
</span></code></pre></div><p>其中渲染的页面位于 <code>src/App.vue</code></p>
<hr>
<blockquote>
<p>xxx.vue 是vue中的<a href="https://cn.vuejs.org/v2/guide/single-file-components.html">单文件组件</a>，将 <code>html css javascript</code> 集合在同一个文件，便于组织管理</p>
</blockquote>
<div class="highlight"><pre class="chroma"><code class="language-html" data-lang="html">src/App.vue

<span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span>
  <span class="p">&lt;</span><span class="nt">div</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;app&#34;</span><span class="p">&gt;</span>
    <span class="p">&lt;</span><span class="nt">div</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;nav&#34;</span><span class="p">&gt;</span>
      <span class="p">&lt;</span><span class="nt">router-link</span> <span class="na">to</span><span class="o">=</span><span class="s">&#34;/&#34;</span><span class="p">&gt;</span>Home<span class="p">&lt;/</span><span class="nt">router-link</span><span class="p">&gt;</span>
      <span class="p">&lt;</span><span class="nt">router-link</span> <span class="na">to</span><span class="o">=</span><span class="s">&#34;/about&#34;</span><span class="p">&gt;</span>About<span class="p">&lt;/</span><span class="nt">router-link</span><span class="p">&gt;</span>
    <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
    <span class="p">&lt;</span><span class="nt">router-view</span><span class="p">/&gt;</span>
  <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
<span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">style</span><span class="p">&gt;</span>
<span class="p">#</span><span class="nn">app</span> <span class="p">{</span>
  <span class="k">font-family</span><span class="p">:</span> <span class="s1">&#39;Avenir&#39;</span><span class="p">,</span> <span class="n">Helvetica</span><span class="p">,</span> <span class="n">Arial</span><span class="p">,</span> <span class="kc">sans-serif</span><span class="p">;</span>
  <span class="kp">-webkit-</span><span class="n">font-smoothing</span><span class="p">:</span> <span class="n">antialiased</span><span class="p">;</span>
  <span class="kp">-moz-</span><span class="n">osx-font-smoothing</span><span class="p">:</span> <span class="n">grayscale</span><span class="p">;</span>
  <span class="k">text-align</span><span class="p">:</span> <span class="kc">center</span><span class="p">;</span>
  <span class="k">color</span><span class="p">:</span> <span class="mh">#2c3e50</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">#</span><span class="nn">nav</span> <span class="p">{</span>
  <span class="k">padding</span><span class="p">:</span> <span class="mi">30</span><span class="kt">px</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">#</span><span class="nn">nav</span> <span class="nt">a</span> <span class="p">{</span>
  <span class="k">font-weight</span><span class="p">:</span> <span class="kc">bold</span><span class="p">;</span>
  <span class="k">color</span><span class="p">:</span> <span class="mh">#2c3e50</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">#</span><span class="nn">nav</span> <span class="nt">a</span><span class="p">.</span><span class="nc">router-link-exact-active</span> <span class="p">{</span>
  <span class="k">color</span><span class="p">:</span> <span class="mh">#42b983</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">&lt;/</span><span class="nt">style</span><span class="p">&gt;</span>
</code></pre></div><p>其中 <code>是页面的 `html` 元素，`style` 是样式元素，还可以添加一个 `javascript` 为js元素。其中</code> 是路由组件，会被自动渲染成当前路由对应的页面</p>
<hr>
<div class="highlight"><pre class="chroma"><code class="language-js" data-lang="js"><span class="nx">src</span><span class="o">/</span><span class="nx">router</span><span class="p">.</span><span class="nx">js</span>

<span class="kr">import</span> <span class="nx">Vue</span> <span class="nx">from</span> <span class="s1">&#39;vue&#39;</span>
<span class="kr">import</span> <span class="nx">Router</span> <span class="nx">from</span> <span class="s1">&#39;vue-router&#39;</span>
<span class="kr">import</span> <span class="nx">Home</span> <span class="nx">from</span> <span class="s1">&#39;./views/Home.vue&#39;</span>
<span class="nx">Vue</span><span class="p">.</span><span class="nx">use</span><span class="p">(</span><span class="nx">Router</span><span class="p">)</span>
<span class="kr">export</span> <span class="k">default</span> <span class="k">new</span> <span class="nx">Router</span><span class="p">({</span>
  <span class="nx">mode</span><span class="o">:</span> <span class="s1">&#39;history&#39;</span><span class="p">,</span>
  <span class="nx">base</span><span class="o">:</span> <span class="nx">process</span><span class="p">.</span><span class="nx">env</span><span class="p">.</span><span class="nx">BASE_URL</span><span class="p">,</span>
  <span class="nx">routes</span><span class="o">:</span> <span class="p">[</span>
    <span class="p">{</span>
      <span class="nx">path</span><span class="o">:</span> <span class="s1">&#39;/&#39;</span><span class="p">,</span>
      <span class="nx">name</span><span class="o">:</span> <span class="s1">&#39;home&#39;</span><span class="p">,</span>
      <span class="nx">component</span><span class="o">:</span> <span class="nx">Home</span>
    <span class="p">},</span>
    <span class="p">{</span>
      <span class="nx">path</span><span class="o">:</span> <span class="s1">&#39;/about&#39;</span><span class="p">,</span>
      <span class="nx">name</span><span class="o">:</span> <span class="s1">&#39;about&#39;</span><span class="p">,</span>
      <span class="c1">// route level code-splitting
</span><span class="c1"></span>      <span class="c1">// this generates a separate chunk (about.[hash].js) for this route
</span><span class="c1"></span>      <span class="c1">// which is lazy-loaded when the route is visited.
</span><span class="c1"></span>      <span class="nx">component</span><span class="o">:</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="kr">import</span><span class="p">(</span><span class="cm">/* webpackChunkName: &#34;about&#34; */</span> <span class="s1">&#39;./views/About.vue&#39;</span><span class="p">)</span>
    <span class="p">}</span>
  <span class="p">]</span>
<span class="p">})</span>
</code></pre></div><p>如 <code>http://localhost:8080/</code> 下 <code>被渲染成 `home` 页面，`http://localhost:8080/about` 下</code> 被渲染成 <code>about</code> 页面。</p>
<blockquote>
<p>技巧 这里加载组件使用函数加载，就可以实现懒加载以优化性能。</p>
</blockquote>
<hr>
<p><code>src/views/</code> 则是保存页面文件，例如</p>
<div class="highlight"><pre class="chroma"><code class="language-html" data-lang="html">src/views/Home.vue

<span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span>
  <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;home&#34;</span><span class="p">&gt;</span>
    <span class="p">&lt;</span><span class="nt">img</span> <span class="na">alt</span><span class="o">=</span><span class="s">&#34;Vue logo&#34;</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;../assets/logo.png&#34;</span><span class="p">&gt;</span>
    <span class="p">&lt;</span><span class="nt">HelloWorld</span> <span class="na">msg</span><span class="o">=</span><span class="s">&#34;Welcome to Your Vue.js App&#34;</span><span class="p">/&gt;</span>
  <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
<span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span>
<span class="c1">// @ is an alias to /src
</span><span class="c1"></span><span class="kr">import</span> <span class="nx">HelloWorld</span> <span class="nx">from</span> <span class="s1">&#39;@/components/HelloWorld.vue&#39;</span>
<span class="kr">export</span> <span class="k">default</span> <span class="p">{</span>
  <span class="nx">name</span><span class="o">:</span> <span class="s1">&#39;home&#39;</span><span class="p">,</span>
  <span class="nx">components</span><span class="o">:</span> <span class="p">{</span>
    <span class="nx">HelloWorld</span>
  <span class="p">}</span>
<span class="p">}</span>
<span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</code></pre></div><p>这就是路由 <code>home</code> 对应的页面，其中用到了 <code>HelloWorld</code> 组件，需要在js中引入。</p>
<hr>
<p><code>src/components/</code> 里面保存可复用的组件，例如上面的 `` 就是一个独立的组件（component）</p>
<div class="highlight"><pre class="chroma"><code class="language-html" data-lang="html">src/components/HelloWorld.vue

<span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span>
  <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;hello&#34;</span><span class="p">&gt;</span>
    <span class="p">&lt;</span><span class="nt">h1</span><span class="p">&gt;</span>{{ msg }}<span class="p">&lt;/</span><span class="nt">h1</span><span class="p">&gt;</span>
    <span class="p">&lt;</span><span class="nt">p</span><span class="p">&gt;</span>
      For a guide and recipes on how to configure / customize this project,<span class="p">&lt;</span><span class="nt">br</span><span class="p">&gt;</span>
      check out the
      <span class="p">&lt;</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;https://cli.vuejs.org&#34;</span> <span class="na">target</span><span class="o">=</span><span class="s">&#34;_blank&#34;</span> <span class="na">rel</span><span class="o">=</span><span class="s">&#34;noopener&#34;</span><span class="p">&gt;</span>vue-cli documentation<span class="p">&lt;/</span><span class="nt">a</span><span class="p">&gt;</span>.
    <span class="p">&lt;/</span><span class="nt">p</span><span class="p">&gt;</span>
  <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
<span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span>
<span class="kr">export</span> <span class="k">default</span> <span class="p">{</span>
  <span class="nx">name</span><span class="o">:</span> <span class="s1">&#39;HelloWorld&#39;</span><span class="p">,</span>
  <span class="nx">props</span><span class="o">:</span> <span class="p">{</span>
    <span class="nx">msg</span><span class="o">:</span> <span class="nb">String</span>
  <span class="p">}</span>
<span class="p">}</span>
<span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
<span class="c">&lt;!-- Add &#34;scoped&#34; attribute to limit CSS to this component only --&gt;</span>
<span class="p">&lt;</span><span class="nt">style</span> <span class="na">scoped</span><span class="p">&gt;</span>
<span class="nt">h1</span> <span class="p">{</span>
  <span class="k">margin</span><span class="p">:</span> <span class="mi">40</span><span class="kt">px</span> <span class="mi">0</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">&lt;/</span><span class="nt">style</span><span class="p">&gt;</span>
</code></pre></div><p>这就是一个典型的组件，结构就是一个正常的单文本组件，说明组件就是把一部分页面逻辑抽出来组成一个文件。这样的好处是分离与页面无关的组件，使页面逻辑更加清晰，同时也便于复用组件。如果有以下情况，我建议把部分代码分离成独立的组件：</p>
<ol>
<li>需要多次复用的组件，如基础按钮、表单等</li>
<li>较为复杂但与页面逻辑无关的，如页面的Header、Footer等</li>
</ol>
<blockquote>
<p>技巧 在 <code>style</code> 表情上添加 <code>scoped</code> 属性可以限制CSS生效范围为该组件，不会影响其他元素，在编译时vue解释器会给这些css属性加上随机的hash保证唯一</p>
</blockquote>
<hr>
<p>vue还提供了一个状态管理模式 <a href="https://vuex.vuejs.org/zh/">vuex</a>，可以很方便的管理所有组件的状态。对应文件 <code>src/store.js</code></p>
<div class="highlight"><pre class="chroma"><code class="language-js" data-lang="js"><span class="nx">src</span><span class="o">/</span><span class="nx">store</span><span class="p">.</span><span class="nx">js</span>

<span class="kr">import</span> <span class="nx">Vue</span> <span class="nx">from</span> <span class="s1">&#39;vue&#39;</span>
<span class="kr">import</span> <span class="nx">Vuex</span> <span class="nx">from</span> <span class="s1">&#39;vuex&#39;</span>
<span class="nx">Vue</span><span class="p">.</span><span class="nx">use</span><span class="p">(</span><span class="nx">Vuex</span><span class="p">)</span>
<span class="kr">export</span> <span class="k">default</span> <span class="k">new</span> <span class="nx">Vuex</span><span class="p">.</span><span class="nx">Store</span><span class="p">({</span>
  <span class="nx">state</span><span class="o">:</span> <span class="p">{</span>
  <span class="p">},</span>
  <span class="nx">mutations</span><span class="o">:</span> <span class="p">{</span>
  <span class="p">},</span>
  <span class="nx">actions</span><span class="o">:</span> <span class="p">{</span>
  <span class="p">}</span>
<span class="p">})</span>
</code></pre></div><p><del>由于我还没用过 <code>vuex</code> 所以自己看文档吧</del></p>
<p>对于简单的应用也可以不用 <code>vuex</code> ，一个简单的 <a href="https://cn.vuejs.org/v2/guide/state-management.html#%E7%AE%80%E5%8D%95%E7%8A%B6%E6%80%81%E7%AE%A1%E7%90%86%E8%B5%B7%E6%AD%A5%E4%BD%BF%E7%94%A8"><code>store</code> 模式</a> 就足够了</p>
<h1 id="0x4-开发指引">0x4 开发指引</h1>
<p>有了上面的基础，我们就可以开始写代码了，这里以添加一个 <code>Foo</code> 页面展示从远端获得的数据为例</p>
<h2 id="1-给页面添加一个-tab">1. 给页面添加一个 <code>Tab</code></h2>
<p><code>Tab</code> 属于与页面逻辑无关，但又处处要使用的元素，所以我们将其独立成为一个组件</p>
<div class="highlight"><pre class="chroma"><code class="language-html" data-lang="html">src/components/Tab.vue

<span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span>
  <span class="p">&lt;</span><span class="nt">div</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;tab&#34;</span><span class="p">&gt;</span>
    <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;item&#34;</span> <span class="err">@</span><span class="na">click</span><span class="o">=</span><span class="s">&#34;router(&#39;/&#39;)&#34;</span><span class="p">&gt;</span>
      <span class="p">&lt;</span><span class="nt">span</span><span class="p">&gt;</span>home<span class="p">&lt;/</span><span class="nt">span</span><span class="p">&gt;</span>
    <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
    <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;item&#34;</span> <span class="err">@</span><span class="na">click</span><span class="o">=</span><span class="s">&#34;router(&#39;/about&#39;)&#34;</span><span class="p">&gt;</span>
      <span class="p">&lt;</span><span class="nt">span</span><span class="p">&gt;</span>about<span class="p">&lt;/</span><span class="nt">span</span><span class="p">&gt;</span>
    <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
    <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;item&#34;</span> <span class="err">@</span><span class="na">click</span><span class="o">=</span><span class="s">&#34;router(&#39;/foo&#39;)&#34;</span><span class="p">&gt;</span>
      <span class="p">&lt;</span><span class="nt">span</span><span class="p">&gt;</span>foo<span class="p">&lt;/</span><span class="nt">span</span><span class="p">&gt;</span>
    <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
  <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
<span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span>
<span class="kr">export</span> <span class="k">default</span> <span class="p">{</span>
  <span class="nx">name</span><span class="o">:</span> <span class="s1">&#39;Tab&#39;</span><span class="p">,</span>
  <span class="nx">methods</span><span class="o">:</span> <span class="p">{</span>
    <span class="nx">router</span> <span class="p">(</span><span class="nx">url</span><span class="p">){</span>
      <span class="k">this</span><span class="p">.</span><span class="nx">$router</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">url</span><span class="p">)</span>
    <span class="p">}</span>
  <span class="p">}</span>
<span class="p">}</span>
<span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">style</span> <span class="na">scoped</span><span class="p">&gt;</span>
<span class="p">#</span><span class="nn">tab</span> <span class="p">{</span>
  <span class="k">display</span><span class="p">:</span> <span class="kc">flex</span><span class="p">;</span>
  <span class="k">justify-content</span><span class="p">:</span> <span class="kc">space</span><span class="o">-</span><span class="n">around</span><span class="p">;</span>
  <span class="k">align-items</span><span class="p">:</span> <span class="kc">center</span><span class="p">;</span>
  <span class="k">bottom</span><span class="p">:</span> <span class="mi">0</span><span class="kt">px</span><span class="p">;</span>
  <span class="k">position</span><span class="p">:</span> <span class="kc">fixed</span><span class="p">;</span>
  <span class="k">height</span><span class="p">:</span> <span class="mi">65</span><span class="kt">px</span><span class="p">;</span>
  <span class="k">left</span><span class="p">:</span> <span class="mi">0</span><span class="kt">px</span><span class="p">;</span>
  <span class="k">width</span><span class="p">:</span> <span class="mi">100</span><span class="kt">%</span><span class="p">;</span>
  <span class="k">background-color</span><span class="p">:</span> <span class="kc">aquamarine</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">.</span><span class="nc">item</span> <span class="p">{</span>
  <span class="k">padding-top</span><span class="p">:</span> <span class="mi">5</span><span class="kt">px</span><span class="p">;</span>
  <span class="k">display</span><span class="p">:</span> <span class="kc">flex</span><span class="p">;</span>
  <span class="k">flex-direction</span><span class="p">:</span> <span class="kc">column</span><span class="p">;</span>
  <span class="k">justify-content</span><span class="p">:</span> <span class="kc">center</span><span class="p">;</span>
  <span class="k">align-items</span><span class="p">:</span> <span class="kc">center</span><span class="p">;</span>
  <span class="k">height</span><span class="p">:</span> <span class="mi">50</span><span class="kt">px</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">&lt;/</span><span class="nt">style</span><span class="p">&gt;</span>
</code></pre></div><p>在这里我们创建了一个底部导航栏，有三个 <code>Tab</code> ，并且对应跳转到不同的页面</p>
<h2 id="2-创建-foovue-相关逻辑并与后端交互">2. 创建 <code>Foo.vue</code> 相关逻辑并与后端交互</h2>
<p>因为我们需要从远端API获取数据并进行展示，就涉及网络请求。在vue中，官方推荐使用 <a href="https://github.com/axios/axios">axios</a> 发起请求。<code>axios</code> 是一个基于 <code>promise</code> 的异步网络请求库，相关用法可参考文档或网上教程</p>
<p>安装 <code>axios</code></p>
<div class="highlight"><pre class="chroma"><code class="language-bash" data-lang="bash">yarn add axios
</code></pre></div><p>接着在 <code>src/main.js</code> 中加入以下语句启用 <code>axios</code></p>
<div class="highlight"><pre class="chroma"><code class="language-js" data-lang="js"><span class="kr">import</span> <span class="nx">Axios</span> <span class="nx">from</span> <span class="s1">&#39;axios&#39;</span>
<span class="c1">// 把axios添加到原型链
</span><span class="c1"></span><span class="nx">Vue</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">$axios</span> <span class="o">=</span> <span class="nx">Axios</span>
</code></pre></div><p>现在就可以在vue实例中用 <code>this.$router</code> 来访问路由了</p>
<hr>
<p>接着来创建 <code>Foo.vue</code></p>
<div class="highlight"><pre class="chroma"><code class="language-vue" data-lang="vue"><span class="nx">src</span><span class="o">/</span><span class="nx">views</span><span class="o">/</span><span class="nx">Foo</span><span class="p">.</span><span class="nx">vue</span>

<span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span>
  <span class="p">&lt;</span><span class="nt">div</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;foo&#34;</span><span class="p">&gt;</span>
    <span class="p">&lt;</span><span class="nt">p</span><span class="p">&gt;{{</span> <span class="nx">result</span> <span class="p">}}&lt;/</span><span class="nt">p</span><span class="p">&gt;</span>
    <span class="p">&lt;</span><span class="nt">button</span> <span class="nt">@click</span><span class="s">=&#34;bar()&#34;</span><span class="p">&gt;</span><span class="na">bar</span><span class="p">&lt;/</span><span class="nt">button</span><span class="p">&gt;</span>
  <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
<span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">script</span><span class="p">&gt;</span>
<span class="kr">export</span> <span class="k">default</span> <span class="p">{</span>
  <span class="nx">name</span><span class="o">:</span> <span class="s1">&#39;Foo&#39;</span><span class="p">,</span>
  <span class="nx">data</span><span class="p">(){</span>
    <span class="k">return</span> <span class="p">{</span>
      <span class="nx">result</span> <span class="o">:</span> <span class="s1">&#39;naive&#39;</span>
    <span class="p">}</span>
  <span class="p">},</span>
  <span class="nx">methods</span><span class="o">:</span> <span class="p">{</span>
    <span class="nx">bar</span><span class="p">(){</span>
      <span class="cm">/* eslint-disable */</span>
      <span class="k">this</span><span class="p">.</span><span class="nx">result</span> <span class="o">=</span> <span class="s1">&#39;loading...&#39;</span>
      <span class="k">this</span><span class="p">.</span><span class="nx">$axios</span><span class="p">.</span><span class="nx">post</span><span class="p">(</span><span class="s1">&#39;http://httpbin.org/post&#39;</span><span class="p">)</span>
        <span class="p">.</span><span class="nx">then</span><span class="p">(</span><span class="nx">res</span> <span class="p">=&gt;</span> <span class="p">{</span><span class="k">this</span><span class="p">.</span><span class="nx">result</span> <span class="o">=</span> <span class="nx">res</span><span class="p">})</span>
        <span class="p">.</span><span class="k">catch</span><span class="p">(</span><span class="nx">res</span> <span class="p">=&gt;</span> <span class="p">{</span><span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="nx">res</span><span class="p">)})</span>
    <span class="p">}</span>
  <span class="p">}</span>
<span class="p">}</span>
<span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
<span class="p">&lt;</span><span class="nt">style</span><span class="p">&gt;</span>
<span class="p">&lt;/</span><span class="nt">style</span><span class="p">&gt;</span>
</code></pre></div><p><del>具体实现代码里面很清楚了</del> 这个文件的作用就是请求 <code>http://httpbin.org/get</code> 并把结果显示在界面上</p>
<p><strong>注意前后端分离的项目会有<a href="https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS">跨域(CORS)问题</a>，这个问题比较复杂，巨坑，网上也有较多解决的文章<del>咕咕咕</del></strong></p>
<h1 id="0x5-技巧及坑们">0x5 技巧及坑们</h1>
<p>这里介绍一些我写vue项目遇到的坑和使用的技巧</p>
<h2 id="data-methods-computed-props-的区别">data methods computed props 的区别</h2>
<p><del>TODO</del></p>
<p>vue中存储数据有几种方式——data、</p>
<h1 id="0x6-结语">0x6 结语</h1>
<p>到此，一个简单的vue项目就完成了，对于如何组织项目，前后端如何交互应该已经有了初步了解。本文也会持续更新，欢迎关注我的<a href="https://blog.creedowl.com/">blog</a></p>
<p>本文的实例请访问<a href="https://github.com/creedowl/vue_demo/">github</a></p>
]]></content>
		</item>
		
		<item>
			<title>南航校园网OpenWRT配置IPv6 NAT6</title>
			<link>https://blog.creedowl.com/posts/ipv6_and_nat6_in_nuaa/</link>
			<pubDate>Thu, 04 Apr 2019 23:29:39 +0800</pubDate>
			
			<guid>https://blog.creedowl.com/posts/ipv6_and_nat6_in_nuaa/</guid>
			<description>当前IPv6越来越流行，不但解决了IPv4地址不够用的问题，还能给接入的设备提供公网访问的支持。但由于校园网的特殊性，虽然很早就有了IPv6</description>
			<content type="html"><![CDATA[<blockquote>
<p>当前IPv6越来越流行，不但解决了IPv4地址不够用的问题，还能给接入的设备提供公网访问的支持。但由于校园网的特殊性，虽然很早就有了IPv6支持，大部分学校在IPv6地址分配上却有剧毒，无法向下游设备分配独立的IPv6地址（正常家庭网络环境中路由器及下游设备都能自动分配独立的公网IPv6地址），以南航为例，OpenWRT上虽然获取到了2001开头，/64结尾的公网IPv6地址，但下游设备却无IPv6访问权限。本文介绍南航校园网环境下如何在OpenWRT下获取IPv6并配置NAT6使下游设备拥有IPv6访问权限。</p>
</blockquote>
<h2 id="0软硬件需求">0：软硬件需求</h2>
<p><strong>硬件</strong>：支持OpenWRT固件的路由器或软路由（推荐使用软路由，性能更好，支持启用更多服务，且不需要考虑太多兼容性问题，x86版本OpenWRT一把梭）</p>
<p><strong>软件</strong>：较新版本的OpenWRT或其衍生版本（lede，pandorabox等，但原版OpenWRT最佳）</p>
<p><strong>OpenWRT内核需高于3.7</strong>， linux kernel在3.7版本开始支持NAT6</p>
<p><del>内核版本不够高且无法升级可以尝试早年由北邮大佬开发的NAT66项目，但不推荐</del></p>
<p>所需软件包：</p>
<div class="highlight"><pre class="chroma"><code class="language-shell" data-lang="shell">ip6tables kmod-ipt-nat6 kmod-ip6tables kmod-ip6tables-extra luci-proto-ipv6 iputils-traceroute6
</code></pre></div><p>较新版本已经自带IPv6支持，老版本需要自己安装相关软件包，但兼容性较差，或者内核版本不足，建议找较新版本或者自行编译。</p>
<h2 id="1openwrt配置获取ipv6">1：OpenWRT配置获取IPv6</h2>
<p><img src="https://blog-1251984664.cos.ap-shanghai.myqcloud.com/UTOOLS1588304901671.png/compress" alt="IPv6_setting_1"></p>
<h3 id="1-修改ipv6-ula前缀为fd开头默认为dd开头">1. 修改IPv6 ULA前缀为fd开头（默认为dd开头）</h3>
<h3 id="2-修改wan6口配置如下图所示">2. 修改wan6口配置（如下图所示）</h3>
<p><img src="https://blog-1251984664.cos.ap-shanghai.myqcloud.com/UTOOLS1588304939503.png/compress" alt="IPv6_setting_2"></p>
<h3 id="3-修改lan口dhcp服务器配置如下图所示">3. 修改lan口DHCP服务器配置（如下图所示）</h3>
<p><img src="https://blog-1251984664.cos.ap-shanghai.myqcloud.com/UTOOLS1588304973656.png/compress" alt="IPv6_setting_3"></p>
<p><img src="https://blog-1251984664.cos.ap-shanghai.myqcloud.com/UTOOLS1588304997028.png/compress" alt="IPv6_setting_4"></p>
<p>此时稍等片刻，在概况中应该就能看到获取到的IPv6地址及下游设备DHCPv6分配内网IPv6地址</p>
<p><img src="https://blog-1251984664.cos.ap-shanghai.myqcloud.com/UTOOLS1588305013341.png/compress" alt="IPv6_setting_5"></p>
<p><img src="https://blog-1251984664.cos.ap-shanghai.myqcloud.com/UTOOLS1588305030637.png/compress" alt="IPv6_setting_6"></p>
<p>现在你的路由器已经可以使用IPv6了，但下游设备并不行，就需要配置NAT6。</p>
<h2 id="2配置nat6">2：配置NAT6</h2>
<p>虽然NAT6并不符合IPv6的设计哲学，但却能解决一些问题，如安全隔离，或者像我们这样通过单一地址转发给多个设备使用。</p>
<h3 id="1-修改防火墙">1. 修改防火墙</h3>
<p><img src="https://blog-1251984664.cos.ap-shanghai.myqcloud.com/UTOOLS1588305046715.png/compress" alt="IPv6_setting_7"></p>
<p>加入以下自定义规则</p>
<div class="highlight"><pre class="chroma"><code class="language-shell" data-lang="shell"><span class="nv">WAN6</span><span class="o">=</span>pppoe-wan
<span class="nv">LAN</span><span class="o">=</span>br-lan
ip6tables -t nat -A POSTROUTING -o <span class="nv">$WAN6</span> -j MASQUERADE
ip6tables -A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
ip6tables -A FORWARD -i <span class="nv">$LAN</span> -j ACCEPT
</code></pre></div><p><strong>注意这里WAN6和LAN要改为外网IPv6和内网网卡名字，在终端使用ifconfig查看</strong></p>
<p><img src="https://blog-1251984664.cos.ap-shanghai.myqcloud.com/UTOOLS1588305068355.png/compress" alt="IPv6_setting_8"></p>
<h3 id="2-配置网关">2. 配置网关</h3>
<p>在终端执行 <code>ip -6 r</code> 查看默认网关（default开头），如果是类似</p>
<p><code>default from 2001:xxxx:xxxx:xxxx::/64 via fe80::ded2:fcff:fe98:64f7 dev pppoe-wan proto static metric 512 pref medium</code></p>
<p>这样蛋疼的网关，需要去掉<code>from 2001:xxxx:xxxx:xxxx::/64</code> 这段再添加默认网关，例如</p>
<p><code>ip -6 route add default via fe80::ded2:fcff:fe98:64f7 dev pppoe-wan proto static metric 512</code></p>
<p>可以建立一个<code>/etc/hotplug.d/iface/99-ipv6</code>实现自动配置</p>
<div class="highlight"><pre class="chroma"><code class="language-shell" data-lang="shell"><span class="cp">#!/bin/sh
</span><span class="cp"></span><span class="o">[</span> <span class="s2">&#34;</span><span class="nv">$ACTION</span><span class="s2">&#34;</span> <span class="o">=</span> ifup <span class="o">]</span> <span class="o">||</span> <span class="nb">exit</span> <span class="m">0</span>
<span class="nv">iface</span><span class="o">=</span>wan6
<span class="o">[</span> -z <span class="s2">&#34;</span><span class="nv">$iface</span><span class="s2">&#34;</span> -o <span class="s2">&#34;</span><span class="nv">$INTERFACE</span><span class="s2">&#34;</span> <span class="o">=</span> <span class="s2">&#34;</span><span class="nv">$iface</span><span class="s2">&#34;</span> <span class="o">]</span> <span class="o">||</span> <span class="nb">exit</span> <span class="m">0</span>
ip -6 route add <span class="sb">`</span>ip -6 route show default<span class="p">|</span>sed -e <span class="s1">&#39;s/from [^ ]* //&#39;</span><span class="sb">`</span>
logger -t IPv6 <span class="s2">&#34;Add IPv6 default route.&#34;</span>
</code></pre></div><p>最后别忘了修改权限</p>
<p><code>chmod +x /etc/hotplug.d/iface/99-ipv6</code></p>
<p>重启路由器就可以愉快的玩耍了。</p>
<p><del>本来还想折腾IPv6端口转发，但一直无法配置成功。后来突然发现南航校园网下没有对IPv4做严格隔离（除了80端口基本上都能直接访问），就直接做IPv4端口转发配合DDNS，连接校园WIFI（nuaa.portal，<strong>付费版本</strong>）就可以直接使用，速度还挺快。。。就真香了。。不过配置IPv6端口转发还是有意义，比如可以从非校园网访问，校园网访问IPv6无需认证（不要充钱了），相关教程有空再补上（咕咕咕）有需求可以参考<a href="https://shura.eu.org/2018/12/06/ipv6-NAT%E5%90%8E%E9%85%8D%E7%BD%AE%E7%AB%AF%E5%8F%A3%E8%BD%AC%E5%8F%91/">ipv6 NAT后配置端口转发</a></del></p>
<h2 id="参考资料">参考资料</h2>
<blockquote>
<p><a href="https://github.com/tuna/ipv6.tsinghua.edu.cn/blob/master/openwrt.md">清华OpenWRT 路由器作为 IPv6 网关的配置</a></p>
</blockquote>
]]></content>
		</item>
		
		<item>
			<title>PostgreSQL 简介与常用命令</title>
			<link>https://blog.creedowl.com/posts/postgres-handbook/</link>
			<pubDate>Sun, 03 Feb 2019 18:32:39 +0800</pubDate>
			
			<guid>https://blog.creedowl.com/posts/postgres-handbook/</guid>
			<description>PostgreSQL 简介与常用命令 PostgreSQL，简称postgres，是继mysql被收购之后极为热门的开源关系型数据库，这篇文章简单记录一些使用过程</description>
			<content type="html"><![CDATA[<h1 id="postgresql-简介与常用命令">PostgreSQL 简介与常用命令</h1>
<p>PostgreSQL，简称postgres，是继mysql被收购之后极为热门的开源关系型数据库，这篇文章简单记录一些使用过程中的基本用法。</p>
<h2 id="安装">安装</h2>
<p>为了保证宿主机环境的纯净，这里使用docker来安装postgres</p>
<div class="highlight"><pre class="chroma"><code class="language-shell" data-lang="shell">docker run --name postgres -p 5432:5432 -e <span class="nv">POSTGRES_PASSWORD</span><span class="o">=</span>password -d postgres
</code></pre></div><p>常用环境变量</p>
<blockquote>
<p>POSTGRES_USER: 数据库用户，缺省postgres</p>
<p>POSTGRES_PASSWORD: 数据库密码 ps. 若从localhost连接则默认trust，不需要填写</p>
<p>POSTGRES_DB: 数据库名，缺省postgres</p>
</blockquote>
<p>更多docker相关内容参考<a href="https://hub.docker.com/_/postgres">postgres官方docker镜像页面</a></p>
<h2 id="常用命令">常用命令</h2>
<p><strong>连接数据库</strong></p>
<div class="highlight"><pre class="chroma"><code class="language-shell" data-lang="shell">psql -U user -d database -h host -p port
</code></pre></div><p><strong>导入数据</strong></p>
<div class="highlight"><pre class="chroma"><code class="language-shell" data-lang="shell">psql database &lt; db.sql
</code></pre></div><p><strong>控制台命令</strong></p>
<blockquote>
<p>\h：查看SQL命令的解释，比如\h select。</p>
<p>?：查看psql命令列表。</p>
<p>\l：列出所有数据库。</p>
<p>\c [database_name]：连接其他数据库。</p>
<p>\d：列出当前数据库的所有表格。</p>
<p>\d [table_name]：列出某一张表格的结构。</p>
<p>\du：列出所有用户。</p>
<p>\e：打开文本编辑器。</p>
<p>\conninfo：列出当前数据库和连接的信息。</p>
</blockquote>
<p><strong>数据库操作</strong></p>
<p><em>采用基本sql语法</em></p>
<div class="highlight"><pre class="chroma"><code class="language-sql" data-lang="sql"><span class="o">#</span> <span class="err">建表</span>
<span class="k">CREATE</span> <span class="k">TABLE</span> <span class="k">IF</span> <span class="k">NOT</span> <span class="k">EXISTS</span> <span class="k">user</span> <span class="p">(</span>
	<span class="n">id</span> <span class="nb">SERIAL</span> <span class="k">PRIMARY</span> <span class="k">KEY</span><span class="p">,</span>
    <span class="n">name</span> <span class="nb">TEXT</span> <span class="k">NOT</span> <span class="k">NULL</span>
<span class="p">);</span>

<span class="o">#</span> <span class="err">插入</span>
<span class="k">INSERT</span> <span class="k">INTO</span> <span class="k">user</span> <span class="p">(</span><span class="n">name</span><span class="p">)</span> <span class="k">VALUES</span> <span class="p">(</span><span class="s1">&#39;username&#39;</span><span class="p">);</span>

<span class="o">#</span> <span class="err">选择</span>
<span class="k">SELECT</span> <span class="o">*</span> <span class="k">FROM</span> <span class="k">user</span><span class="p">;</span>

<span class="o">#</span> <span class="err">更新</span>
<span class="k">UPDATE</span> <span class="k">user</span> <span class="k">SET</span> <span class="n">name</span> <span class="o">=</span> <span class="s1">&#39;qwer&#39;</span> <span class="k">WHERE</span> <span class="n">id</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>

<span class="o">#</span> <span class="err">删除</span>
<span class="k">DELETE</span> <span class="k">FROM</span> <span class="k">user</span> <span class="k">WHERE</span> <span class="n">id</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>

<span class="o">#</span> <span class="err">添加栏位</span>
<span class="k">ALTER</span> <span class="k">TABLE</span> <span class="k">user</span> <span class="k">ADD</span> <span class="n">gender</span> <span class="nb">VARCHAR</span><span class="p">(</span><span class="mi">2</span><span class="p">);</span>

<span class="o">#</span> <span class="err">更新表结构</span>
<span class="k">ALTER</span> <span class="k">TABLE</span> <span class="k">user</span> <span class="k">ALTER</span> <span class="k">COLUMN</span> <span class="n">gender</span> <span class="k">SET</span> <span class="k">NOT</span> <span class="k">NULL</span><span class="p">;</span>

<span class="o">#</span> <span class="err">字段更名</span>
<span class="k">ALTER</span> <span class="k">TABLE</span> <span class="k">user</span> <span class="k">RENAME</span> <span class="k">COLUMN</span> <span class="n">name</span> <span class="k">TO</span> <span class="n">username</span><span class="p">;</span>

<span class="o">#</span> <span class="err">删除字段</span>
<span class="k">ALTER</span> <span class="k">TABLE</span> <span class="k">user</span> <span class="k">DROP</span> <span class="k">COLUMN</span> <span class="n">gender</span><span class="p">;</span>

<span class="o">#</span> <span class="err">表名更改</span>
<span class="k">ALTER</span> <span class="k">TABLE</span> <span class="k">user</span> <span class="k">RENAME</span> <span class="k">TO</span> <span class="n">users</span><span class="p">;</span>

<span class="o">#</span> <span class="err">删除表格</span>
<span class="k">DROP</span> <span class="k">TABLE</span> <span class="n">users</span><span class="p">;</span>
</code></pre></div><h2 id="相关链接">相关链接</h2>
<blockquote>
<p><a href="http://www.ruanyifeng.com/blog/2013/12/getting_started_with_postgresql.html">PostgreSQL新手入门</a></p>
</blockquote>
]]></content>
		</item>
		
		<item>
			<title>var和let声明变量的区别</title>
			<link>https://blog.creedowl.com/posts/js-differences-between-var-and-let/</link>
			<pubDate>Sat, 26 Jan 2019 21:59:14 +0800</pubDate>
			
			<guid>https://blog.creedowl.com/posts/js-differences-between-var-and-let/</guid>
			<description>var和let声明变量的区别 今天因为全局变量重复声明的问题被let坑了好久，就研究了一下var和let的区别 var var是最早的变量标识符（但在</description>
			<content type="html"><![CDATA[<h1 id="var和let声明变量的区别">var和let声明变量的区别</h1>
<blockquote>
<p>今天因为全局变量重复声明的问题被let坑了好久，就研究了一下var和let的区别</p>
</blockquote>
<h2 id="var">var</h2>
<p>var是最早的变量标识符（但在js中声明变量也不一定要标识符。。），兼容性也是最好的，但也存在一些问题，如</p>
<ol>
<li>变量提升，即函数和变量的声明都将被提升到函数的最顶部。但初始化并不会提升</li>
</ol>
<div class="highlight"><pre class="chroma"><code class="language-javascript" data-lang="javascript"><span class="p">{</span>
    <span class="c1">// 变量a未声明
</span><span class="c1"></span>    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">a</span><span class="p">)</span> <span class="c1">// error: Uncaught ReferenceError: a is not defined
</span><span class="c1"></span><span class="p">}</span>

<span class="p">{</span>
    <span class="c1">// 变量声明提升，但未初始化
</span><span class="c1"></span>    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">a</span><span class="p">)</span> <span class="c1">// undefined
</span><span class="c1"></span>    <span class="kd">var</span> <span class="nx">a</span> <span class="o">=</span> <span class="mi">2</span> <span class="c1">// 初始化a
</span><span class="c1"></span>    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">a</span><span class="p">)</span> <span class="c1">// 2
</span><span class="c1"></span><span class="p">}</span>
</code></pre></div><ol start="2">
<li>可重复声明</li>
</ol>
<div class="highlight"><pre class="chroma"><code class="language-javascript" data-lang="javascript"><span class="kd">var</span> <span class="nx">a</span> <span class="o">=</span> <span class="mi">2</span>
<span class="kd">var</span> <span class="nx">a</span> <span class="o">=</span> <span class="s1">&#39;3&#39;</span>
</code></pre></div><ol start="3">
<li>没有局部变量，变量作用域都是全局容易导致</li>
</ol>
<h2 id="let">let</h2>
<p>为了解决var的一些奇奇怪怪的问题，es6新增了let命令用于声明变量。与var相比，let更像其他语言（如c艹）</p>
<ol>
<li>不存在变量提升，所以类似上面的写法会抛出未定义的错误</li>
</ol>
<div class="highlight"><pre class="chroma"><code class="language-javascript" data-lang="javascript"><span class="p">{</span>
    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">a</span><span class="p">)</span> <span class="c1">// error: Uncaught ReferenceError: a is not defined
</span><span class="c1"></span>    <span class="kd">let</span> <span class="nx">a</span> <span class="o">=</span> <span class="mi">2</span> <span class="c1">// 初始化a
</span><span class="c1"></span>    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">a</span><span class="p">)</span> <span class="c1">// 2
</span><span class="c1"></span><span class="p">}</span>
</code></pre></div><ol start="2">
<li>不可重复声明</li>
</ol>
<div class="highlight"><pre class="chroma"><code class="language-javascript" data-lang="javascript"><span class="kd">let</span> <span class="nx">a</span> <span class="o">=</span> <span class="mi">1</span>
<span class="kd">let</span> <span class="nx">a</span> <span class="o">=</span> <span class="mi">2</span> <span class="c1">// SyntaxError: Identifier &#39;a&#39; has already been declared
</span></code></pre></div><ol start="3">
<li>有块级作用域等特性，所以如循环变量等局部变量等不会影响外部的变量</li>
</ol>
]]></content>
		</item>
		
		<item>
			<title>decorator 装饰器</title>
			<link>https://blog.creedowl.com/posts/python-decorator/</link>
			<pubDate>Sat, 03 Nov 2018 16:24:59 +0800</pubDate>
			
			<guid>https://blog.creedowl.com/posts/python-decorator/</guid>
			<description>decorator 装饰器 普通装饰器 有个计算a+b的函数 def add(a, b): return a+b 现在想要在函数执行前后进行操作，可以用装饰器 from functools import wraps def log(func): @wraps(func) # 避免原函数的__name__等属</description>
			<content type="html"><![CDATA[<h2 id="decorator-装饰器">decorator 装饰器</h2>
<p><img src="https://ws4.sinaimg.cn/large/006tNbRwgy1fxlsbrfedaj30rs13ahdt.jpg" alt=""></p>
<h3 id="普通装饰器">普通装饰器</h3>
<p>有个计算a+b的函数</p>
<div class="highlight"><pre class="chroma"><code class="language-python" data-lang="python"><span class="k">def</span> <span class="nf">add</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">):</span>
    <span class="k">return</span> <span class="n">a</span><span class="o">+</span><span class="n">b</span>
</code></pre></div><p>现在想要在函数执行前后进行操作，可以用装饰器</p>
<div class="highlight"><pre class="chroma"><code class="language-python" data-lang="python"><span class="kn">from</span> <span class="nn">functools</span> <span class="kn">import</span> <span class="n">wraps</span>

<span class="k">def</span> <span class="nf">log</span><span class="p">(</span><span class="n">func</span><span class="p">):</span>
    <span class="nd">@wraps</span><span class="p">(</span><span class="n">func</span><span class="p">)</span> <span class="c1"># 避免原函数的__name__等属性发生变化</span>
    <span class="k">def</span> <span class="nf">wrapper</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
        <span class="k">print</span><span class="p">(</span><span class="s1">&#39;before func&#39;</span><span class="p">)</span>
        <span class="n">func</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span> <span class="c1"># 执行原函数，若不调用则不执行原函数</span>
        <span class="k">print</span><span class="p">(</span><span class="s1">&#39;after func&#39;</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">wrapper</span>
    
<span class="nd">@log</span> <span class="c1"># @:装饰器语法糖</span>
<span class="k">def</span> <span class="nf">add</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">):</span>
    <span class="k">return</span> <span class="n">a</span><span class="o">+</span><span class="n">b</span>
    
<span class="s1">&#39;&#39;&#39;
</span><span class="s1">add(1, 2)
</span><span class="s1">output: 
</span><span class="s1">before func
</span><span class="s1">3
</span><span class="s1">after func
</span><span class="s1">&#39;&#39;&#39;</span>
</code></pre></div><h3 id="带参数的装饰器">带参数的装饰器</h3>
<p>要实现带参数的装饰器，只需要再用一个函数包装普通装饰器就行</p>
<div class="highlight"><pre class="chroma"><code class="language-python" data-lang="python"><span class="kn">from</span> <span class="nn">functools</span> <span class="kn">import</span> <span class="n">wraps</span>

<span class="k">def</span> <span class="nf">log</span><span class="p">(</span><span class="n">text</span><span class="p">):</span>
    <span class="k">def</span> <span class="nf">decorator</span><span class="p">(</span><span class="n">func</span><span class="p">):</span>
        <span class="nd">@wraps</span><span class="p">(</span><span class="n">func</span><span class="p">)</span> <span class="c1"># 避免原函数的__name__等属性发生变化</span>
        <span class="k">def</span> <span class="nf">wrapper</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
            <span class="k">print</span><span class="p">(</span><span class="n">text</span><span class="p">)</span>
            <span class="n">func</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span> <span class="c1"># 执行原函数，若不调用则不执行原函数</span>
            <span class="k">print</span><span class="p">(</span><span class="s1">&#39;after func&#39;</span><span class="p">)</span>
        <span class="k">return</span> <span class="n">wrapper</span>
    <span class="k">return</span> <span class="n">decorator</span>
    
<span class="nd">@log</span><span class="p">(</span><span class="s1">&#39;log&#39;</span><span class="p">)</span> <span class="c1"># @:装饰器语法糖</span>
<span class="k">def</span> <span class="nf">add</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">):</span>
    <span class="k">return</span> <span class="n">a</span><span class="o">+</span><span class="n">b</span>
    
<span class="s1">&#39;&#39;&#39;
</span><span class="s1">add(1, 2)
</span><span class="s1">output: 
</span><span class="s1">log
</span><span class="s1">3
</span><span class="s1">after func
</span><span class="s1">&#39;&#39;&#39;</span>
</code></pre></div>]]></content>
		</item>
		
	</channel>
</rss>
